• 検索結果がありません。

データグリッドビュー

N/A
N/A
Protected

Academic year: 2021

シェア "データグリッドビュー"

Copied!
28
0
0

読み込み中.... (全文を見る)

全文

(1)

■ .NET の新しいデータグリッドを大解剖 ■

.NET で(Visual Studio 2005 を使って)Windows アプリケーションを作成する場合、其のユーザーイ ンターフェイスの構築には、予め用意されたコントロール(或いは、コンポーネント)部品をフォーム にドラッグ&ドロップし乍ら行うのが一般的で有り、此れはVisual Studio の大きな特徴でも有る。其 の様なコントロール部品は、.NET では標準、或いは、サードパーティに依り多数用意されて居る。 然して、其の様なコントロール部品の中でも、特に業務アプリケーション構築に於いて重要と成るのが、 グリッドコントロールで有る。謂う迄も無く、グリッドコントロールは、データを表形式で表示し、セ ル内の値を編集したり、行追加に依りデータを追加したりする為の物で有る。データベース上のレコー ドの参照、追加、削除、更新処理が主と成る業務アプリケーションでは、グリッドコントロールの使い 勝手が大きなポイントと成ると謂える。 此の様に、グリッドコントロールが業務アプリケーションで果たす役割は大きいが、其れに比例してグ リッドコントロールに要求される機能も多い。実際.NET に標準で用意されて居るグリッドコントロー ル(具体的にはDataGridView コントロール)は、数有るコントロールの中でも最も複雑で規模の大き い物に成って居る。其の為、此れを使い熟し、適切な実装を行うには(行える様に成るには)少なから ずの工数が必要と成る。 本稿では、此の最も頻繁に利用され、開発者が実装に最も労力を要するで有ろうグリッドコントロール を使い熟す為に、グリッドコントロールに付いて研究し乍、其の基礎から応用迄を解説して行く。先ず は.NET 標準のグリッドコントロールに付いて観て行くが、後半では、サードパーティ製のグリッド製 品等も取り上げる予定で有る。

猶、.NET でグリッドコントロールと謂えば、Windows アプリケーション用と Web アプリケーション (ASP.NET)用の物が有るが、本稿では Windows アプリケーション而巳にターゲットを絞って解説す る。

■ Visual Studio 2005 で新たに追加された DataGridView コントロール

.NET 標準のグリッドコントロールと仕ては、.NET Framework 1.x(Visual Studio .NET 及び Visual Studio .NET 2003)迄は DataGrid コントロールが使われて居たが、.NET Framework 2.0(Visual Studio 2005)では全く新しく、DataGridView コントロール(以下、DGV コントロールと略す)が追 加された。

.NET Framework 2.0 でも DataGrid コントロールは利用可能だが、此れは下位互換性の為に残されて 居る丈で有る。此の事はDGV コントロールが DataGrid コントロールから根本的に大きく変わって居 る事を意味して居る。 2 つのグリッドコントロールの主な違いに付いては、MSDN の「Windows フォームの DataGridView コントロールとDataGrid コントロールの違いに付いて」で纏められて居る。其の記述の内、先ず注目 す可きは次の部分で有る。 「DataGridView コントロールは全く新しいアーキテクチャを採用して居る為、DataGridView コント ロールでDataGrid のカスタマイズされた機能を使用出来る様にする簡単な変換パスは有りません。」 折角、此れ迄多くの開発者がDataGrid コントロールに付いて多くのノウハウやテクニックを蓄積して 来たと謂うのに、其等の殆どを捨てて仕舞わなければ成らないので有る!今後は此の様な互換性の無い 置き換えは控えて欲しい物で有る。

(2)

■ DataGridView コントロールの構造 扨て、気を取り直して、DataGrid コントロールよりも簡単に拡張やカスタマイズが行えると謂う DGV コントロールに、新たな気持ちで取り組んで行く事にする。 先ずは、DGV コントロールの各名称を確認して置く事にする。以下の画面は、DGV コントロールを配 置したWindows アプリケーションの実装例で有る。 DataGridView コントロールの各名称 此の画面のWindows アプリケーションでは、フォームの中央に DGV コントロールを配置して居る。 ① 選択中のセル ② 行ヘッダ ③ 列ヘッダ ④ イメージ列 ⑤ テキストボックス列(デフォルトで使用される列) ⑥ リンク列 ⑦ コンボボックス列 ⑧ チェックボックス列 ⑨ ボタン列 基本的に、DGV コントロールでは表形式のデータを表示する。詰まり、各行が 1 つの(1 組の)データ を表し(此の画面では全部で 5 行のデータを表示して居る)、各列には各データ内の同じ種類の項目が 並ぶ(例えばタイトル表示用の列ならば各データのタイトル項目而巳)。拠って、データが増えて行け ば、グリッドは縦方向に其れに合わせて長く成って行く事に成る。 実際のデータは、各「セル」内に表示される(図中①)。セルは、キーボードやマウスに依り選択する 事が出来、選択されて居るセルには色が付けられる。複数個のセルを選択する事も出来る。 先頭の行は、「行ヘッダ」と呼ばれる行(図中②)で有り、此処には各列の見出しを表示する。行ヘッ ダ内の各列部分をクリックすれば、其の列の項目でデータを昇順、又は、逆順に並べ替える様にする事 も出来る。同様に、最も左端の列は、「列ヘッダ」と呼ばれる列(図中③)で有る。列ヘッダ部分をク リックする事に依り行単位での選択が出来る。 DGV コントロールでは、単成るテキストや数値以外のデータもセルで表示可能で有る。上記の画面で は、全部で6 種類の列を使用して居るが(図中④~⑨)、此れが DGV コントロールに標準で備わって居 る総ての種類の列で有る。以前のDataGrid コントロールに比べると、イメージ列やボタン列等が増え て居る。此等以外の特殊な列(例えば日付を選択する為のDataTimePicker コントロールを表示する様 な列)を使いたければ、新しい列の種類を自作して表示させる事も出来る。 ■ DataGridView コントロールのオブジェクト構造 次に、DGV コントロールを構成する一連のオブジェクトに付いて観て行く事にする。 コントロールで有るDGV は、クラスライブラリ的に観ると、DataGridView クラスの 1 つのインスタ ンス(オブジェクト)で有るのだが、実際にDGV コントロールを構成する行や列、セルは総て個々の 別のオブジェクトで有り、DGV オブジェクトは、謂わば、此等のオブジェクトの入れ物と成って居る。

(3)

