GcSpreadGridがセル結合を含んでいると、コピー&ペーストの実行時に不正に非表示の行列を処理の対象に含める

文書番号 : 86185     文書種別 : 不具合     登録日 : 2020/11/12     最終更新日 : 2020/11/12
文書を印刷する
対象製品
SPREAD for WPF 3.0J
状況
回避方法あり
詳細
GcSpreadGridが行結合や列結合などのセル結合を含んでいると、非表示の行列をまたいでセル範囲を選択しコピー&ペーストを実行した場合に、不正に非表示の行列を処理の対象に含めてしまいます。

例えば、「回避方法」に記載のサンプルコードで、RowSpan/ColumnSpanプロパティによるセル結合を有効にすると、非表示行のセルに値がペーストされます。本来はペーストされない動作が正しい動作となります。
  1. 15、16行目を非表示にします(Button_Clickイベント)。
  2. A列の14~17行目を範囲選択してCtrl+Cでコピーします。
  3. B列の14行目のセルを選択してCtrl+Vで貼り付けします。
  4. 15、16行目を表示します(Button_Clickイベント)。
  5. 非表示にしていた行のセルに値がペーストされます。
回避方法
コピー&ペーストを行う際に一時的にセルの結合を解除することで回避します。このとき、セル結合の状態を保存しておくために、独自に作成したSetRowSpan/SetColumnSpan関数により結合を行います。

◎サンプルコード(C#)
public partial class MainWindow : Window
{
  public MainWindow()
  {
    InitializeComponent();

    gcSpreadGrid1[13, 0].Text = "A14";
    gcSpreadGrid1[14, 0].Text = "A15";
    gcSpreadGrid1[15, 0].Text = "A16";
    gcSpreadGrid1[16, 0].Text = "A17";

    // RowSpan/ColumnSpanプロパティによるセル結合は使用しません。
    //gcSpreadGrid1.Cells[0, 0].RowSpan = 10;
    //gcSpreadGrid1.Cells[0, 0].ColumnSpan = 4;

    // 独自に用意したSetRowSpan/SetColumnSpan関数により結合を行うことで回避します。
    SetRowSpan(0, 0, 10);
    SetColumnSpan(0, 0, 4);

    gcSpreadGrid1.AddHandler(CommandManager.ExecutedEvent,
      new ExecutedRoutedEventHandler(Spread_Executed), true);
    gcSpreadGrid1.AddHandler(CommandManager.PreviewExecutedEvent,
      new ExecutedRoutedEventHandler(Spread_PreviewExecuted), true);
  }

  private void Button_Click(object sender, RoutedEventArgs e)
  {
    gcSpreadGrid1.Rows[14].IsVisible = !gcSpreadGrid1.Rows[14].IsVisible;
    gcSpreadGrid1.Rows[15].IsVisible = !gcSpreadGrid1.Rows[15].IsVisible;
  }

  private readonly Dictionary _rowSpans = new Dictionary();
  private readonly Dictionary _columnSpans = new Dictionary();

  private void SetRowSpan(int row, int column, int value)
  {
    var key = new Int32Point { Row = row, Column = column };
    if (value == 1 && _rowSpans.ContainsKey(key))
    {
      _rowSpans.Remove(key);
    }
    else
    {
      _rowSpans[key] = value;
    }
    gcSpreadGrid1.Cells[row, column].RowSpan = value;
  }

  private void SetColumnSpan(int row, int column, int value)
  {
    var key = new Int32Point { Row = row, Column = column };
    if (value == 1 && _columnSpans.ContainsKey(key))
    {
      _columnSpans.Remove(key);
    }
    else
    {
      _columnSpans[key] = value;
    }
    gcSpreadGrid1.Cells[row, column].ColumnSpan = value;
  }

  private void Spread_Executed(object sender, ExecutedRoutedEventArgs e)
  {
    if (e.Command != ApplicationCommands.Copy &&
      e.Command != ApplicationCommands.Paste)
    {
      return;
    }

    RecoverCellSpans();
  }

  private void RecoverCellSpans()
  {
    foreach (var item in _rowSpans)
    {
      var key = item.Key;
      gcSpreadGrid1.Cells[key.Row, key.Column].RowSpan = item.Value;
    }
    foreach (var item in _columnSpans)
    {
      var key = item.Key;
      gcSpreadGrid1.Cells[key.Row, key.Column].ColumnSpan = item.Value;
    }
  }

  private void Spread_PreviewExecuted(object sender, ExecutedRoutedEventArgs e)
  {
    if (e.Command != ApplicationCommands.Copy &&
      e.Command != ApplicationCommands.Paste)
    {
      return;
    }

    RemoveCellSpans();
  }

  private void RemoveCellSpans()
  {
    foreach (var item in _rowSpans)
    {
      var key = item.Key;
      gcSpreadGrid1.Cells[key.Row, key.Column].RowSpan = 1;
    }
    foreach (var item in _columnSpans)
    {
      var key = item.Key;
      gcSpreadGrid1.Cells[key.Row, key.Column].ColumnSpan = 1;
    }
  }
}

internal struct Int32Point : IEquatable
{
  public int Row, Column;

  public bool Equals(Int32Point other)
  {
    return Row == other.Row && Column == other.Column;
  }

  public override int GetHashCode()
  {
    return Row + (Column << 20);
  }

  public override bool Equals(object obj)
  {
    if (obj is Int32Point)
    {
      return Equals((Int32Point)obj);
    }
    return false;
  }
}
キーワード
GcSpreadGrid