データグリッドビュー

Similar documents
ListViewコントロール

ICONファイルフォーマット

NotifyIconコントロール

データベースプログラミング

VB 資料 電脳梁山泊烏賊塾 音声認識 System.Speech の利用 System.Speech に依るディクテーション ( 音声を文字列化 ).NetFramework3.0 以上 (Visual Studio 2010 以降 ) では 標準で System.Speech が用意されて居るの

ファイル操作

Userコントロール

プロセス間通信

Visual Basic 資料 電脳梁山泊烏賊塾 コレクション初期化子 コレクション初期化子 初めに.NET 版の Visual Basic では 其れ迄の Visual Basic 6.0 とは異なり 下記の例の様に変数宣言の構文に 初期値を代入する式が書ける様に成った 其の際 1 の様に単一の値

VB.NET解説

ハッシュテーブル

データアダプタ概要

グラフィックス

ルーレットプログラム

正規表現応用

ファイル監視

ファイル操作-バイナリファイル

ブロック パニック

C#の基本

ウィンドウ操作 応用

DAOの利用

構造体

グラフィックトレーニング 概要.NET のグラフィック描画は どんなことができるのでしょうか? グラフィックオブジェクトやグラフィック環境 概念を理解するためには クラスを使って馴れることが近道です 本 書に記載されているコードをカットアンドペーストして 一つ一つの機能を体験してください 前提 グラ

スライド 1

64bit環境で32bitコンポーネントの利用

.NETプログラマー早期育成ドリル ~VB編 付録 文法早見表~

VB実用Ⅲ⑩ フリーデータベースⅡ

グラフィックス 目次

スレッド操作 タイマー

データベースプログラミング

...Visual Studio 2015\Projects\MyHomePage 用サンプル \Database(Access2)\Database(Access2)\MainForm.cs 2 れを含めておかないと Database への更新がきかない oadp.fill(dtbl); dgvk

Microsoft Word - VB.doc

グラフィックス 目次

プログラミング基礎I(再)

Microsoft Excel操作

ブロック崩し風テニス

C#の基本2 ~プログラムの制御構造~

VB.NET解説

データベースⅠ

mySQLの利用

VFD256 サンプルプログラム

VB実用⑦ エクセル操作Ⅰ

印刷

目次 はじめに... 3 システムの必要条件... 4 ライセンス認証... 4 アクティベーション... 6 開発... 7 手順 1. アプリケーションの作成... 7 手順 2. データソースの作成と代入... 7 手順 3. テンプレートの作成 手順 4. レポートビューアの追加

モグラ叩きプログラム

エクセル詳細 アドイン

PowerPoint プレゼンテーション

VB実用① データベースⅠ

占領双六ゲーム

API 連携方式 外部 DLL の呼び出し宣言 外部 DLL の呼び出し宣言のサンプルコード (Microsoft Visual C#.NET の場合 ) プログラムコードの先頭で using System.Runtime.InteropServices; が必要 クラスの内部に以下のような外部 D

Javaプログラムの実行手順

PYTHON 資料 電脳梁山泊烏賊塾 PYTHON 入門 関数とメソッド 関数とメソッド Python には関数 (function) とメソッド (method) が有る モジュール内に def で定義されて居る物が関数 クラス内に def で定義されて居る物がメソッドに成る ( 正確にはクラスが

インベーダープログラム

3D回転体プログラム

目次 はじめに... 3 システムの必要条件... 3 サンプルアプリケーションの作成... 3 手順 手順 手順 手順 手順 手順 終わりに... 23

万年暦プログラム

PowerPoint プレゼンテーション

TestDesign for Web

ExcelVBA

(Microsoft Word \203v\203\215\203O\203\211\203~\203\223\203O)

回文作成支援プログラム

データグリッドビュー

PowerPoint プレゼンテーション

MVP for VB が語る C# 入門

PowerPoint プレゼンテーション

占領双六ゲーム

正規表現概要

FileExplorer for ASP.NET Web Forms

草競馬プログラム

印刷

WebBrowserコントロール

GS1-128 の描画 DLL について (ver. 2.3) 動作環境など動作環境 WindowsXP Windows Vista Windows7 Windows8/8.1 Windows10 上記 OS について すべて日本語版を対象としております 32bit アプリケーションから呼び出される

ASP.NET 2.0 Provider Model 概要

Transcription:

データグリッドビュー.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 コントロールに付いて多くのノウハウやテクニックを蓄積して来たと謂うのに 其等の殆どを捨てて仕舞わなければ成らないので有る! 今後は此の様な互換性の無い置き換えは控えて欲しい物で有る -1-

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

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

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

図の左上に有る 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) 列オブジェクトの追加 (3) 行オブジェクトの追加 (= データの追加 ) -5-

例えば データベースでは 最初にスキーマ ( カラムと成る項目 ) を決めてからしかレコードを追加出来ない様に DGV コントロールでも 先ず 列オブジェクトを追加して置き 其の列の種類に従った幾つかのセルを含む行オブジェクトを追加して行く事に成る 列オブジェクトの作成と追加 手順 (1) の DGV クラスのインスタンス化は 単にクラスを New する丈で有る Dim dgv As New DataGridView( ) DataGridView dgv = new DataGridView( ); Visual Basic C# 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 コントロールに列がセットされる事に成る dgv.columns.add(column1) dgv.columns.add(column2) dgv.columns.add(column1); dgv.columns.add(column2); Visual Basic C# 因みに Visual Studio 2005 を使って居る場合には DGV コントロールをツールボックスからドラッグ & ドロップし [DataGridView タスク ] メニューから列の追加を選択すれば ダイアログから列を追加する事が出来る -6-

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

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 プロパティを読み出すケースは少なくない ( ) -8-

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

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 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); -10-

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 コントロールでは 従来のデータバインドに依るデータの表示に加えて 非バインドモード ( 今回のサンプルプログラムは此れで有る ) や 大量のデータを扱う時に利用出来る 仮想モード 等が用意されて居る 次回は此の辺りの解説から始める予定で有る -11-

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 コントロールは データソースを指定しなくても利用出来る様に成って居る 此れが 非連結モード で有る 此方のモードでは 列オブジェクトと行オブジェクトをコードに依り作成し 自由にデータを表示する事が出来る 前回のサンプルプログラムでは此れを行った -12-

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