此の為、DGV コントロールのプログラミングをマスターするには、各オブジェクトの基と成るクラス の機能(プロパティやメソッド等)は基より、各クラスの継承関係や各オブジェクトの包含関係をも熟 知して置く必要が有る。 列オブジェクト 先ず列を示すオブジェクトだが、此れは、DataGridViewColumn クラスの派生クラスのオブジェクト で有る(※)。此の派生クラスには、次の図に示す様に、標準では 6 種類のクラスが存在し、先程の画 面の6 種類の列に対応して居る。DataGridViewColumn クラスでは「列オブジェクト」に共通のメソ ッドやプロパティが実装されて居り、各種類の列に固有の機能は、其の派生クラスで実装されて居ると 謂う訳で有る。 ※ 以降で登場する DGV コントロール関連のクラス(クラス名は総て「DataGridView~」で始まる) は、総てSystem.Windows.Forms 名前空間に属するクラスで有る。 列オブジェクトのクラス階層 此の図の先頭に有るDataGridViewElement クラスは、DGV コントロールの総ての要素(列や行やセ ル)のベースと成るクラスで有る。此のクラスには、例えば、DataGridView プロパティが有るが、此 のプロパティに依りDGV コントロールの何の要素からも、其れが属して居る DGV コントロールを参 照する事が出来る。 其の派生クラスで有るDataGridViewBand クラスは、列や行等の帯状(Band)のオブジェクトを表す ベースクラスで有る。DataGridViewElement クラスには、今 1 つ派生クラスが有り、其方は、セルを 表すDataGridViewCell クラスで有る(後述)。 因みに、独自のコントロールを配置した列を使いたければ、DataGridViewColumn クラスから継承し た派生クラスを自作する事に成る。 行オブジェクト DGV コントロールで既に行データを表示して居る場合、各行の行データは「行オブジェクト」に依り 表される。行オブジェクトとは、DataGridViewRow クラスのオブジェクトで有る。此のクラスも列オ ブジェクトと同様にDataGridViewBand クラスの派生クラスで有る。

(4)

セルオブジェクト 1 つの行オブジェクトには、データの項目数(其の行が属する DGV コントロールの列数)分の「セル オブジェクト」が含まれる。DGV コントロールで実際に表示されて居るデータを保持するのは、此の セルオブジェクトで有る。 セルオブジェクトは、以下の図で示したDataGridViewCell クラスをベースとする派生クラスのオブジ ェクトで有る。セルオブジェクトの種類は、自ずと列オブジェクトの種類と同じ丈必要と成る(※)。 例えば、イメージ列に含まれるセルは総てDataGridViewImageCell オブジェクトで有る。 ※ 此の為独自の列を作った場合には、併せて其れに対応した独自のセルも実装しなくては成らない。 セルオブジェクトのクラス階層 DGV コントロールが管理するオブジェクトは、実際には、此等以外にもヘッダ内のセルを表すオブジ ェクト等多数存在するが、先ずは、此等のオブジェクトを押さえて置けば良い。 DataGridView コントロールのコレクション 次の図は、DGV コントロールの主要なオブジェクトの関係を纏めた物で有る。 DGV コントロールの主要なオブジェクトの関連 列とセルに付いては、実際には此の図で示して居る DataGridViewColumn オブジェクト、或いは DataGridViewCell オブジェクトの派生オブジェクトと成る。

(5)

図の左上に有る「DGV」から伸びて居る「Columns プロパティ」は、DGV コントロールの此のプロパ ティから各列の列オブジェクトにアクセス出来る事を示して居る。同様に、Rows プロパティからは、 DGV コントロールに含まれる各列の列オブジェクトにアクセス出来る。亦、図では少し解り難いが、 列オブジェクトのCells プロパティからは、其の列に含まれる個々のセルオブジェクトにアクセスする 事が出来る。 此等3 つのプロパティの実体は、以下の表に示すコレクションクラスのオブジェクトで有る。 3 つのプロパティと其れに割り当てられるオブジェクトのクラス プロパティ名 コレクションクラス名(プロパティの型) Columns プロパティ DataGridViewColumnCollection クラス Rows プロパティ DataGridViewRowCollection クラス Cells プロパティ DataGridViewCellCollection クラス 此等のプロパティには、DGV コントロールに列や行を追加した時、又は、行にセルを追加した時に、 該当するコレクションクラスのオブジェクトが作成され、設定される。 此処では詳しく述べないが、コレクションクラスの利用は.NET プログラミングでは頻繁に登場する為、 是非押さえて於いて欲しい。例えばAdd メソッドに依りコレクションの要素を追加したり、インデクサ (VB ではデフォルトプロパティと成って居る Item プロパティ)を使ってインデックス番号に依りコレ クションの要素を取得したりと謂った具合で有る。 以下では実際にコードを記述してDGV プログラミングを解説するが、其処で此等のプロパティにも触 れる。 ■ 初めての DataGridView プログラミング 其れでは、此処迄に登場したオブジェクトに付いて、より理解を深める為に、実際にコードを記述して 観る事にする。此処では次の画面の様なグリッドを表示するプログラムを記述して行く。 此処で作成するサンプル・プログラムの実行画面 2 列目ではクラスライブラリに含まれて居るアイコンを表示して居る。 DGV コントロールにデータを表示する為のプログラミングは、基本的に以下の様な手順と成る。Visual Studio 2005 の IDE や、DGV コントロールのデータバインディング機能(次回で解説予定)を使えば、 (2)や(3)の手順はコードを記述せずとも可能だが、今回は総て明示的にコーディングを行って行く。 (1)DGV クラス(DGV コントロール)のインスタンス化 (2)列オブジェクトの追加

(6)

例えば、データベースでは、最初にスキーマ(カラムと成る項目)を決めてからしかレコードを追加出 来ない様に、DGV コントロールでも、先ず、列オブジェクトを追加して置き、其の列の種類に従った 幾つかのセルを含む行オブジェクトを追加して行く事に成る。 列オブジェクトの作成と追加 手順(1)の DGV クラスのインスタンス化は、単にクラスを「New」する丈で有る。 Visual Basic

Dim dgv As New DataGridView( )

C#

DataGridView dgv = new DataGridView( );

DGV オブジェクトは、通常のコントロールと同様に、フォームの Controls プロパティに追加すればフ ォーム上に表示される。

次に、列で表示したいデータの種類に対応した列オブジェクトを作成する。此処では第1 列でテキスト、 第2 列で画像を表示するので、テキストボックス列とイメージ列を使用する。

Visual Basic

Dim column1 As New DataGridViewTextBoxColumn column1.HeaderText = "1 列目の見出し"

Dim column2 As New DataGridViewImageColumn column2.HeaderText = "2 列目の見出し"

C#

DataGridViewTextBoxColumn column1 = new DataGridViewTextBoxColumn( ); column1.HeaderText = "1 列目の見出し";

