DataGridの行の背景色を行データによって変えたいのよ

SilverlightのDataGridは主にItemsSourceにデータをバインドして使うと思うけど
今日はそのデータによって行の背景色を変えてみましょうという記事です。

よくあるケースで考えられるのは
「データが変更されている場合はピンク色で」とか、
「ユーザーが(エラーじゃ無いけど)斜め上な値を入力したら警告の意味を含めて赤色で」
とか。

このやり方、いろいろGoogle先生に聞いてみたんだけど、
DataGrid.LoadingRowイベントのハンドラで

e.Row.BackGround = 〜〜

みたいにするのが多いみたい。
でも、これだと行が描画されるときにしか色が変わらない。
そうじゃなくて、「編集された時点でピンクになる」とか、リアルタイムで変わって欲しいときのサンプルが無いようだったので書きます。
「状態によって見た目が変化」なので、VisualStateを使ってみようと思います。

※ Expression Blendが必須です。(Silverlight開発には必須ですよ〜)

例えば、こんなDataGridがあったとします。

こいつはWCF RIA Servicesを使って「データソース」ウインドウからポトペタでページに貼っ付けただけのものです。
簡単ですねぇ。
せっかくEntityが行にバインドされているので、こいつのEntityState(変更されたかどうか)によって色が変わるようにしてみましょう。

いざ。

1.RowStyleの編集からテンプレートを開く

Expression Blendで該当データグリッドを右クリック → 「追加テンプレートの編集」 → 「RowStyleの編集」
と選択していき、DataGridのRowStyleを編集します。

「オブジェクトとタイムライン」ウインドウには、こんな感じでRowのテンプレートが丸裸にされます。

2.まずはCellsPresenterのBackGroundをTransParentに設定

テンプレートの中の「CellsPresenter」というコントロールがあります。
こいつはその名の通り行中のCell達を表示するコンテナです。
こいつのBackgroundを弄ることによって、行の背景色を変化させることにします。
行データのEntityStateが変更されたときだけ色をつけたいので、基本はTransParentとしておきます。

3.新しいVisualStateGroup、VisualStateを作る

左上の「状態」タブで、新しいVisualStateGroupを作成します。

既に「MouseOver」とか色々状態はありますが、今回はEntityの状態によって変化させるので専用の状態グループと状態を作ります。

グループ名は「EntityStates」
状態は「Modified」と「Unmodified」にしてみました。

4.状態「Modified」を記録する

さっき作った状態「Modified」の記録モードをONにして、CellsPresenterの背景色を変更します。

今回はピンクにしてみました。
Opacityを100%にしちゃうと行が選択された時とかわかりにくくなるので微妙に薄めておきます。(70%にしてます)

また、「Unmodified」の方は何も記録しません。(通常行なので当たり前ですが)

5.GoToStateActionを仕込んでできあがり

ここまでで状態の設定は終わりました。
しかし、一体誰がこの状態に持って行けば良いでしょう?

そこでGoToStateActionの登場です。

「アセット」タブから、GoToStateActionを選択し、テンプレートの「Root」直下に配置します。

配置したら、右側のプロパティペインでGoToStateActionの設定をします。
まずは「Modified」の設定。

ポイントは、

  • TriggerActionは既定でEventTriggerに結びつくけど、今回はイベントでは無くデータで変化するのでDataTriggerに換える。
  • Bindingはカスタム式でEntityStateを指定(行にバインドしているEntity.EntityStateによって背景色を変化させるため)
  • StateNameは上記で作った「Modified」を指定

また、同じように「Unmodified」も設定します。

さあ、出来上がりです。
プロジェクトを実行すると・・・


名前を編集して確定すると色が・・・

変わったーーー!!!!
さあっし。

ちなみに、今回はWCF RIA Servicesを使用しているので、ViewModelとかで

personDomainDataSource.RejectChanges();

とかやるとちゃんと元の色に戻ってくれます。

まとめ

今回紹介したやり方だと、1行もC#コードを書かずにデータによってDataGridの行の背景色を変化させることが可能だということがわかります。
Expression Blendすばらしい・・・。
私はもうBlend無しにSilverlight開発はできなくなってます・・・。

ちなみに、今回の作業でBlendが吐き出したXamlの重要な部分を見ておくと、

<Style x:Key="DataGridRowStyle1" TargetType="sdk:DataGridRow">
  <Setter Property="IsTabStop" Value="False"/>
  <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="sdk:DataGridRow">
          <sdk:DataGridFrozenGrid x:Name="Root">
          
              <VisualStateManager.VisualStateGroups>
                <VisualStateGroup x:Name="EntityStates">
                  <VisualState x:Name="Modified">
                      <Storyboard>
                        <ColorAnimation Duration="0" To="Pink" Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)" Storyboard.TargetName="CellsPresenter" d:IsOptimized="True"/>
                        <DoubleAnimation Duration="0" To="0.7" Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="CellsPresenter" d:IsOptimized="True"/>
                      </Storyboard>
                  </VisualState>
                  <VisualState x:Name="Unmodified"/>
                </VisualStateGroup>
              </VisualStateManager.VisualStateGroups>
              
              <i:Interaction.Triggers>
                <ei:DataTrigger Binding="{Binding EntityState}" Value="Modified">
                  <ei:GoToStateAction StateName="Modified"/>
                </ei:DataTrigger>
                <ei:DataTrigger Binding="{Binding EntityState}" Value="Unmodified">
                  <ei:GoToStateAction StateName="Unmodified"/>
                </ei:DataTrigger>
              </i:Interaction.Triggers>

という風になってます。
状態毎に背景を変えて、
その状態を呼び出すDataTriggerとActionがあって・・・
Xamlだとすごくわかりやすいですね。

ということで、ビバ! Blend!