Adsense_top

2009年5月29日金曜日

C# DataGridView セルの結合 その2

「その1」で書いた分にフォーカスが入った場合の描画処理を追加が考えていたより厄介なようです。
手間が掛かるのであればと言う事で、DataGridViewTextBoxColumnを拡張して結合可能セルの作成に挑戦する事にしました。
と言っても、本当の意味での結合をさせるには、セルを拡張するのではなくDataGridView自体を拡張を行う必要がある事は明らかですが、ハッキリ言って私には荷が重過ぎます(キッパリ)。
取りあえず、編集可能で見た目はセルが結合されているように見えるものにはしたいと思います。
先ずは、行の結合が出来るものから少しずつ...(列の結合は行の結合が完成したら...)。
アドバイス大歓迎ですので、あれば下さい。

DataGridViewTextBoxColumnとDataGridViewTextBoxCellを継承して拡張します。
DataGridViewTextBoxEditingControl に関しては、今のところは拡張せずに使う予定です。

一気には出来ませんので、今回は編集時に結合されているように編集コントロール(TextBox)のサイズを合わせて表示するようにします。
public class DgvMergebleTextBoxColumn : DataGridViewTextBoxColumn{
  // 結合行数
  private int mergeRows = 1;
  // コンストラクタ
  public DgvMergebleTextBoxColumn() {
    this.CellTemplate = new DgvMergebleTextBoxCell();
  }
  // 結合行数を設定または取得します。
  public int MergeRows {
    get {
      return
      ((DgvMergebleTextBoxCell)this.CellTemplate).MergeRows;
    }
    set {
      if(this.mergeRows == value)
        return;
      //セルテンプレートの値を変更する
      ((DgvMergebleTextBoxCell)this.CellTemplate).MergeRows
      = value;
      //DataGridViewにすでに追加されているセルの値を変更する
      if(this.DataGridView == null)
        return;
      int rowCount = this.DataGridView.RowCount;
      for(int i = 0; i < rowCount; i++) {
        DataGridViewRow r = this.DataGridView.Rows.SharedRow(i);
        ((DgvMergebleTextBoxCell)r.Cells[this.Index]).MergeRows
        = value;
      }
    }
  }
  // MergeRowsプロパティを追加しているので、
  // Clone()をオーバーライドします。
  public override object Clone() {
    DgvMergebleTextBoxColumn col =
    (DgvMergebleTextBoxColumn)base.Clone();
    col.MergeRows = this.MergeRows;
    return col;
  }
}

public class DgvMergebleTextBoxCell : DataGridViewTextBoxCell {
  // 結合行数
  private int mergeRows = 1;
  public override object Clone() {
    DgvMergebleTextBoxCell cel =
    (DgvMergebleTextBoxCell)base.Clone();
    cel.MergeRows = this.MergeRows;
    return cel;
  }
  /// 

  /// DataGridView 内でセルがいくつの行にまたがって表示されるかを
  /// 示す値を取得または設定します。
  /// 

  public int MergeRows {
    get { return mergeRows; }
    set { mergeRows = value; }
  }
  // ホストされる編集コントロールの位置とサイズを設定します。
  public override void PositionEditingControl(
  bool setLocation, bool setSize,
  Rectangle cellBounds,
  Rectangle cellClip,
  DataGridViewCellStyle cellStyle,
  bool singleVerticalBorderAdded,
  bool singleHorizontalBorderAdded,
  bool isFirstDisplayedColumn,
  bool isFirstDisplayedRow) {
    // 結合するセルの高さに合わせるために、結合するセルの高さを足す
    Rectangle mergeCellBounds = cellBounds;
    for(int i = 1; i < mergeRows; i++) {
      if(RowIndex + i < this.DataGridView.Rows.Count) {
        mergeCellBounds.Height +=
        DataGridView.Rows.SharedRow(RowIndex + i).Height;
      }
    }
    if(RowIndex % mergeRows == 0) {
      base.PositionEditingControl(setLocation, setSize,
      mergeCellBounds,
      mergeCellBounds, cellStyle,
      singleVerticalBorderAdded,
      singleHorizontalBorderAdded,
      isFirstDisplayedColumn,
      isFirstDisplayedRow);
    } else {
      int row = (RowIndex / mergeRows) * mergeRows;
      for(int k = row; k < RowIndex; k++) {
        mergeCellBounds.Y -=
        DataGridView.Rows.SharedRow(k).Height;
      }
      base.PositionEditingControl(setLocation, setSize,
      mergeCellBounds,
      mergeCellBounds, cellStyle,
      singleVerticalBorderAdded,
      singleHorizontalBorderAdded,
      isFirstDisplayedColumn,
      isFirstDisplayedRow);
    }
  }
}

以下のようにデータを入れてテストしています
int num = 1;
for (int i = 0; i < 9; i++) {
  dataGridView1.Rows.Add(num++, num++);
}

2列目に先に書いた「DgvMergebleTextBoxColumn」を設定し1行目を編集状態にした図です。

キャプチャするときに消えてしまっていますが、値「2」の後ろにキャレットが点滅しています(編集状態)


実際に動かすと分かると思いますが、選択したセルの値を編集することになり、問題があります。
次回はその点を改善したいと思います。

1 件のコメント:

  1. LSI設計屋の初心者プログラマ2014年3月27日 23:50

    結合例を参考にしてセル結合ができるようになりました。
    CellPaintingを使う方法に違和感があったため
    最終的ちょっと違う方法でColumn/Row両方のセル結合を実装をしてみました。
    mergeRows(+mergeColumn(追加))からセルサイズを計算して
    非セル編集表示用に
    DataGridViewCell.Paintで結合する左上のセルを大きくして、隠れてしまうセルにはPaintを実行しない。
    セル編集表示用に
    DataGridViewCell.PositionEditingControlで結合する左上のセルを大きくする。
    1セル単位で加工したかったためDgvMergebleTextBoxCellを直接入力
    DgvMergebleTextBoxCell[,] newcell = DgvMergebleTextBoxCell[2,10];
    newcell[1,0] = new DgvMergebleTextBoxCell();
    newcell[1,0].mergeRows = 3;
    dataGridView1[1.0] = newcell[1,0];
    このままでは
    セル選択時に選択範囲色が元サイズの1セル分しか変わらなかったため、
    (選択セルと選択解除セルがあった部分しか再描画されていない)
    CellPaintingを使わずにdataGridViewのCellEnter/CellLeaveイベント発生時に
    dataGridView1.InvalidateCell(0,1)とdataGridView1.InvalidateCell(0,2)を
    実行して上手くいきました。

    返信削除