DataGridViewImageColumn column2 = new DataGridViewImageColumn( ); column2.HeaderText = "2 列目の見出し"; 亦、此処では、列ヘッダに見出しを表示する為に、HeaderText プロパティに見出し用の文字列を設定 して居る。此のプロパティは、グリッドが表示されて居る時にも読み書きが可能で有る。 扨て、DGV クラスのプロパティの中で列オブジェクトを管理するのは、先程の図でも登場した Columns プロパティで有る。作成した列オブジェクトは、此のプロパティに追加して行く事に依り、実際にDGV コントロールに列がセットされる事に成る。 Visual Basic dgv.Columns.Add(column1) dgv.Columns.Add(column2) C# dgv.Columns.Add(column1); dgv.Columns.Add(column2); 因みに、Visual Studio 2005 を使って居る場合には、DGV コントロールをツールボックスからドラッ グ&ドロップし、[DataGridView タスク]メニューから列の追加を選択すれば、ダイアログから列を 追加する事が出来る。

(7)

Visual Studio 2005 による列の追加 列の名前、型(種類)、ヘッダに表示されるテキストを設定して[追加]ボタンをクリックすれば、DGV コントロールに列が追加される。 ① フォームに配置した DGV コントロール ② DGV コントロールのスマートタグから開く[DataGridView タスク]メニュー ③ 列の種類を選択 ③の型を選択するコンボボックスでは、上述した 6 種類の列が選択出来る筈で有る。猶、[列の追加] ダイアログには「データバインド列」と「非バインド列」の2 つの列が有るが(上記の画面ではデータ バインド列はグレイアウトして居る)、此の列の違いに付いては、次回で解説する予定で有る。 行オブジェクトの作成と追加 DGV コントロールに列オブジェクトを作成し追加すれば、次に行(実データ)を追加する。 行の追加では、行オブジェクトを作成して追加し、更にセルオブジェクトを作成して追加すると謂った 明示的な作業をしなくても、DGV コントロールの Rows プロパティの Add メソッドを利用すれば、メ ソッドのパラメータに実際のデータを並べて書く丈で良い(※)。 ※ 此のメソッドのドキュメントを観ると「独自に作成したコードから直接使用する為の物では有りま せん」と書かれて居るが、「方法:Windows フォーム DataGridView コントロールの並べ替え機能 をカスタマイズする」に有るサンプルプログラム等では堂々と使われて居る。 此処ではAdd メソッドに渡すパラメータと仕て、文字列とアイコン(※)を指定する。イメージ列に対 してはIcon オブジェクトか Image オブジェクトが指定可能で有る。

(8)

Visual Basic dgv.Rows.Add("Asterisk", SystemIcons.Asterisk) dgv.Rows.Add("Error", SystemIcons.Error) dgv.Rows.Add("Exclamation", SystemIcons.Exclamation) dgv.Rows.Add("Question", SystemIcons.Question) C# dgv.Rows.Add("Asterisk", SystemIcons.Asterisk); dgv.Rows.Add("Error", SystemIcons.Error); dgv.Rows.Add("Exclamation", SystemIcons.Exclamation); dgv.Rows.Add("Question", SystemIcons.Question);

※ SystemIcons クラスは Windows システムで共通のアイコンを Icon オブジェクトと仕て取得出来る クラスで有る。此処で此のクラスを使って居るのは、単にアイコンオブジェクトが簡単に得られる と謂う理由からで有る。 此の様な Add メソッドの呼出に依り、セルオブジェクトや行オブジェクトが自動的に作成され、DGV コントロールに追加される事に成る。 行オブジェクトの作成と追加の別の方法 DGV コントロールへのデータの追加には幾つかの記述方法が有る。次に示すコードでは、先ず空の行 を追加し、其の後、各セルにデータを設定して居る。セルの値を読み書きするにはセルオブジェクトの Value プロパティを使用する。 Visual Basic

Dim rowIndex As Integer = dgv.Rows.Add( ) dgv.Rows(rowIndex).Cells(0).Value = "Asterisk"

dgv.Rows(rowIndex).Cells(1).Value = SystemIcons.Asterisk

C#

int rowIndex = dgv.Rows.Add( );

dgv.Rows[rowIndex].Cells[0].Value = "Asterisk"; dgv.Rows[rowIndex].Cells[1].Value = SystemIcons.Asterisk; Add メソッドが返す値は、其れに依り追加された行オブジェクトのインデックス、詰まり、行番号で有 る(0 始まり)。行番号が得られれば、dgv.Rows(<行番号>).Cells(<列番号>) に依り、1 つのセルオ ブジェクトを参照する事が出来る為、其のValue プロパティに依り特定の位置のセルの値を読み書き出 来る(C#では ( ) の代わりに [ ] を使用)。 亦、DGV コントロールには、行番号と列番号を指定して直接セルにアクセスする為のインデクサも用 意されて居る為、行への値の設定には次の様な記述も可能で有る。 Visual Basic dgv(0, rowIndex).Value = "Asterisk" ' 1 列目の値 dgv(1, rowIndex).Value = SystemIcons.Asterisk ' 2 列目の値 C# dgv[0, rowIndex].Value = "Asterisk"; // 1 列目の値 dgv[1, rowIndex].Value = SystemIcons.Asterisk; // 1 列目の値 実際には、セルの Value プロパティに対して此の様に直接データを設定する様な機会は少ないのだが、 指定されたセルの値を得る為にValue プロパティを読み出すケースは少なくない(※)。

(9)

※ Value プロパティの型は Object 型なので、データを読み出す時には通常ダウンキャスト(例えば Integer 型や String 型へのキャスト)が必要と成る。 サンプルコードの纏め 最後に、此処迄のコードを実際にコンパイルして実行出来る様に仕たソースコードを次に示して置く。 Visual Basic ' firstdgv.vb Imports System Imports System.Drawing Imports System.Windows.Forms Public Class MyForm

Inherits Form

Shared Sub Main( )

Application.Run(New MyForm( )) End Sub Dim dgv As DataGridView Sub New( ) dgv = New DataGridView( ) ' DGV のインスタンス化 dgv.Dock = DockStyle.Fill ' フォームの全面に配置 ' ユーザーに依る新しい行の追加禁止 dgv.AllowUserToAddRows = False ' ユーザーに依る行の変更、削除の禁止 dgv.ReadOnly = True ' 行の高さの自動調整 dgv.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells Me.Controls.Add(dgv) InitializeDGV( ) FillDGV( ) End Sub Sub InitializeDGV( )

Dim column1 As New DataGridViewTextBoxColumn

column1.HeaderText = "1 列目の見出し" dgv.Columns.Add(column1)

Dim column2 As New DataGridViewImageColumn

column2.HeaderText = "2 列目の見出し" dgv.Columns.Add(column2)

(10)

Sub FillDGV( ) dgv.Rows.Add("Asterisk", SystemIcons.Asterisk) dgv.Rows.Add("Error", SystemIcons.Error) dgv.Rows.Add("Exclamation", SystemIcons.Exclamation) dgv.Rows.Add("Question", SystemIcons.Question) End Sub End Class C# // firstdgv.cs using System; using System.Drawing; using System.Windows.Forms; public class MyForm : Form {

static void Main( ) { Application.Run(new MyForm( )); } DataGridView dgv; public MyForm( ) { dgv = new DataGridView( ); // DGV のインスタンス化 dgv.Dock = DockStyle.Fill; // フォームの全面に配置 // ユーザーに依る新しい行の追加禁止 dgv.AllowUserToAddRows = false; // ユーザーに依る行の変更、削除の禁止 dgv.ReadOnly = true; // 行の高さの自動調整 dgv.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells; this.Controls.Add(dgv); InitializeDGV( ); FillDGV( ); } void InitializeDGV( ) {

DataGridViewTextBoxColumn column1 = new DataGridViewTextBoxColumn( ); column1.HeaderText = "1 列目の見出し";

dgv.Columns.Add(column1);

DataGridViewImageColumn column2 = new DataGridViewImageColumn( ); column2.HeaderText = "2 列目の見出し";

dgv.Columns.Add(column2);

(11)

} void FillDGV( ) { dgv.Rows.Add("Asterisk", SystemIcons.Asterisk); dgv.Rows.Add("Error", SystemIcons.Error); dgv.Rows.Add("Exclamation", SystemIcons.Exclamation); dgv.Rows.Add("Question", SystemIcons.Question); } } プログラム内のコメントにも有る様に、此処では DGV コントロールを表示専用と仕て利用する為に AllowUserToAddRows プロパティと ReadOnly プロパティの 2 つを設定して居る。デフォルトでは既 存のセルの値を編集したり、一番下に表示される空の行から新しい行を追加したりする事が出来る。 DGV コントロールの編集機能に付いては孰れ取り上げる予定で有る。 亦、DGV コントロールには、行の高さや、各列の幅をセルの値に応じて自動的に調節する機能が新し く追加されて居る。此のプログラムでは、AutoSizeRowsMode プロパティを設定する事に依り 2 列目 の画像全体が表示される様に仕て居るが、此のサイズ自動調節機能に付いても、回を改めて解説する予 定で有る。 ■ 今回は、DGV コントロールの基本構造に付いて解説し、最後に簡単なサンプルプログラムを作成して 実際にグリッドにデータを表示して観た。 DataGrid コントロールを使用した経験が有れば、データを表示するのにデータバインド(データ連結) 機能が使われて居ない事に気付いたと思う。DGV コントロールでは、従来のデータバインドに依るデ ータの表示に加えて、「非バインドモード」(今回のサンプルプログラムは此れで有る)や、大量のデー タを扱う時に利用出来る「仮想モード」等が用意されて居る。次回は此の辺りの解説から始める予定で 有る。

