FlexGrid for WPF
カスタムセル
ミュージックライブラリサンプル > カスタムセル

これで、アプリケーションで最も注目する部分を作成する準備ができました。以下の要素を表示するためのカスタムセルを作成する ICellFactory オブジェクトを作成します。

これらすべての作業は、ICellFactory インターフェイスを実装するカスタム MusicCellFactory クラスによって実行されます。カスタムセルファクトリは、それをグリッドの CellFactory プロパティに割り当てるだけで使用できます。

コードのコピー
_flexiTunes.CellFactory = new MusicCellFactory()

MusicCellFactory クラスはデフォルトの CellFactory クラスから継承され、CreateCellContent メソッドをオーバーライドすることにより、各セルのコンテンツを表示するために使用する要素を作成します。

CreateCellContent メソッドは、パラメータとして、親グリッド、基本クラスによって作成されてセルの背景と境界を提供する Border 要素、および作成する必要があるセル(単一のセル、またはマージされている場合は複数のセル)を指定する CellRange オブジェクトを受け取ります。

CreateCellContent は、セルコンテンツのみを作成します。境界と背景も含めてセル全体を作成する場合は、代わりに CreateCell メソッドをオーバーライドします。

この例では、CreateCellContent メソッドは、通常のセルとグループ行内のセルという 2 つの主要なケースを処理します。

通常のセルの処理は簡単です。メソッドは、作成される列に基づいて新しい SongCell 要素または RatingCell 要素を返すだけです。これらは StackPanel から派生するカスタム要素で、セルが表すデータ項目に連結される画像とテキストを含みます。

グループ行のセルは、多少複雑です。このアプリケーションでは、グループ行はアーティストとアルバムを表すために使用されます。これらの項目は、ソースコレクションのデータ項目に対応していません。ここで使用する CreateCellContent メソッドは、フェイクの Song オブジェクトを作成してアーティストとアルバムを表します。このフェイクの Song オブジェクトには、グループに含まれる曲に基づいて計算されたプロパティが含まれます。アルバムの Duration は、アルバムの各曲の Duration の合計として計算され、Rating は、アルバムの各曲の Rating の平均として計算されます。このフェイクの Song オブジェクトが作成されると、SongCell 要素と RatingCell 要素を通常どおりに使用できます。

次に、MusicCellFactory クラスとその CreateCellContent の実装を多少簡略化して示します。

