- PR -

DataGridViewの独自列(カラム)でのイベント処理について

1
投稿者投稿内容
Leo
会議室デビュー日: 2008/11/11
投稿数: 13
投稿日時: 2009-02-24 11:16
C#です。
DataGridViewに独自の列(カラム)を作成し、各セルごとに独自のイベントが発生するようにしたいと思っています。

今のところ、DataGridViewTextBoxCellから派生した独自セルクラス内で、イベントを発生させることができるようになったので、その中でイベントに対する処理を記述しています。
しかし、この方法では、イベント処理内容もセルクラスに記述することになるため、汎用性がないと感じています。
もっと上位(例えば、Formクラスのコンストラクタなど)でそのイベントを登録および捕捉することは可能でしょうか?

[現在の方法]
 Windows Form 内      :DataGridViewに独自列を追加。
 DataGridViewControlCell 内 :イベント登録+イベント発生 + イベント処理

[望む方法]
 Windows Form 内      :DataGridViewに独自列を追加。+イベント登録+イベント発生+イベント処理


現在の構成は以下です。

コード:

// Windows Form
public class Form1 : Form
{
public Form1()
{
...
// 列を追加する。
DataGridViewControlColumn col = new DataGridViewControlColumn();
col.Name = "Controls";
col.Width = 200;
dataGridView1.Columns.Add(col);
// ここで、セルのイベント登録することは可能?
...
}
}

// 独自列クラス
public class DataGridViewControlColumn : DataGridViewColumn
{
public DataGridViewControlColumn()
{
this.CellTemplate = new DataGridViewControlCell();
}
}

// 独自セルクラス
public class DataGridViewControlCell : DataGridViewTextBoxCell
{
...
public delegate void ControllCellCommand(object sender, DataGridViewControllCellEventArgs e); // デリゲート
public event ControllCellCommand FireControllCellCommand; // イベント
...

public DataGridViewControlCell()
{
this.FireControllCellCommand += new ControllCellCommand(OnMouseCommand);
}

protected override void OnMouseUp(DataGridViewCellMouseEventArgs e)
{
...
// 独自のイベントを発行する。
if (FireControllCellCommand != null) {
FireControllCellCommand(this, eventCommand);
}
...
}

protected void OnMouseCommand(object sender, DataGridViewControllCellEventArgs e)
{
// 独自処理を記述する。
...
}
}



よろしくお願いします。

[ メッセージ編集済み 編集者: Leo 編集日時 2009-02-24 11:20 ]
テッテ
ベテラン
会議室デビュー日: 2008/03/16
投稿数: 91
投稿日時: 2009-02-24 11:38
public として宣言されているイベントなら、インスタンスが参照できるならイベントハンドラを登録することは可能ですが、そういう意味でしょうか?

例えば1行1列目のセルがその DataGridViewControlCell 型だとして、そのセルに対してイベントハンドラを登録したいなら

コード:

dataGridView1[0, 0].FireControllCellCommand += 
    new ControllCellCommand(DataGridViewCell_FireControllCellCommand);


こういうことではなく?



ただし、DataGridView のセルには共有行という考え方があるので、個々のセルにイベントを登録するとややこしいことになる可能性もあるような気がします。(やったことがないのでわかりませんが)

[Windows フォーム DataGridView コントロールを拡張するための推奨される手順](MSDN)
http://msdn.microsoft.com/ja-jp/library/ha5xt0d9(VS.80).aspx
Leo
会議室デビュー日: 2008/11/11
投稿数: 13
投稿日時: 2009-02-24 13:39
レスありがとうございます。

ご提示いただいた方法で、Windows Form内でイベントを登録することができました。

コード:

// 行が追加されたときに発生するイベント。
private void myDataGridView1_RowsAdded(object sender, DataGridViewRowsAddedEventArgs e)
{
    foreach (DataGridViewCell cell in myDataGridView1.Rows[e.RowIndex].Cells) {
        DataGridViewControlCell cellCtrl = cell as DataGridViewControlCell;
        if (cellCtrl == null) continue;
        cellCtrl.FireControllCellCommand += new DataGridViewControlCell.ControllCellCommand(OnMouseCommand);
    }
}



テッテさんがおっしゃるように、個々のセルにではなく、
データ追加前に、列全体に対してテンプレート的にイベント登録する方法があれば、エレガントなんでしょうけど。
Leo
会議室デビュー日: 2008/11/11
投稿数: 13
投稿日時: 2009-02-25 09:16
自己レスです。

以下のようにすることで、列全体にイベント登録できました。