(12)

■ DataGridView コントロールでマインスイーパ ■ 前回では、DataGridView コントロール(以下、DGV コントロール)のオブジェクト構造に付いて解 説し、行や列の生成と、其等をDGV コントロールに追加する為の基本的なプログラミングに付いて解 説した。然して、DGV コントロールを表示する簡単なサンプルプログラムを作成した。 DGV コントロールには、大きく以下の 2 つのモードが有る。 ・連結モード(バインドモード) ・非連結モード(非バインドモード) 前回のサンプルプログラムは、後者の非連結モードを利用した例で有る。今回は先ずは此の2 つのモー ドに付いて簡単に説明し、其の後で非連結モードの応用例と仕て別のサンプルプログラムを作成する。 ■ 連結モードと非連結モード

「連結モード」は、.NET Framework 1.x の頃の DataGrid コントロールと同様に、データベースから 取得したレコードで満たされたデータセットをDGV コントロールに「データ連結(Data Binding)」 し、データセットの内容をグリッド表示するモードで有る。

DGV コントロールの連結モード

データセットと連結する事に依り、データセットの内容をDGV コントロールのグリッドに自動的に反 映出来る。亦逆に、グリッド上での編集結果をデータセットに反映出来る。猶、此の図では省略して居 るが、.NET Framework 2.0 では、通常は DGV コントロールとデータセットとの間に BindingSource コントロールを使用する。 連結モードでは、DGV コントロールの DataSource プロパティに、データソースと成るデータセット (或いは、コレクション、配列等)を指定する丈で、其の内容が表形式で表示される。詰まり、データ ソースのレコードやデータに従って、DGV コントロール内に行オブジェクトが自動的に作成される訳 で有る(デフォルトでは、列オブジェクトも自動作成される)。 亦、DGV コントロールは、データソースを指定しなくても利用出来る様に成って居る。此れが「非連 結モード」で有る。此方のモードでは、列オブジェクトと行オブジェクトをコードに依り作成し、自由 にデータを表示する事が出来る。前回のサンプルプログラムでは此れを行った。

(13)

データバインド列と非バインド列 データ連結の観点からは、DGV コントロールの列にも 2 つの種類が有る。 連結モードでは、グリッドの各列は、データソース内の列(データソースがコレクションや配列の場合 には、其の要素の特定のプロパティ)に対応付けられて居る。其の様な列は「データバインド列」と呼 ばれる。 一方、データソース内の列に対応付けられて居ない列は「非バインド列」と呼ばれる。当然乍、非連結 モードでは総ての列が非バインド列に成る。 但し、連結モードでも総ての列がデータバインド列で有る必要は無く、非バインド列をDGV コントロ ールに追加する事も出来る。通常、此の場合には、非バインド列のセルには、データバインド列のセル から求められた値を表示する。例えば「合計値列」と仕て使用する非バインド列のセルでは、セルが表 示されるタイミングで、其の行の他のセルの値の合計値を求めて表示する。 ■ DGV コントロールを使ったマインスイーパ「Grid スイーパ」 今回は、非連結モードの応用例と仕て、DGV コントロールを使ったマインスイーパで有る「Grid スイ ーパ」を作成して観る事にする。マインスイーパを知らない人は居ないと思うが、Windows の[スタ ート]メニューの[総てのプログラム]-[ゲーム]-[マインスイーパ]で起動する爆弾撤去ゲーム で有る。 本物のマインスイーパでは、其のゲームフィールドを総てビットマップで表示して居るが、Grid スイー パではフィールド表示にグリッドを利用する。以下がGrid スイーパの実行画面で有る。

(14)

此の Grid スイーパでは、マウスのクリックや[スペース]キーに依り、セルを開いて行く。間違って 爆弾の有るセル(爆弾セル)を開くと撤去失敗で有り、爆弾セルを赤色で表示する(上記画面右上)。 爆弾セル以外の総てのセルを開ければ、爆弾セルが緑色で表示され、撤去成功と成る(上記画面右下)。 亦、[F2]キーを押す事に依り、何時でもゲームを再スタートする事が出来る。 猶コードを極力短くする為、マインスイーパには有る以下の様な機能は実装しない。 ・旗が立てられない ・時間制ではない ・数字の色が全部同じ 其れでは、早速プログラミングして行く事にする。 フォームの設定