コードのコピー
// ミュージックライブラリ セルを作成するために使用されるセルファクトリー。
public class MusicCellFactory : CellFactory
{
  static Thickness _emptyThickness = new Thickness(0);
  public List<RatingCell> _ratings = new List<RatingCell>();
  // セルを範囲に連結します。
  public override void CreateCellContent(
         C1FlexGrid grid, Border bdr, CellRange range)
  {
    // 行・列を取得します。
    var row = grid.Rows[range.Row];
    var col = grid.Columns[range.Column];
    var gr = row as GroupRow;
    // グループ行に罫線を表示しません。
    if (gr != null)
    {
      bdr.BorderThickness = _emptyThickness;
    }
    // ツリーのセルを連結します。
    if (gr != null && range.Column == 0)
    {
    BindGroupRowCell(grid, bdr, range);
    return;
    }
    // 標準のデータ行のセルを連結します。
    var colName = col.ColumnName;
    if (colName == "Name")
    {
      bdr.Child = new SongCell(row);
      return;
    }
    if (colName == "Rating")
    {
      var song = row.DataItem as Song;
      if (song != null)
      {
       // このセルを表すレートコントロールを作成します。
       // 注:
       // - データコンテキストとして罫線要素を使用します。
       // - 連結するために列を使用します。
       var cell = new RatingCell();
       cell.SetBinding(RatingCell.RatingProperty, col.Binding);
       bdr.Child = Cell;
       return;
      }
    }
    // デフォルトの連結
    base.CreateCellContent(grid, bdr, range);
  }

BindGroupRowCell メソッドは、前述のフェイクの Song オブジェクトを作成し、それらを行の DataItem プロパティに割り当てて CreateCellContent メソッドで使用できるようにし、グループ行の最初のセルに特別な処理を行います。各グループ行の最初のセルは他と異なり、通常のセルのコンテンツに加えてグループの折りたたみ/展開のボタンが含まれています。

次に、各グループ行の最初の項目を処理するコードを示します。

コードのコピー
// グループ行にセルを連結します。
void BindGroupRowCell(C1FlexGrid grid, Border bdr, CellRange range)
{
  // 行とグループ行を取得します。
  var row = grid.Rows[range.Row];
  var gr = row as GroupRow;
  // グループのキャプション・画像を最初列に表示します。
  if (range.Column == 0)
  {
    // 必要であれば、カスタムデータアイテムを作成します。
    if (gr.DataItem == null)
    {
      gr.DataItem = BuildGroupDataItem(gr);
    }
    // 必要なセル型を取得します。
    Type cellType = gr.Level == 0 ? typeof(ArtistCell) : typeof(AlbumCell);
    // セルを作成します。
    bdr.Child = gr.Level == 0 
      ? (ImageCell)new ArtistCell(row) 
      : (ImageCell)new AlbumCell(row);
  }
}

最後に、アーティストとアルバムを表す Song オブジェクトを作成するコードを示します。このメソッドでは、GroupRow.GetDataItems メソッドを使用して、グループに含まれるすべてのデータ項目のリストを取得します。次に、LINQ ステートメントを使用して、グループ内の曲の合計サイズ、再生時間、およびレーティングの平均を計算します。

コードのコピー
// グループを示す曲を作成します。
// GetChildDataItems メソッドはこのノードに付属するすべての曲を返します。
// 以下の LINQ ステートメントは、アルバム・アーティスト別にの合計のサイズ、長さおよび
// 平均レートを計算します。
Song BuildGroupDataItem(GroupRow gr)
{
  var gs = gr.GetDataItems().OfType<Song>();
  return new Song()
  {
    Name = gr.Group.Name.ToString(),
    Size = (long)gs.Sum(s => s.Size),
    Duration = (long)gs.Sum(s => s.Duration),
    Rating = (int)(gs.Average(s => s.Rating) + 0.5)
  };
}
コードのコピー
// グループを示す曲を作成します。

// GetChildDataItems メソッドはこのノードに付属するすべての曲を返します。

// 以下の LINQ ステートメントは、アルバム・アーティスト別にの合計のサイズ、長さおよび

// 平均レートを計算します。

Song BuildGroupDataItem(GroupRow gr)

{

  var gs = gr.GetDataItems().OfType<Song>();

  return new Song()

  {

    Name = gr.Group.Name.ToString(),

    Size = (long)gs.Sum(s => s.Size),

    Duration = (long)gs.Sum(s => s.Duration),

    Rating = (int)(gs.Average(s => s.Rating) + 0.5)

  };

}

これが必要な作業の大部分です。残っている部分は、個々のセルを表すために使用するカスタム要素の定義だけです。次の要素があります。

次の図に、グリッドにどのように要素が表示されるかを示します。

これらはすべて通常のWPF Framework の要素で、Microsoft Blend またはコードで作成できます。

次に、RatingCell 要素を実装するコードを示します。他の要素も同様で、サンプルソースコードを確認すれば実装の詳細がわかります。

コードのコピー
///
/// 星付き画像として表示されるレートを示すセル。
///
public class RatingCell : StackPanel
{
  static ImageSource _star;
  const int MAXRATING = 5;
  const double OFF = 0.2;
  const double ON = 1.0;
  ///
  /// <see cref="ItemsSource"/> 依存プロパティを示します。
  ///
  public static readonly DependencyProperty RatingProperty =
      DependencyProperty.Register(
        "Rating",
          typeof(int),
          typeof(RatingCell),
          new PropertyMetadata(0, OnRatingChanged));
  public RatingCell()
  {
    if (_star == null)
    {
      _star = ImageCell.GetImageSource("star.png");
    }
    Orientation = Orientation.Horizontal;
    for (int i = 0; i < 5; i++)
    {
     var img = GetStarImage();
     img.Opacity = OFF;
     img.MouseLeftButtonDown += img_MouseLeftButtonDown;
     Children.Add(img);
    }
  }
  public int Rating
  {
      get { return (int)GetValue(RatingProperty); }
      set { SetValue(RatingProperty, value); }
   }
   void img_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
   {
      // 星レーティングのインデックスに基づいたレートを計算します。
      Image img = sender as Image;
      RatingCell cell = img.Parent as RatingCell;
      int index = cell.Children.IndexOf(img);
      if (index > 0 || e.GetPosition(img).X > img.Width / 3)
      {
           index++;
      }
      
      // 新しいレートを適用します。
      cell.Rating = index;
      Animate(img);
  }
  static Image GetStarImage()
  {
    var img = new Image();
    img.Source = _star;
    img.Width = img.Height = 17;
    img.Stretch = Stretch.None;
    return img;
  }
}
コードのコピー
///

/// 星付き画像として表示されるレートを示すセル。

///

public class RatingCell : StackPanel

{

  static ImageSource _star;

  const int MAXRATING = 5;

  const double OFF = 0.2;

  const double ON = 1.0;

 

  // 

  // 依存プロパティを示します。

  // 

  public static readonly DependencyProperty RatingProperty =

     DependencyProperty.Register(

          "Rating",

           typeof(int),

           typeof(RatingCell),

           new PropertyMetadata(0, OnRatingChanged));

 

  public RatingCell()

  {

    if (_star == null)

    {

      _star = ImageCell.GetImageSource("star.png");

    }

    Orientation = Orientation.Horizontal;

    for (int i = 0; i < 5; i++)

    {

      var img = GetStarImage();

      img.Opacity = OFF;

      img.MouseLeftButtonDown += img_MouseLeftButtonDown;

      Children.Add(img);

    }

  }

  static Image GetStarImage()

  {

    var img = new Image();

    img.Source = _star;

    img.Width = img.Height = 17;

    img.Stretch = Stretch.None;

    return img;

  }

}

RatingCell 要素は簡単です。これは、いくつかの Image 要素を含む StackPanel で構成されます。Image 要素の数は、表示するレーティング(コンストラクタに渡される値)によって定義されます。各 Image 要素によって 1 つの星アイコンが表示されます。これは、レーティングが変化しないことを前提とした静的な処理なので、動的な連結は必要ありません。

関連トピック