・独自セルクラスにおいてCloneメソッドをオーバーライド。ここでイベントを代入。
・利用側(フォーム)で、カラム(列)のテンプレートにイベントを登録。

[独自列、セルクラス]
コード:
// 独自列クラス
public class DataGridViewControlColumn : DataGridViewColumn
{
    public DataGridViewControlColumn()
    {
        this.CellTemplate = new DataGridViewControlCell();
    }
}

// 独自セルクラス
public class DataGridViewControlCell : TgvDataGridViewTextBoxCell
{
    ...
    public delegate void ControllCellCommand(object sender, DataGridViewControllCellEventArgs e); // デリゲート
    public event ControllCellCommand FireControllCellCommand; // イベント
    ...

    public DataGridViewControlCell()
    {
        ...
    }

    // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
    // ※ Cloneメソッドをオーバーライド ※
    public override object Clone()
    {
        DataGridViewControlCell cell = (DataGridViewControlCell)base.Clone();
        cell.FireControllCellCommand = this.FireControllCellCommand;
        return cell;
    }
    // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑

    protected override void OnMouseUp(DataGridViewCellMouseEventArgs e)
    {
        ...
        // 独自のイベントを発行する。
        if (FireControllCellCommand != null) {
            FireControllCellCommand(this, eventCommand);
        }       
        ...
    }
    ...
}



[利用側(フォーム)]
コード:
// Windows Form
public class Form1 : Form
{
    public Form1()
    {
        ...
        // 列を追加する。
        DataGridViewControlColumn col = new DataGridViewControlColumn();
        col.Name = "Controls";
        col.Width = 200;

        // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
        // ※ 独自列のテンプレートにイベントを登録 ※
        DataGridViewControlCell cellTemplate = (DataGridViewControlCell)colButton.CellTemplate;
        cellTemplate.FireControllCellCommand += new DataGridViewControlCell.ControllCellCommand(OnMouseCommand); 
        // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑

        dataGridView1.Columns.Add(col);

        ...
    }

    protected void OnMouseCommand(object sender, DataGridViewControllCellEventArgs e)
    {
        // ここにイベント処理を記述する。
        ...
    }
}



テッテ
ベテラン
会議室デビュー日: 2008/03/16
投稿数: 91
投稿日時: 2009-02-25 09:40
列のすべてのセルのイベントを処理するのが目的でしたか。「各セルごとに」と書いてあったので、本当にセル毎に違う処理を行うのかと思っていました。

列のイベントを処理したいなら、DataGridViewControlColumn 側にイベントを定義して、それをフォーム側でハンドルすることができると思います。

DataGridViewControlColumn のコンストラクタで CellTemplate を設定するときにセルのイベントをハンドルして、そのイベントが発生したときに列のイベントを発生させるようにすれば可能だと思います。

発生元セルを特定できる情報を、イベント引数に持たせる必要があると思いますが。

コード:
// 独自列クラス
public class DataGridViewControlColumn : DataGridViewColumn
{
    public event イベント型 イベント名;

    public DataGridViewControlColumn()
    {
        DataGridViewControlCell cellTemplate = new DataGridViewControlCell();
        cellTemplate.ControllCellCommand += OnFireControllCellCommand;
        this.CellTemplate = cellTemplate;
    }

    private void OnFireControllCellCommand(object sender, イベント引数型 e)
    {
        // ここでイベント引数を作成

        if (イベント名 != null) {
            イベント名(this, イベント引数);
        }
    }
}



本題と関係ないですが、イベントのデリゲートは独自に宣言するのではなくて、ジェネリックの EventHandler を使って、EventHandler<DataGridViewControllCellEventArgs> とするのがよいと思います。

また、これもどうでもいいですが、ControllCellCommand の最初の「コントロール」の l が1つ多いです。(Control)
Leo
会議室デビュー日: 2008/11/11
投稿数: 13
投稿日時: 2009-02-25 17:32
返信ありがとうございます。

テッテさんのやり方で、イベントが発生することを確認できました。
各セル→列→上位(フォームなど)というように、順番にイベントが伝達されるイメージですね。

ジェネリックのイベントの件、ご指摘ありがとうございます。
(この方法がMicrosoft推奨とのことらしい)
delegate を宣言しなくて良いなど、簡潔に記述できますね。

コード:
// イベントの登録
cellTemplate.FireControlCellCommand += new EventHandler<DataGridViewControlCellEventArgs>(OnMouseCommand);

// イベントの定義
public event EventHandler<DataGridViewControlCellEventArgs> FireControlCellCommand;



勉強になりました。
ありがとうございました。(スペルミスの件も)
1

スキルアップ/キャリアアップ(JOB@IT)