先ずはVisual Studio 2005 を起動し、「Windows アプリケーション」の新規プロジェクトを作成する。 プロジェクト名は「GridSweeperVB」(C#版は「GridSweeperCS」)とする。 次に、DGV コントロールをフォームに配置する。此の時表示される「DataGridView タスク」のメニ ューから[親コンテナにドッキング]を選択して、フォーム全面に配置して置く(DGV コントロール の右上に有る小さな三角マークグリフからも可能)。 亦、プロパティウィンドウでは、名前「(Name)」を dgv に変更し、更に DefaultCellStyle プロパティ を選択すると現れる[...]ボタンをクリックし、セルで使用されるフォントに付いて、以下の 2 つの設 定を行う。 ・配置の[Alignment]を「MiddleCenter」に指定 ・表示用フォントを[Font]で指定(本稿では「Arial Black」の 9pt を選択) 此のDefaultCellStyle プロパティで設定されたセルスタイルは、DGV コントロールに追加される行で 使用される既定のセルスタイルと成る。此等の設定はコードからも行えるが、IDE で設定した方が楽で 有る。勿論、フォントの種類は自由に選択しても良い。 C#の場合には、更にプロパティウィンドウから次の 4 つのイベントに付いてイベントハンドラをフォー ムとDGV コントロールに追加して置く。 ・フォームのLoad イベント ・DGV コントロールの CellClick イベント ・DGV コントロールの KeyDown イベント ・DGV コントロールの SelectionChanged イベント 全ソースコード

Grid スイーパのソースコード(Form1.vb や Form1.cs に記述するコード)は 200 行程度なので、以下 に其の総てを示して置く。上記の設定を行い、此のコードを Form1.vb(C#の場合は Form1.cs)にコ ピー&ペーストすれば、プログラムを実行出来る筈で有る。

次のページからは、此の中のポイントと成る部分を解説して行く。

Visual Basic

Public Class Form1

(15)

' 定数の宣言

Const UNOPEN As Integer = -2 Const MINE As Integer = -1

' 変数の宣言

Dim cellSize As Integer = 20 ' セルのサイズ Dim sx As Integer = 16 ' フィールドの幅 Dim sy As Integer = 16 ' フィールドの高さ Dim numMine As Integer = 40 ' 爆弾の数

Dim numCellOpened As Integer ' 開いたセルの数

Dim gameStarted As Boolean ' ゲームを開始して居るか何うかのフラグ

Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs) _ Handles MyBase.Load ' 以下はプロパティウィンドウでも設定可能 dgv.ReadOnly = True dgv.ColumnHeadersVisible = False dgv.RowHeadersVisible = False dgv.AllowUserToResizeColumns = False dgv.AllowUserToResizeRows = False dgv.AllowUserToAddRows = False dgv.ShowCellToolTips = False ' 答えが観えない様に設定 ' フィールドの作成 dgv.RowTemplate.Height = cellSize ' 追加される行の高さ dgv.ColumnCount = sx dgv.RowCount = sy

For Each col As DataGridViewColumn In dgv.Columns col.Width = cellSize

Next

' フォームのサイズを DGV に合わせる Dim cell As DataGridViewCell = dgv(0, 0)

Me.ClientSize = New Size(cell.Size.Width * sx + 3, cell.Size.Height * sy + 3) initGame( ) End Sub ' ゲームの初期化 Sub initGame( ) numCellOpened = 0 gameStarted = True ' 総てのセルの初期化 For x As Integer = 0 To sx - 1 For y As Integer = 0 To sy - 1 dgv(x, y).Value = UNOPEN dgv(x, y).Style.ForeColor = Color.WhiteSmoke dgv(x, y).Style.BackColor = Color.WhiteSmoke Next Next

(16)

' 爆弾の配置

Dim rnd As New Random Dim doneNum As Integer = 0 While doneNum < numMine Dim x As Integer = rnd.Next(sx) Dim y As Integer = rnd.Next(sy) If CInt(dgv(x, y).Value) <> MINE Then dgv(x, y).Value = MINE doneNum += 1 End If End While showRemain( ) End Sub ' 残りセル(開いて居ないセル)の表示 Sub showRemain( )

Me.Text = "爆弾:" & numMine & " 残りのセル:" & (sx * sy - numCellOpened) End Sub

' (x, y)がグリッド内に含まれるか

Function isInField(ByVal x As Integer, ByVal y As Integer) If x < 0 Or y < 0 Or x >= sx Or y >= sy Then Return False End If Return True End Function ' cell に隣接するセルを配列で返す

Function getNeighbors(ByVal cell As DataGridViewCell) Dim x As Integer = cell.ColumnIndex

Dim y As Integer = cell.RowIndex

Dim cc As New List(Of DataGridViewCell)

If isInField(x - 1, y - 1) Then cc.Add(dgv(x - 1, y - 1)) If isInField(x - 1, y + 1) Then cc.Add(dgv(x - 1, y + 1)) If isInField(x + 1, y - 1) Then cc.Add(dgv(x + 1, y - 1)) If isInField(x + 1, y + 1) Then cc.Add(dgv(x + 1, y + 1)) If isInField(x - 1, y) Then cc.Add(dgv(x - 1, y))

If isInField(x + 1, y) Then cc.Add(dgv(x + 1, y)) If isInField(x, y - 1) Then cc.Add(dgv(x, y - 1)) If isInField(x, y + 1) Then cc.Add(dgv(x, y + 1))

Return cc.ToArray( ) End Function

' 数字セルの表示

Sub drawNumberCell(ByVal cell As DataGridViewCell) cell.Style.BackColor = Color.LightGray If CInt(cell.Value) = 0 Then ' 0 は表示しない(背景色で描画) cell.Style.ForeColor = Color.LightGray Else ' 1~8 の数字

(17)

cell.Style.ForeColor = Color.Blue End If

End Sub

' セルを開こうとする

Sub tryCell(ByVal cell As DataGridViewCell) If gameStarted = False Then

Return End If

If CInt(cell.Value) = MINE Then gameOver(False)

ElseIf CInt(cell.Value) = UNOPEN Then openCell(cell)

showRemain( )

If numCellOpened = sx * sy - numMine Then gameOver(True) End If End If End Sub ' セルを開く

Sub openCell(ByVal cell As DataGridViewCell) numCellOpened += 1

Dim count As Integer = 0

' 周りの爆弾の数を数える

For Each c As DataGridViewCell In getNeighbors(cell) If CInt(c.Value) = MINE Then

count += 1 End If Next ' 周りの爆弾の数を表示 cell.Value = count drawNumberCell(cell) ' 周りのセルに爆弾がない場合、周りのセルも開く If count = 0 Then

For Each c As DataGridViewCell In getNeighbors(cell) If CInt(c.Value) = UNOPEN Then

openCell(c) ' 再帰呼出 End If Next End If End Sub

Sub gameOver(ByVal isSuccess As Boolean) gameStarted = False Me.Text = "F2 キーでリトライ" ' 爆弾位置の表示 For x As Integer = 0 To sx - 1 For y As Integer = 0 To sy - 1

(18)

dgv(x, y).Value = ""

dgv(x, y).Style.BackColor = IIf(isSuccess, Color.Green, Color.Red) End If Next Next End Sub ' DGV の KeyDown イベントハンドラ

Private Sub dgv_KeyDown(ByVal sender As Object, ByVal e As KeyEventArgs) _ Handles dgv.KeyDown

If e.KeyCode = Keys.F2 Then initGame( )

End If

If e.KeyCode = Keys.Space Then tryCell(dgv.CurrentCell) End If

End Sub

' DGV の CellClick イベントハンドラ

Private Sub dgv_CellClick(ByVal sender As Object, ByVal e As DataGridViewCellEventArgs) _ Handles dgv.CellClick tryCell(dgv(e.ColumnIndex, e.RowIndex)) End Sub ' DGV の SelectionChanged イベントハンドラ

Private Sub dgv_SelectionChanged(ByVal sender As Object, ByVal e As EventArgs) _ Handles dgv.SelectionChanged ' セルを選択状態にさせない dgv.ClearSelection( ) End Sub End Class C# using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; namespace GridSweeperCS { public partial class Form1 : Form { int cellSize = 20; // セルのサイズ int sx = 16; // フィールドの幅 int sy = 16; // フィールドの高さ int numMine = 40; // 爆弾の数

const int UNOPEN = -2; const int MINE = -1;

(19)

int numCellOpened; // 開いたセルの数 bool gameStarted; // ゲームを開始して居るか public Form1( ) { InitializeComponent( ); }

private void Form1_Load(object sender, EventArgs e) { // 以下はプロパティウィンドウでも設定可能 dgv.ReadOnly = true; dgv.ColumnHeadersVisible = false; dgv.RowHeadersVisible = false; dgv.AllowUserToResizeColumns = false; dgv.AllowUserToResizeRows = false; dgv.AllowUserToAddRows = false; dgv.ShowCellToolTips = false; // 答えが観えない様に // フィールドの作成 dgv.RowTemplate.Height = cellSize; // 追加される行の高さ dgv.ColumnCount = sx; dgv.RowCount = sy;

foreach (DataGridViewColumn col in dgv.Columns) { col.Width = cellSize;

}

// フォームのサイズを DGV に合わせる DataGridViewCell cell = dgv[0, 0];

this.ClientSize = new Size(cell.Size.Width * sx + 3, cell.Size.Height * sy + 3); initGame( ); } // ゲームの初期化 void initGame( ) { numCellOpened = 0; gameStarted = true; // 総てのセルの初期化 for (int x = 0; x < sx; x++) {

for (int y = 0; y < sy; y++) { dgv[x, y].Value = UNOPEN; dgv[x, y].Style.ForeColor = Color.WhiteSmoke; dgv[x, y].Style.BackColor = Color.WhiteSmoke; } } // 爆弾の配置

