Question

4leafclover17 on Thu, 28 Aug 2014 09:46:21


いつもお世話になっております。

Windows Form から WPF に移植する必要があるプログラムがあり、現在勉強中です。
元々VB.NETで作成しており、C#への言語変更が必要ですが、できれば、Windows Form で
作成したコーディングは流用したいと思っています。

開発環境 Visual Studio 2012 / .NET Framework 4.5
開発言語 C#

DataTable に以下のようなフィールドがあります。

選択           (bool)
社員番号       (string)
ユーザコード   (string)
ユーザ名       (string)
所属部門       (int)
所属グループ   (int)
性別           (int)
郵便番号       (string)
住所1         (string)
住所2         (string)
電話番号       (string)
携帯番号       (string)
FAX番号        (string)
メールアドレス (string)
備考           (string)
入社年度       (int)

-------------------------------------------------------------------------------

Window1.xaml.cs

-------------------------------------------------------------------------------

namespace WpfApplication1
{
    /// <summary>
    /// Window1.xaml の相互作用ロジック
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
            dgUserList.Columns.Clear();
            dgUserList.ItemsSource = null;
            dgUserList.ItemsSource = DataTable.DefaultView;
        }
    }
}

-------------------------------------------------------------------------------

Window1.xaml

-------------------------------------------------------------------------------

<Window x:Class="WpfApplication1.Window1"
  省略

    Title="Window1" Height="768" Width="1024">
    <Grid>
      <Grid.Resources>
        <!-- DataGridスタイル設定 -->
        <Style TargetType="{x:Type cc:CustomDataGrid}">
          <Setter Property="Width" Value="976"/>
          <Setter Property="Height" Value="555"/>
          <Setter Property="FontSize" Value="16"/>
          <Setter Property="AlternatingRowBackground" Value="Beige"/>
          <Setter Property="AutoGenerateColumns" Value="False"/>
          <Setter Property="CanUserAddRows" Value="False"/>
          <Setter Property="CanUserDeleteRows" Value="False"/>
          <Setter Property="CanUserReorderColumns" Value="False"/>
          <Setter Property="HorizontalScrollBarVisibility" Value="Auto"/>
          <Setter Property="VerticalScrollBarVisibility" Value="Auto"/>
        </Style>
      </Grid.Resources>

      <DataGrid x:Name="dgUserList"
                Canvas.Left="20"
                Canvas.Top="20"
                ItemsSource="{Binding}">
          <DataGrid.Columns>
              <DataGridCheckBoxColumn x:Name="選択"         Header="選択"         Binding="{Binding 選択,         Mode=TwoWay}" Width="213" IsReadOnly = "False"></DataGridCheckBoxColumn>
              <DataGridTextColumn     x:Name="社員番号"     Header="社員番号"     Binding="{Binding 社員番号,     Mode=TwoWay}" Width="110" IsReadOnly = "True" ></DataGridTextColumn>
              <DataGridTextColumn     x:Name="ユーザID"   Header="ユーザID"   Binding="{Binding ユーザID,   Mode=TwoWay}" Width="213" IsReadOnly = "True" ></DataGridTextColumn>
              <DataGridTextColumn     x:Name="ユーザ名"     Header="ユーザ名"     Binding="{Binding ユーザ名,     Mode=TwoWay}" Width="110" IsReadOnly = "False"></DataGridTextColumn>
              <DataGridComboBoxColumn x:Name="性別"         Header="性別"         Binding="{Binding 性別,         Mode=TwoWay}" Width="110" IsReadOnly = "False"></DataGridComboBoxColumn>
              <DataGridComboBoxColumn x:Name="所属部署"     Header="所属部署"     Binding="{Binding 所属部署,     Mode=TwoWay}" Width="110" IsReadOnly = "False"></DataGridComboBoxColumn>
              <DataGridComboBoxColumn x:Name="所属グループ" Header="所属グループ" Binding="{Binding 所属グループ, Mode=TwoWay}" Width="110" IsReadOnly = "False"></DataGridComboBoxColumn>
          </DataGrid.Columns>
      </DataGrid>
    </Grid>
</Window>

-------------------------------------------------------------------------------

上記のようにコーディングしたら、DataGrid にデータ表示されません。
AutoGenerateColumns を True にし、<DataGrid.Columns> ~ </DataGrid.Columns> を注釈にすると
表示されましたが、DataTable にセットした情報が全て表示されてしまします。

1)DataTableにはフィールドが沢山ありますが、DataGrid に表示したいのはDataTable の一部だけの場合、
  どのようにコーディングしたらいいでしょうか?

2)コンボボックスについて
  コンボボックスで選択できるデータと、画面表示時に表示するデータが異なります。
  例えば所属部署には営業部、管理部、システム部、開発部、サービス部、総務部があり、
  営業部がデフォルト表示されます。
  営業部からシステム部に変更後、画面を再起動したときには、システム部を表示したいのですが、
  どのような方法があるでしょうか?

