Adsense_top

2009年6月21日日曜日

DataGridView セルの結合 2-5 コードまとめ

DataGridView セルの結合のここまでのソースコードを一旦まとめました。
問題点が多々あります。問題点を検討して書こうかと思いましたが、コードが長くなりましたので、今日はここまでのコードだけ載せておきます。
public class DgvMergebleTextBoxColumn {
  // 結合行数
  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);
    }
  }
  public override void InitializeEditingControl(
  int rowIndex,
  object initialFormattedValue,
  DataGridViewCellStyle dataGridViewCellStyle) {
    base.InitializeEditingControl(rowIndex,
    initialFormattedValue,
    dataGridViewCellStyle);
    DataGridViewTextBoxEditingControl ctl =
    this.DataGridView.EditingControl
    as DataGridViewTextBoxEditingControl;
    // 結合された列の上端の行インデックスを算出しセルを取得
    int row = (rowIndex / mergeRows) * mergeRows;
    DataGridViewCell cell =
    this.DataGridView[ColumnIndex, row];
    // 初期化した編集コントロールに、
    // 結合された列の上端セルの値を代入
    ctl.Text = Convert.ToString(cell.Value);
  }
  protected override object GetValue(int rowIndex) {
    if(rowIndex % mergeRows == 0) {
      // 結合された列の上端行のセルの場合のみ値を返す
      return base.GetValue(rowIndex);
    } else {
      // 結合された列の上端行のセル以外の場合は、null値を返す
      return null;
    }
  }
  protected override bool SetValue(int rowIndex, object value) {
    if(this.DataGridView == null || rowIndex % mergeRows == 0) {
      // DataGridViewにまだ追加されていないセルの場合と
      // 結合された列の上端行セルの場合は通常通り値を設定します。
      return base.SetValue(rowIndex, value);
    } else {
      try {
        // 結合された列の上端行のインデックスを計算
        int topRowIndex = (rowIndex / mergeRows) * mergeRows;
        // DataGridViewにすでに追加されていて、結合された列の
        // 上端行以外のセルの場合、結合された列の上端行のセルに
        // 値を設定します。
        this.DataGridView[this.ColumnIndex,
        TopRowIndex(rowIndex)].Value = value;
        return true;
      } catch(Exception ex) {
        // 何らかのエラーが発生した場合は、falseを返します。
        return false;
      }
    }
  }
  protected override void Paint(Graphics graphics,
  Rectangle clipBounds,
  Rectangle cellBounds,
  int rowIndex,
  DataGridViewElementStates cellState,
  object value, object formattedValue,
  string errorText,
  DataGridViewCellStyle cellStyle,
  DataGridViewAdvancedBorderStyle advancedBorderStyle,
  DataGridViewPaintParts paintParts) {
    Rectangle rect = cellBounds;
    int topRowIndex = TopRowIndex(rowIndex);
    bool isPrint = true;
    object topFormattedValue = null;
    object topValue = null;
    bool cellSelected = false;
    for(int i = 0; i < mergeRows; i++) {
      // 端数行がある場合、マージ行数より少ない行でも表示を
      // 行うために結合行数以下で切り上げる
      if(topRowIndex + i >= this.DataGridView.Rows.Count) {
        break;
      }
      DataGridViewCell cell =
      this.DataGridView[ColumnIndex, topRowIndex + i];
      if(i == 0) {
        // 結合行の最上端セルの値を取得
        topFormattedValue = cell.FormattedValue;
        topValue = cell.Value;
      }
      if(!cell.Equals(this) && !cell.OwningRow.IsNewRow) {
        rect.Height += cell.OwningRow.Height;
      }
      // 描画矩形のY座標を、結合行の最上端セルの座標にあわせる
      if(i < (rowIndex % mergeRows))
        rect.Y -= cell.OwningRow.Height;
      // 結合されたセルのグループ内に選択されたセルがあるか確認
      if((cell.State & DataGridViewElementStates.Selected) != 0)
        cellSelected = true;
    }
    // グループ内に選択されたセルがあった場合は、
    // UIの状態を選択状態にする。
    if(cellSelected)
      cellState |= DataGridViewElementStates.Selected;
    // マージしていることになっている他のセルも
    // 書き直す必要があるためDataGridViewのGraphicsを取得
    Graphics g = this.DataGridView.CreateGraphics();
    // Graphics オブジェクト、描画矩形を設定して描画する。
    base.Paint(g, rect, rect, rowIndex, cellState, topValue,
    topFormattedValue, errorText, cellStyle,
    advancedBorderStyle, paintParts);
  }
  protected override void OnKeyDown(KeyEventArgs e, int rowIndex) {
    // カレントセルにする行Index
    int curIndex = -1;
    // 列ヘッダの場合は、基底クラスに任す
    if(rowIndex >= 0) {
      // ↑キー
      if(e.KeyCode == Keys.Up) {
        if(e.Control) {
          // Ctrlキーが押下されていた場合は
          // 最初の行をカレントセルに設定
          curIndex = 0;
        } else {
          if(TopRowIndex(rowIndex) > 0) {
            // 一つ上の結合セルのグループの上端の行を
            // カレントセルのIndexに設定
            curIndex =
            TopRowIndex(TopRowIndex(rowIndex) - mergeRows);
          } else {
            // 自身が一番上の結合グループのセルの場合は、
            // 自身の結合グループの上端の行(Index=0)を
            // カレントセルのIndexに設定
            curIndex = 0;
          }
        }
        // カレントセルを設定
        this.DataGridView.CurrentCell =
        this.DataGridView[ColumnIndex, curIndex];
        e.SuppressKeyPress = true;
      }
        // ↓キー
      else if(e.KeyCode == Keys.Down) {
        if(e.Control) {
          // Ctrlキーが押下されていた場合は最後の行を
          // カレントセルのIndexに設定
          curIndex =
          TopRowIndex(this.DataGridView.RowCount - 1);
        } else {
          if(TopRowIndex(rowIndex) + mergeRows
          <= this.DataGridView.RowCount) {
            // 自身のセルの属する結合グループより下に、
            // 行が存在する場合、自身のグループの下端の
            // 次の行をカレントセルのIndexに設定
            curIndex =
            TopRowIndex(TopRowIndex(rowIndex) + mergeRows);
          } else {
            // 自身のセルの属する結合グループより下に、
            // 行がない場合は、自身のグループの中の下端の行を
            // カレントセルのIndexに設定。
            // MergeRow分の行がない可能性もあるので、
            // DataGridViewの下端のIndexを設定している。
            curIndex =
            TopRowIndex(this.DataGridView.RowCount - 1);
          }
        }
        // カレントセルを設定
        this.DataGridView.CurrentCell
        = this.DataGridView[ColumnIndex, curIndex];
        e.SuppressKeyPress = true;
      }
    } else {
      // ↑↓キー以外は通常の処理へ
      base.OnKeyDown(e, rowIndex);
    }
  }
  // 現在のセルが所属する結合セルのグループの
  // 上端のセルのインデックスを返します。
  private int TopRowIndex(int rowIndex) {
    return (rowIndex / mergeRows) * mergeRows;
  }
}

次回は、スクロール時の描画の問題点をはじめ多々ある問題点の洗い出しなどをしたいと思っています。

0 件のコメント:

コメントを投稿