Random rnd = new Random( );

(20)

{

int x = rnd.Next(sx); int y = rnd.Next(sy);

if ((int)dgv[x, y].Value != MINE) { dgv[x, y].Value = MINE; doneNum++; } } showRemain( ); } // 残りセル(開いて居ないセル)の表示 void showRemain( ) {

this.Text = "爆弾:" + numMine + " 残りのセル:" + (sx * sy - numCellOpened); }

// (x, y)がグリッド内に含まれるか bool isInField(int x, int y)

{ if (x < 0 || y < 0 || x >= sx || y >= sy) return false; return true; } // cell に隣接するセルを配列で返す

DataGridViewCell[] getNeighbors(DataGridViewCell cell) {

int x = cell.ColumnIndex; int y = cell.RowIndex;

List<DataGridViewCell> cc = new List<DataGridViewCell>( );

if (isInField(x - 1, y - 1)) cc.Add(dgv[x - 1, y - 1]); if (isInField(x - 1, y + 1)) cc.Add(dgv[x - 1, y + 1]); if (isInField(x + 1, y - 1)) cc.Add(dgv[x + 1, y - 1]); if (isInField(x + 1, y + 1)) cc.Add(dgv[x + 1, y + 1]); if (isInField(x - 1, y)) cc.Add(dgv[x - 1, y]);

if (isInField(x + 1, y)) cc.Add(dgv[x + 1, y]); if (isInField(x, y - 1)) cc.Add(dgv[x, y - 1]); if (isInField(x, y + 1)) cc.Add(dgv[x, y + 1]); return cc.ToArray( ); } // 数字セルの表示