3)DataGrid のセル選択について
   DataGridをカスタムコントロール化(ビヘイビアでもよかったのですが)し、
   Enterキーでセル移動する等のキー制御を用意しました。
   Enterキーでセル移動した場合、そのセルが編集モードであったなら、
   セルをシングルクリックすると、データを選択状態にする(文字の反転表示)、
   実現方法がわかりません。
   FocusManager を使用したらいいのでしょうか、ネットを調べたのですが、
   よくわかりませんでした。

   //指定セルを選択状態にする
   DataGridCellInfo cellInfo = DataGrid.CurrentCell;
   DataGrid.Focus();
   DataGrid.SelectedIndex = DataGrid.Items.IndexOf(this.CurrentItem);
   DataGrid.CurrentCell = cellInfo;

   //編集モードに移行
   if (isBeginEdit)
   {
    DataGrid.BeginEdit();
    //データグリッドセルのデータを選択状態にする
    //FocusManager を使用したらいいの?
    DataGridCell dcInfo = GetCellInfo(DataGrid.Items.IndexOf(DataGrid.CurrentItem), cellInfo.Column.DisplayIndex);
    FocusManager.SetFocusedElement(DataGrid, dcInfo);
   }

沢山質問しましが、DataGridに関する情報が全くなく、手探り状態です。

お手数をおかけ致しますが、ご教授いただけますよう、お願い致します。



Sponsored



Replies

佐祐理 on Thu, 28 Aug 2014 10:37:07


dgUserList.Columns.Clear();
Cloumns.Clear()すれば当然Columnsはなくなりますが…?

trapemiya on Fri, 29 Aug 2014 04:34:43


>1)DataTableにはフィールドが沢山ありますが、DataGrid に表示したいのはDataTable の一部だけの場合、
  どのようにコーディングしたらいいでしょうか?

とりあえず、サンプルを探してみました。

WPFのDataGridにDataTableを表示
http://90h-tech.blogspot.jp/2011/01/wpfdatagriddatatable.html

おそらく、DataTableの中身はデータベースから取ってきたものですよね。もし、そうであれば、DataTableとは本来はデータベースのデータの一部をローカルにコピーして入れておくものですから、それを直接バインドすべきではないものだと私は考えています。Windowsフォームでも、本来はそのようにすべきだと考えています。ただ、Windowsフォームの場合、直接DataTableをバインドすることによるメリットの方がデメリットより大きいようで、よく使われていますし、私はそれを否定するものではありません。
しかし、WPFはバインドが強化されています。この強化されたバインドの恩恵に預かるには、DataTableをバインドするのではなくINotifyCollectionChangedを実装する、例えばObservableCollectionを使います。これを使えば、バインドしているソースで行の追加や削除を行うと、自動的にDataGridはそれを反映します。

>2)コンボボックスについて

これについては意味がよくわかりませんでした。コンボボックスのデフォルト値を変えたいということでしょうか? それとも、登録時のデフォルト値とは別に、社員毎に持っている所属部署のデータをそれぞれコンボボックスで表示したいということでしょうか?

>3)DataGrid のセル選択について

試していませんが、TextBoxを含むCellEditingTemplateを用意して、そのTextBoxがフォーカスを受け取った時に、TextBoxのSelectAllメソッドを実行すれば良いように思います。

(参考)
WPF の DataGrid 列のテンプレートを構成してユーザー エクスペリエンスを向上する
http://msdn.microsoft.com/ja-jp/magazine/gg983481.aspx

さて、「できれば、Windows Form で作成したコーディングは流用したいと思っています。」と書かれていましたが、折角ですから根本的にWPFの特徴を生かせるようにコーディングし直されることをお勧めします。でなければ、できあがったアプリケーションはWPFだとしても、ユーザーから見れば以前のWindowsフォームのアプリケーションと何ら違いを見ることができません。であれば、工数を使ってWPFに移行しなくても、Windowsフォームのまま開発をすれば良いということに、誰かから突っ込まれたりしないだろうかと心配してしまいます。
ちなみに私はWPFの特徴を出すために、DataGridを使うことはほとんどありません。WPFは表形式を使わない自由なレイアウトが可能です。つまり、社員情報を入力する単票があり、それをそのままリストに放り込めます。Excelのような表形式も必要でしょうが、時にはこのような表現力が威力を発揮します。

4leafclover17 on Mon, 01 Sep 2014 01:01:14


dgUserList.Columns.Clear();
Cloumns.Clear()すれば当然Columnsはなくなりますが…?

佐祐理様

ご回答ありがとうございます。
言葉足らずで申し訳ないです。
DataGridの初期化のつもりで記述しています。
【ItemsSource = null】で事足りるのならいいのですが、それだけでは不十分と
どこかの記事で拝見した為、【Columns.Clear()】を記述しています。
(ソートの情報も消していますが、掲載したサンプルには記載していません。)

最新のWPFではDataGridの初期化は、【ItemsSource = null】だけでいいのでしょうか?
新たにDataTableをバインドしたときに問題が発生しないのなら、冗長なコーディングかもしれません。
ご指摘、ありがとうございます。

佐祐理 on Mon, 01 Sep 2014 01:29:18