此の 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) にコピー & ペーストすれば プログラムを実行出来る筈で有る 次のページからは 此の中のポイントと成る部分を解説して行く Public Class Form1 Visual Basic -14-

' 定数の宣言 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( ) ' ゲームの初期化 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 -15-

' 爆弾の配置 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 While showremain( ) ' 残りセル ( 開いて居ないセル ) の表示 Sub showremain( ) Me.Text = " 爆弾 :" & nummine & " 残りのセル :" & (sx * sy - numcellopened) ' (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 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 の数字 -16-

cell.style.forecolor = Color.Blue ' セルを開こうとする Sub trycell(byval cell As DataGridViewCell) If gamestarted = False Then Return 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) ' セルを開く 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 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) ' 再帰呼出 Next 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 If CInt(dgv(x, y).value) = MINE Then -17-

dgv(x, y).value = "" dgv(x, y).style.backcolor = IIf(isSuccess, Color.Green, Color.Red) Next Next ' DGV の KeyDown イベントハンドラ Private Sub dgv_keydown(byval sender As Object, ByVal e As KeyEventArgs) _ Handles dgv.keydown If e.keycode = Keys.F2 Then initgame( ) If e.keycode = Keys.Space Then trycell(dgv.currentcell) ' DGV の CellClick イベントハンドラ Private Sub dgv_cellclick(byval sender As Object, ByVal e As DataGridViewCellEventArgs) _ Handles dgv.cellclick trycell(dgv(e.columnindex, e.rowindex)) ' DGV の SelectionChanged イベントハンドラ Private Sub dgv_selectionchanged(byval sender As Object, ByVal e As EventArgs) _ Handles dgv.selectionchanged ' セルを選択状態にさせない dgv.clearselection( ) End Class using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; C# 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; -18-

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( ); for (int donenum = 0; donenum < nummine; ) -19-

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; -20-

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)) -21-

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( ); -22-

以下では プログラムの実行順に従って解説して行く 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 と成って仕舞うが 其の背景色と前景色を同じにする事に依り 其れが表示されない様にして居る 此れを行って居るのが initgame メソッドの次のループで有る -23-

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

此の為 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( ) セルが選択されない様にする SelectionChanged イベントハンドラ ClearSelection メソッドの呼出に依り現在選択されて居るセルが変化するが 此の時は幸いに仕て SelectionChanged イベントは発生しない様で有る 因みに 選択されて居るセルをコードから設定するには セルの Selected プロパティを True に設定すれば良い 蛇足に成るが SelectionChanged イベントハンドラで ClearSelection メソッドを呼び出した後に此れを行うとスタックオーバーフローが発生してプログラムは停止して仕舞う -25-

セルのクリック処理 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)) 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( ) ' [ スペース ] キーで現在のセルを開く If e.keycode = Keys.Space Then trycell(dgv.currentcell) DGV コントロールの KeyDown イベントハンドラ 現在フォーカスの有るセルは DGV コントロールの CurrentCell プロパティに依って得られるので [ スペース ] キーが入力された場合には セルのクリック時と同様に 此れを trycell メソッドに渡す -26-

セルのオープン trycell メソッドでは 開こうとするセルの値が爆弾 (MINE) で有れば 即ゲームオーバーだが 未だ開かれて居ないセル (UNOPEN) で有れば opencell メソッドを呼び出して 其れを開く 此等の処理が終わった後 若し 既に開かれて居るセルの数 (numcellopened) が爆弾の無いセルの数 (sx * sy - nummine) と等しければ 爆弾撤去が完了で有る Sub trycell(byval cell As DataGridViewCell) ' ゲームが開始されていなければ何もしない If gamestarted = False Then Return 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) 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 Next ' 周りの爆弾の数を表示 cell.value = count drawnumbercell(cell) ' 周りのセルに爆弾がない場合 周りのセルも開く -27-

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