void drawNumberCell(DataGridViewCell cell) { cell.Style.BackColor = Color.LightGray; if ((int)cell.Value == 0) { // 0 は表示しない(背景色で描画) cell.Style.ForeColor = Color.LightGray;

(21)

} else { // 1~8 の数字 cell.Style.ForeColor = Color.Blue; } } // セルを開こうとする

void tryCell(DataGridViewCell cell) { if (gameStarted == false) { return; } if ((int)cell.Value == MINE) { gameOver(false); }

else if ((int)cell.Value == UNOPEN) { openCell(cell); showRemain( ); if (numCellOpened == sx * sy - numMine) { gameOver(true); } } } // セルを開く

void openCell(DataGridViewCell cell) {

numCellOpened++; int count = 0;

// 周りの爆弾の数を数える

foreach (DataGridViewCell c in getNeighbors(cell)) { if ((int)c.Value == MINE) { count++; } } // 周りの爆弾の数を表示 cell.Value = count; drawNumberCell(cell); // 周りのセルに爆弾がない場合、周りのセルも開く if (count == 0) {

foreach (DataGridViewCell c in getNeighbors(cell)) {

(22)

if ((int)c.Value == UNOPEN) { openCell(c); // 再帰呼出 } } } }

void gameOver(bool isSuccess) { gameStarted = false; this.Text = "F2 キーでリトライ"; // 爆弾位置の表示 for (int x = 0; x < sx; x++) {

for (int y = 0; y < sy; y++) {

if ((int)dgv[x, y].Value == MINE) {

dgv[x, y].Value = "";

dgv[x, y].Style.BackColor = isSuccess ? Color.Green : Color.Red; } } } } // DGV の KeyDown イベントハンドラ

private void dgv_KeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.F2) { initGame( ); } if (e.KeyCode == Keys.Space) { tryCell(dgv.CurrentCell); } } // DGV の CellClick イベントハンドラ

private void dgv_CellClick(object sender, DataGridViewCellEventArgs e) {

tryCell(dgv[e.ColumnIndex, e.RowIndex]); }

// DGV の SelectionChanged イベントハンドラ

private void dgv_SelectionChanged(object sender, EventArgs e) { // セルを選択状態にさせない dgv.ClearSelection( ); } } }

(23)

以下では、プログラムの実行順に従って解説して行く。 DGV コントロールの初期設定 フォームのLoad イベントハンドラでは、DGV コントロールの初期設定を行う。此処では、先ず、行ヘ ッダ、列ヘッダを非表示にし、ユーザーがセルのサイズを変更出来ない様に仕て置く。 Load イベントハンドラでポイントと成るのは、ゲームフィールドの作成部分で有る。非連結モードで は、次の様に記述する丈で、sx 個の列オブジェクトと sy 個の行オブジェクトが作成される。従って最 終的には、sx * sy 個のセルが作成される事に成る。 dgv.ColumnCount = sx dgv.RowCount = sy 行数と列数の指定に依るセルの作成(Load イベントハンドラ内) 此 の 様 に 仕 て 作 成 さ れ る 列 オ ブ ジ ェ ク ト は 、 最 も 標 準 的 な 列 で 有 る テ キ ス ト ボ ッ ク ス 列 (DataGridViewTextBoxColumn オブジェクト)で有る。此の列に属するセルは、其の Value プロパ ティにセットした値が文字列と仕て表示される。 但し、此れ丈では行や列のサイズがデフォルトの儘で有る。Grid スイーパではセルを正方形にし度いの で、上記の2 つのプロパティを設定する前に以下のコードに依り、先ず追加される行の高さを指定する。 dgv.RowTemplate.Height = cellSize ' 追加される行の高さ 行テンプレートにおける行の高さの指定(Load イベントハンドラ内) DGV コントロールに行が追加される場合には、其の RowTemplate プロパティにセットされて居る行オ ブジェクトが行のテンプレート(雛形)と成る為、此処で行のスタイルを調整して置けば、以降の行の 追加時には、其のスタイルが総ての行で使用される様に成る。 一方、列の幅に関しては此の様な列テンプレートが存在しない為、次の様に仕て、総ての列に対して Width プロパティを設定する必要が有る。

For Each col As DataGridViewColumn In dgv.Columns col.Width = cellSize Next 各列の幅の設定(Load イベントハンドラ内) 勿論、行の高さに関しても、総ての行に付いてループに依りHeight プロパティを設定しても良い。以 上に依り、正方形のセルが敷き詰められたフィールドの出来上がりで有る。 ゲームフィールドの初期化 DGV コントロールの初期設定が終われば、次に initGame メソッドでゲームフィールドの初期化を行う。 マインスイーパでは、各セルに付いて少なく共2 つの状態を管理する必要が有る。爆弾が有るか無いか と、既に開かれたか何うかで有る(尤も、既に開かれた爆弾の有るセルと謂うのは、其の時点でゲーム が終了なので存在しない)。 Grid スイーパでは、此の状態を各セルの Value プロパティの値で管理して仕舞う。詰まり、総てのセ ルのValue プロパティを、先ず、未だセルが開かれて居ない事を示す UNOPEN(-1)に設定する。此 れに依りセルの表示が総て「-1」と成って仕舞うが、其の背景色と前景色を同じにする事に依り、其れ

(24)

For x As Integer = 0 To sx - 1 For y As Integer = 0 To sy - 1 dgv(x, y).Value = UNOPEN ' 開かれて居ない状態 dgv(x, y).Style.ForeColor = Color.WhiteSmoke dgv(x, y).Style.BackColor = Color.WhiteSmoke Next Next 総てのセルの初期化(initGame メソッド内) 前回でも説明した様に、各セルはDGV コントロールのインデクサに依り、dgv(x, y) の形で指定出来る (此処で「dgv」は DGV コントロールのインスタンス)。 クリックされ開かれたセルのValue プロパティには、其のセルの周りの爆弾の数を代入する事に成るの だが(爆弾が無ければ 0 ではなく空文字を代入する)、其の時は前景色と背景色を変更する事に依りセ ルを開いた様に観せて居ると謂う訳で有る。 次に、乱数で爆弾の位置を決め、其の位置のセルのValue プロパティを MINE(-2)に設定する。 Dim rnd As New Random

Dim doneNum As Integer = 0 ' 爆弾を配置し終える迄ループ While doneNum < numMine ' 乱数で爆弾の位置を決める Dim x As Integer = rnd.Next(sx) Dim y As Integer = rnd.Next(sy)

' 爆弾が無ければ爆弾を配置

If CInt(dgv(x, y).Value) <> MINE Then dgv(x, y).Value = MINE doneNum += 1 End If End While 爆弾の配置(initGame メソッド内) セルのValue プロパティを設定する場合には、単に文字列や数値を代入すれば良いが(表示時には文字 列に変換されて表示される)、Value プロパティは Object 型なので、其の値を取り出す時には、 CInt(dgv(x, y).Value) の様に、元の型にキャストする必要が有る。 処で、此の時点で爆弾を配置するのは本当は正しくない。此れでは最初に開いたセルが行き成り爆弾と 謂うナンセンスな状況が有り得る為で有る。本来で有れば1 つ目のセルが開かれた後で爆弾を配置す可 きなのだが、今回は其の様な処理は割愛した。 initGame メソッドでは、既に開いたセルの数 numCellOpened を 0 にし、ゲーム中か何うかを示すフ ラグgameStarted を true に設定して置く。 セルの選択を禁止 DGV コントロールでは、通常のセルの値は、其のセルのスタイルの ForeColor プロパティや BackColor プロパティで設定された色で表示されるが、グリッド上で選択されて居るセルに付いては、此等のプロ パティの色ではなく、SelectionForeColor プロパティや SelectionBackColor プロパティの色が使用さ れる。

(25)

此の為、Grid スイーパでは、セルが選択された時にもセルに書き込んだ値(-1 や-2)が観えない様に、 選択されたセルの色も同時に設定する必要が有る。然うしないと次の画面の様に答えが観えて仕舞う。 選択すると観えて仕舞うセルの値 選択されたセルの色は通常のセルの色とは別に設定されて居る為、其の色も変更して置かないと、此の 様な状態に成って仕舞う。 併し、此等の設定はコードを煩雑にするので、今回はセルの選択自体を出来なくして仕舞って居る(此 れに依りフォーカスが移動しても、セルが選択されない様に成る)。 但し、DGV コントロールには、現在選択されて居るセルの選択を解除する為の ClearSelection メソッ ドが用意されて居るが、セルの選択を禁止する様な機能は用意されて居ない(※)。 ※ MultiSelect プロパティを False に設定すれば、複数のセルの選択は禁止出来る。 其処で今回は、選択されて居るセルが変化した時に発生するSelectionChanged イベントのタイミング で ClearSelection メ ソッド を呼び出し て、セルが 選択されて も直ぐに其 れを解除し て居る。 SelectionChanged イベントハンドラは次の様に成る。

Private Sub dgv_SelectionChanged(ByVal sender As Object, ByVal e As EventArgs) _ Handles dgv.SelectionChanged ' セルを選択状態にさせない dgv.ClearSelection( ) End Sub セルが選択されない様にするSelectionChanged イベントハンドラ ClearSelection メソッドの呼出に依り現在選択されて居るセルが変化するが、此の時は幸いに仕て SelectionChanged イベントは発生しない様で有る。 因みに、選択されて居るセルをコードから設定するには、セルのSelected プロパティを True に設定す れば良い。蛇足に成るが、SelectionChanged イベントハンドラで ClearSelection メソッドを呼び出し

(26)

セルのクリック処理 DGV コントロールでは、セルがクリックされると、其のセルが選択されフォーカスが設定されるが、 Grid スイーパではセルがクリックされたら、其のセルを開く(実際には開いた様に観せる為に色を変更 し、Value プロパティを書き換える)処理が必要で有る。 其処で、セルがクリックされた時に発生するCellClick イベントをハンドリングする。CellClick イベン トハンドラでは、メソッドのパラメータと仕てDataGridViewCellEventArgs オブジェクトが渡される ので、其のColumnIndex プロパティと RowIndex プロパティに依り、何の位置のセルがクリックされ たかを知る事が出来る。詰まり、クリックされたセルオブジェクトは、dgv(e.ColumnIndex, e.RowIndex) に依り得る事が出来る。 CellClick イベントハンドラは、次の様に成る。此処ではクリックされたセルを tryCell メソッドに渡す。 Private Sub dgv_CellClick(ByVal sender As Object, ByVal e As DataGridViewCellEventArgs) _ Handles dgv.CellClick ' クリックされたセルを tryCell メソッドに渡す tryCell(dgv(e.ColumnIndex, e.RowIndex)) End Sub DGV コントロールの CellClick イベントハンドラ キー入力の処理 DGV コントロールでは予め多くのキーをハンドリングして居り(※)、例えば、フォーカスの有るセル を移動するには、カーソルキーやタブキーが使える。 ※ キーの一覧は、Windows フォーム DataGridView コントロールの既定のキーボード処理とマウス処 理に有る。 Grid スイーパでは、[F2]キーでゲームの再スタートを行い、[スペース]キーで現在フォーカスの有 るセルを開く様にして、キーボード丈でもゲームが出来る様に仕て観た。 キーの入力に関しては、KeyDown イベントに対応すれば良い。此のイベントハンドラは次の様に成る。 e.KeyCode が DGV コントロールに対して入力されたキーのコードと成る。

Private Sub dgv_KeyDown(ByVal sender As Object, ByVal e As KeyEventArgs) _ Handles dgv.KeyDown

' [F2]キーで再スタート If e.KeyCode = Keys.F2 Then initGame( )

End If

' [スペース]キーで現在のセルを開く If e.KeyCode = Keys.Space Then tryCell(dgv.CurrentCell) End If End Sub DGV コントロールの KeyDown イベントハンドラ 現在フォーカスの有るセルは、DGV コントロールの CurrentCell プロパティに依って得られるので、[ス ペース]キーが入力された場合には、セルのクリック時と同様に、此れをtryCell メソッドに渡す。

(27)

セルのオープン

tryCell メソッドでは、開こうとするセルの値が爆弾(MINE)で有れば、即ゲームオーバーだが、未だ 開かれて居ないセル(UNOPEN)で有れば、openCell メソッドを呼び出して、其れを開く。

此等の処理が終わった後、若し、既に開かれて居るセルの数(numCellOpened)が爆弾の無いセルの 数(sx * sy - numMine)と等しければ、爆弾撤去が完了で有る。

Sub tryCell(ByVal cell As DataGridViewCell) ' ゲームが開始されていなければ何もしない If gameStarted = False Then

Return End If

If CInt(cell.Value) = MINE Then

gameOver(False) ' 爆弾だったらゲームオーバー ElseIf CInt(cell.Value) = UNOPEN Then

openCell(cell) ' 実際にセルを開く showRemain( )

' 開いたセルと爆弾のないセルが同じ数なら撤去完了 If numCellOpened = sx * sy - numMine Then gameOver(True) End If End If End Sub tryCell メソッド セルの実際のオープン クリックされたセルを実際に開いて数字(又は、空文字)に書き換える処理は openCell メソッドで行 って居る。マインスイーパでは、単にクリックされたセルを開く丈でなく、セルに爆弾が無かったら連 鎖的に周りのセルも開かなければ成らない場合が有るので、此のメソッドは少し丈複雑で有る。 openCell メソッドでは先ず、メソッドのパラメータで指定されたセルに隣接するセル(最高 8 つ)を getNeighbors メソッドに依り取得する。然して爆弾の有るセルの数を数え、其れをセルの値とし、 drawNumberCell メソッドに依り表示する。

Sub openCell(ByVal cell As DataGridViewCell) numCellOpened += 1

Dim count As Integer = 0

' 周りの爆弾の数を数える

For Each c As DataGridViewCell In getNeighbors(cell) If CInt(c.Value) = MINE Then

count += 1 End If Next ' 周りの爆弾の数を表示 cell.Value = count drawNumberCell(cell)

(28)

If count = 0 Then

For Each c As DataGridViewCell In getNeighbors(cell) If CInt(c.Value) = UNOPEN Then

openCell(c) ' 再帰呼出 End If Next End If End Sub openCell メソッド 隣接するセルに爆弾が有れば、セルに数字を書いて処理は終わりだが、若し、隣接するセルの孰れにも 爆弾が無かったら、其等のセルを開き、更に其のセルの周りにも爆弾が無かったら其等を開き…と謂う 処理を然う謂うセルが無く成る迄繰り返さなくては成らない。 処理は難しいがサンプルコードの様にメソッドの再帰呼出を使えばコードは非常に単純に記述出来る。 隣接する未だ開いて居ない各セルに対して、更にopenCell メソッドを呼び出す丈で有る。 ■ 以上、今回はゲームを作り乍、DGV コントロールの非連結モードに付いて解説して観た。DGV コント ロールは、データ連結しなくても手軽に使える様に成って居るので、此れ迄以上に様々な用途で活用す る事が出来るのではないかと思う。

参照

関連したドキュメント

SVF Migration Tool の動作を制御するための設定を設定ファイルに記述します。Windows 環境 の場合は「SVF Migration Tool の動作設定 (p. 20)」を、UNIX/Linux

・「下→上(能動)」とは、荷の位置を現在位置から上方へ移動する動作。

は、これには該当せず、事前調査を行う必要があること。 ウ

Bluetooth® Low Energy プロトコルスタック GUI ツールは、Microsoft Visual Studio 2012 でビルドされた C++アプリケーションです。GUI

Windows Hell は、指紋または顔認証を使って Windows 10 デバイスにアクセスできる、よ

Visual Studio 2008、または Visual Studio 2010 で開発した要素モデルを Visual Studio

【オランダ税関】 EU による ACXIS プロジェクト( AI を活用して、 X 線検査において自動で貨物内を検知するためのプロジェク

ダウンロードしたファイルを 解凍して自動作成ツール (StartPro2018.exe) を起動します。.