ご自身で何をしたいのか理解できていますか? カラムの無いDataGridを作りたいのですか? それともカラムの表示されるDataGridを作りたいのですか? Cloumns.Clear()をすればカラムの無いDatGridを作ることができます。Cloumns.Clear()をしなければカラムの表示されるDataGridを作ることができます。

プログラミングに「おまじない」などありません。なんとなくClear()したりなんとなくnullを代入したりしても、コードを書いた人の自己満足でしかなく、何の意味もありません。

4leafclover17 on Tue, 09 Sep 2014 00:50:06


回答が遅れ、大変申し訳ございません。

確かに、提示したサンプルで Columns.Clear() すれば、カラムが消えるのも道理なのですが、
決して「おまじない」の意味で Clear() しているのではなく、コードビハインド側で、
同グリッドに全く別物の内容で再描画する必要があり、Columns.Clear()の後、【Columns.Add】等を
行い、カラムの再作成を行っています。
(ItemsSource = null でいいとは思うのですが、当方にも事情があり、お察しいただければ幸いです。)

只、提示したサンプルには不要なコーディングでした。
Clearすればカラムが消えるので、グリッドに表示されないのは当たり前でした。

ご指摘いただき、猛省しました。
ご教授、ありがとうございました。

4leafclover17 on Tue, 09 Sep 2014 01:05:21


trapemiya 様

ご回答ありがとうございます。
返信が遅れ、大変申し訳ございません。

> 1)…省略…
> おそらく、DataTableの中身はデータベースから取ってきたものですよね。
> もし、そうであれば、DataTableとは本来はデータベースのデータの一部を
> ローカルにコピーして入れておくものですから、それを直接バインドすべきでは
> ないものだと私は考えています。…

 サンプルの提示、ありがとうございました。

 はいそうです。
 DataTableの中身はDBから取得しております。

 WPFはバインドの機能が強化されているのは十分、承知しておりますが、
 仕様上、DataTableをバインドせざる負えません。
 もったいないとは思うのですが、次回機会があれば提案したいと思っています。

2)コンボボックスについて

 上手い説明ができず、申し訳ございません。
 コンボボックスに部署データがバインドされているとします。(初期表示は営業部です)
 ある社員データを表示する際、部署のコンボボックスにシステム部を表示したい場合、
 Windows Forms では、cmbBusyo.SelectedIndex = cmbBusyo.FindStringExact("システム部", -1);
 とコーディングしていたのですが、WPFではどのようにコーディングすればいいのか分からず、戸惑っています。
 同様の実現方法を探しているのですが、どのようなコーディング方法があるのか、代わりの方法あるのか、
 ご存じであればご教授いただければ嬉しいです。

3)DataGrid のセル選択について

 試してみます。
 ありがとうございます。

Windows Form で作成したシステムをWPFに移植し、機能拡張をする。
画面レイアウトや操作性等は、Windows Form と同等であること、が仕様です。
画面が同じで、機能拡張なら、Windows Form で作成すればいいのではと提案しましたが、
却下されている為、苦戦しています。
画面全体の9割がデータグリッドを使用しているので、できればWindows Form での
開発がよかったのですが。

コントロール等の実装は、WPFに準拠したコーディングを考えております。
その為、Windows Forms で実装したコーディングをWPFに置き換えられないか、
同様のイベントやプロパティはあるか、置き換えれるか、確認作業を行っているのですが、
ほぼ1から作り直すことになりそうです。

只、WPFに関わらないコーディングに関しては、Windows Forms で作成したものを
移植することで工数削減しようと足掻いています。

trapemiya on Tue, 09 Sep 2014 02:47:18


 ある社員データを表示する際、部署のコンボボックスにシステム部を表示したい場合、
 Windows Forms では、cmbBusyo.SelectedIndex = cmbBusyo.FindStringExact("システム部", -1);
 とコーディングしていたのですが、WPFではどのようにコーディングすればいいのか分からず、戸惑っています。

う~ん、Windows Formsでもあんまり良い書き方じゃないと思いますが、WPFでもこのように名称による検索で指定しなければならないのでしょうか? できれば、コードで制御したいところです。例えば、システム部のコードが10であれば、それをコンボボックスのSelectedValueに指定します。バインドは、以下のようにXAMLに書いておけばOKです。
SelectedValue="{Binding 部署コード}"

どうしても文字列検索ということになれば、以下のような感じでしょうか? とりあえずデータテーブルがバインドしているという前提です。

comboBox1.SelectedItem
    = comboBox1.Items.Cast<DataRowView>().FirstOrDefault(p => p["部署名"].ToString() == "システム部");

#(追記)
書き忘れました。すみません。その他の仕様に関する件は了解です。

#(追記2)
ついでにクエリ式版も載せておきます。

IEnumerable<DataRowView> query =
    from DataRowView item in comboBox1.Items
    where (item["部署名"].ToString() == "システム部")
    select item;

comboBox1.SelectedItem = query.FirstOrDefault();


★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/

4leafclover17 on Thu, 02 Oct 2014 10:52:23


返信が遅くなり、大変申し訳ございません。

未完成ながら、道筋を立てることが叶いましたので、ご報告致します。

本当にありがとうございました。

回答マークの付け方が判らず、見苦しいかと思いますが、回答として、マークさせていただきます。