こんにちは!最近は WindowsForms の Blog を書くことも少なくなってきていましたが、皆様から寄せられるお問い合わせのプラットフォーム別件数を見ると、まだまだ WindowsForms は現役であると感じます。また、VB6.0などで作られたレガシーシステムを最新の.NET Frameworkへマイグレーションするというような案件も、まだ良く耳にします。
本ポストから数回に分けて、WindowsFormsにフォーカスした内容で、特に実際の業務アプリにどのように組み込むのかというところを意識した内容で、WinGrid を使って簡単なサンプルアプリを作成していきたいと思います。ちょっとした小技やサンプルコードなど利用できるところがあればご参考にしていただければ幸いです。
また、WinGridを使ったことが無いという方にはとっかかりとして見ていただけると思います。
WinGrid概要
WinGrid は弊社コントロールの中でも最も歴史の深いコントロールです。フィルターやソートやグループ化などの基本的なオプションは全て簡単なプロパティ設定で実装でき、また、 Excel や PDF 形式でのエクスポートなどグリッド周辺の多様な要件にコストをかけず対応できるコントロールとなっています。このポストでは、基本的なプロパティ設定などにはあまり触れず、オンラインヘルプなどには無い業務アプリケーションに近い視点からサンプルを作成していきたいと思います。
WinGridの紹介ページ:
データの表示
前置きが長くなりましたが、データの表示と登録を行うサンプルを作っていきたいと思います。
WinGridは、様々なデータソース形式に対応しており、SQLサーバ等に直接接続してしまえば、特にデータの表示や更新処理を自分でプログラムを書くことなく実装できてしまいます。しかし、実際に業務アプリを組む際には、データの加工や各種項目チェック処理を独自ロジックで組み込むために、一度データベースから取得したものを加工してグリッドへ表示し、登録時に再度データを加工し各テーブルへ更新を行うという方が多いのではないかと思います。その流れを想定して、今回はプログラム上で DataTable を作成し、それをグリッドコントロールへバインドするという方法をとっています。
まず、グリッドをデザイナへ配置し、コードビハインド側を以下のように記述します。
private void Form1_Load(object sender, EventArgs e) { // グリッドの初期設定 ultraGrid1.DisplayLayout.Override.AllowRowFiltering = Infragistics.Win.DefaultableBoolean.True; //フィルタ設定 ultraGrid1.DisplayLayout.Override.AllowGroupBy = Infragistics.Win.DefaultableBoolean.True; //グループ化設定 ultraGrid1.DisplayLayout.Override.AllowColMoving = Infragistics.Win.UltraWinGrid.AllowColMoving.WithinBand; //列移動設定 ultraGrid1.DisplayLayout.Override.AllowColSizing = Infragistics.Win.UltraWinGrid.AllowColSizing.Synchronized; //列サイズ設定 // バインドするサンプルデータの設定 DataTable dt = GetData(); dt.AcceptChanges(); // バインド時にデータテーブルを全コミット this.ultraGrid1.DataSource = dt; // キー項目(Number列)を非表示化(もしくは編集不可へ) //ultraGrid1.DisplayLayout.Bands[0].Columns["Number"].Hidden = true; ultraGrid1.DisplayLayout.Bands[0].Columns["Number"].CellActivation = Infragistics.Win.UltraWinGrid.Activation.NoEdit; //もしくは編集不可 } // ***************************** // バインドするDatatableを生成 // ***************************** private DataTable GetData() { //Create DataTable DataTable dtSample1 = new DataTable("Sample"); //Create Columns dtSample1.Columns.Add("Number", typeof(System.Int32)); dtSample1.Columns.Add("Check", typeof(System.Boolean)); dtSample1.Columns.Add("String1", typeof(System.String)); dtSample1.Columns.Add("String2", typeof(System.String)); dtSample1.Columns.Add("String3", typeof(System.String)); //Create Rows(20レコード生成する) for (int index = 0; index <= 19; index++) { dtSample1.Rows.Add(new object[] { index, false, "String1_" + index.ToString(), "String2_" + index.ToString(), "String3_" + index.ToString() }); } //Number列をキーとする。 dtSample1.PrimaryKey = new DataColumn[] { dtSample1.Columns["Number"] }; return dtSample1; }
GetData() メソッド内でグリッドへ表示するDataTableを生成しておりますが、実際の業務アプリではここで実DBから値を取得するようなイメージになります。
グリッドコントロールへのバインドは DataSource プロパティに作成/取得したデータを任意のデータ形式で割り当てるだけで、グリッドの表示が行えます。ひとつひとつ列を定義してゆくこともできますが、今回のようにデータをバインドした際に、バインドしたデータが保持している列定義に合わせて自動でグリッドの列定義を生成することができます。また、今回は Number 列をDataTableのキー項目としているため、グリッド上で Number 列を編集できないよう設定しています。
上記のコードのみで、以下のような結果となります。
各列がバインドしたDataTableの情報より自動生成され、Check列はチェックボックス列として生成されていることが確認できます。また、この時点で編集不可として設定していない Number 以外の列はセル編集することができます。
行追加と行削除
続いて、ここに行を追加または削除する機能を付けます。
WinGridでは行削除・行追加についてもプロパティのみで対応することもできます。 AllowAddNew プロパティを有効にすることで画面下部に新規追加するための領域が現れ、そこから行追加を行うことができるのですが、Built-inであるが故の制限もあるためコードビハインドから追加していく方法を紹介します。
ボタンコントロールをフォームに追加し、それぞれのボタンのクリックイベントのハンドラを追加し、以下のよう行追加・削除の処理を記述します。
// ***************************** // 行追加ボタン押下 // ***************************** private void ultraButton1_Click(object sender, EventArgs e) { // バインドしている DataTable の取り出し DataTable dt = ultraGrid1.DataSource as DataTable; // 新規行追加 (キー項目だけは重複しないように注意) dt.Rows.Add(new object[] {dt.Rows.Count,false,"","",""}); } // ***************************** // 行削除ボタン押下 // ***************************** private void ultraButton2_Click(object sender, EventArgs e) { // 対象行チェック if (ultraGrid1.ActiveRow == null) { MessageBox.Show("削除対象行が選択されていません。"); return; } int activeRowIndex = ultraGrid1.ActiveRow.Index; // 削除処理(自動的に確認ダイアログが出る。) ultraGrid1.ActiveRow.Delete(); // アクティブ行を次の行へ移動 if (activeRowIndex < ultraGrid1.Rows.Count) ultraGrid1.ActiveRow = ultraGrid1.Rows[activeRowIndex]; }
WinGrid はデータバインドが前提のコントロールとなっているため、 Grid.Rows.Add(※例です) のような形で行追加するのではなく、バインドされているデータソース側で行追加を行います。この際、初期値としてキー項目は値が重複しないように設定しておく必要があります。
また、行削除については、Delete() メソッドを利用するか、データソース側の対象データを直接削除するかです。上記コードでは現在選択されている行(ActiveRow)に対して Delete() を行っています。そもそも行自体が選択されていない場合はその旨をダイアログ表示しています。
データの更新
最後にデータの更新処理です。今回のサンプルではセルの編集と行追加と行削除ができるため、Update, Insert, Delete と一通りの更新処理が発生します。
今回のサンプルでは DataRowState を利用して、変更のあった行のみを対象に更新処理を入れていきます。
// ***************************** // 行追削除ボタン押下 // ***************************** private void ultraButton3_Click(object sender, EventArgs e) { // DB更新確認 if (MessageBox.Show( "編集内容を確定しますか?", "登録確認", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No) { return; } // バインドしている DataTable の取り出し DataTable dt = ultraGrid1.DataSource as DataTable; // 変更(更新、追加、削除)があった行のみの取り出し DataTable data = dt.GetChanges(); if (data == null){ MessageBox.Show("更新対象がありませんでした。"); return; } foreach (DataRow row in data.Rows){ switch (row.RowState) { case DataRowState.Modified: //更新処理へ System.Diagnostics.Debug.WriteLine("RowModified Number:" + row["Number"].ToString()); break; case DataRowState.Added: //新規追加処理へ System.Diagnostics.Debug.WriteLine("RowAdded Number:" + row["Number"].ToString()); break; case DataRowState.Deleted: //削除処理へ System.Diagnostics.Debug.WriteLine("RowDeleted Number:" + row["Number",DataRowVersion.Original].ToString()); break; default: break; } } //更新処理完了 dt.AcceptChanges(); MessageBox.Show("更新しました。"); }
上記コードでは、まず更新確認のダイアログを出した後に、バインドしている DataTable の変更を GetChanges() で取り出し、更新対象の有無判定を行っています。更新対象があった場合は、GetChanges() によって取り出された各変更行から RowState で 更新 or 登録 or 削除 を判定しています。
※DataRowState についての詳細はこちらから:MSDN DataTableの編集
ここでは実際にデータベースへ書き込みに行く処理は割愛していますが、row[“Key値”] といった具合で対象行の値を取得することができるのでこの値を元に各処理を呼び出すような形になるかと思います。
※削除された行については、row["Key値",DataRowVersion.Original] のように第2引数を指定し削除される前の状態からキー値を取得します。
元に戻す
もはやWinGridとは関係ありませんが、DataTable の RowState を使った更新制御をしていると、データを元に戻す事が非常に楽に実装できます。
DataTable.RejectChanges() メソッドは最後にコミット( AcceptChanges() )されて以降の変更をすべてロールバックします。バインドしているデータソースがロールバックされると自動的にグリッドの内容も編集前の状態に戻ります。
// ***************************** // 元に戻すボタン押下 // ***************************** private void ultraButton4_Click(object sender, EventArgs e) { // バインドしている DataTable の取り出し DataTable dt = ultraGrid1.DataSource as DataTable; //元に戻す dt.RejectChanges(); }
完成です!細かい制御は抜きにして、グリッドコントロールを使った更新ありの一覧画面の基本的な流れができたと思います。
書いたコードを見返してみると、その大部分が WinGrid コントロール独自のものではなく、DataTableに対する操作や確認ダイアログの記述で、一般的なコードとなっています。また、フィルタやソートなどの+αの機能も簡単なプロパティ設定で実装できるため、DBの参照/更新や業務ロジックにのみ集中すればOKで、UI側の実装にはあまり時間をかけずに済みます。(気がつけば WinGrid 活用術というよりも DataTable 活用術な内容ですね。。。
今回のサンプルのダウンロードは こちら から
次回は、ここにバリデーションを加えていきます。エラーにひっかけ方や、特にWinGridにおける効果的なエラーの表現方法をご紹介したいと思います。
無料トライアルダウンロードはこちら