ComponentOne Information

ComponentOne Studio/Wijmo/Xuniの最新情報を公開中

WPFアプリの「条件付き書式」をBindingでより簡潔に実現する

業務アプリケーションにおいて、とくにグリッドによるデータの表示・編集処理にて、「入力値がマイナスのときは赤色で表示したい」「ある一定量を超えるデータを太字で目立たせたい」といったように、ある特定の条件によってデータの表示書式を変えたいという要件は珍しくありません。

C1FlexGridコントロールを例に挙げると、WinForms版では、オーナー描画機能を使用して実現する必要がありました。これは、コントロールによる既定の描画処理の代わりに、OwnerDrawCellイベントを介して独自の描画処理を実行することによってセルの内容をレンダリングする機能で、描画内容をかなり自由にカスタマイズできる半面、描画位置の調整や既定のテーマ配色との統合などを考慮しなければならず、高度なコーディングが要求されるため、決して簡単に実装できるとは言えませんでした。

また一般的に、WPFプラットフォームにおいて条件付き書式を実現するにあたっては、IValueConverterを継承したカスタムコンバーターを作成し、Bindingと併用することで実装することが多いかと思います。WPF版のC1FlexGridコントロールでも、同様の実装方法にてセルに対する条件付き書式を実現することができます。製品に付属する「ConditionalFormatting」サンプルにて、条件付き書式の実装例を示しておりますので、詳しくはご参照ください。

WinForms版の頃に比べれば、これでも十分簡単に実装できるようになりましたが、カスタムコンバーターを定義しなければならないなど、まだ一手間かかる印象があります。カスタムコンバーターを別途用意することなく、バインディングの定義のみで実現できるような、もっと簡単な実装方法はないでしょうか?

Binding Expressions for WPF

ComponentOne Studio for WPFには、「Binding Expressions for WPF」(以下、C1Binding)が収録されています。System.Windows.Data.BindingExpressionクラスと名前が似ていますが、その目的は大きく異なります。

C1Bindingは、バインディングステートメント内へのインライン構文記述を可能にするコンポーネントです。複雑な分離コンバーターを用意することなく、ステートメント内で各種関数を実行したり、式を計算することができます。また、直接 if/else ロジックを適用することもできます。

C1Bindingは、任意のコントロールと組み合わせてバインディングステートメントを簡略化することを目的としています。もちろんC1FlexGridコントロールとの連携も可能です。つまり、C1FlexGridC1Bindingを組み合わせて使用することで、さらに簡単に条件付き書式を実現することが可能となります。

C1Bindingの実装例

「ConditionalFormatting」サンプルを基に、C1Bindingのインライン構文を使用した条件付き書式の実装例を紹介します。

1.C1.WPF.Binding.4.dllアセンブリの参照

ソリューションエクスプローラ上の「参照設定」を右クリックし、コンテキストメニューから「参照の追加」を選択します。「参照マネージャー」にて[アセンブリ]-[拡張]を選択し、一覧から「C1.WPF.Binding.4.dll」を選択しOKをクリックします。
f:id:ComponentOne_JP:20170925163058p:plain

2.Weight2列の追加

カスタムコンバーターを使用した場合の結果と比較するため、Weight列の次にWeight2列を追加します。MainWindow.xaml上のWeight列の定義部分をコピー&貼り付けし、Headerプロパティを加えて、Weight2列を追加します。

<c1:Column Binding="{Binding Weight}">
  <c1:Column.CellTemplate>
    <DataTemplate>
      <TextBloc
        Text="{Binding Weight, StringFormat=n0}"
        HorizontalAlignment="Right"
        Foreground="{Binding Weight,
          Converter={StaticResource ForegroundConverter},
          ConverterParameter={StaticResource WeightRange} }"
        FontWeight="{Binding Weight,
          Converter={StaticResource FontWeightConverter},
          ConverterParameter={StaticResource WeightRange} }"
      />
    </DataTemplate>
  </c1:Column.CellTemplate>
</c1:Column>

<c1:Column Binding="{Binding Weight}"  Header="Weight2">
  <c1:Column.CellTemplate>
    <DataTemplate>
      <TextBlock
        Text="{Binding Weight, StringFormat=n0}"
        HorizontalAlignment="Right"
        Foreground="{Binding Weight,
          Converter={StaticResource ForegroundConverter},
          ConverterParameter={StaticResource WeightRange} }"
        FontWeight="{Binding Weight,
          Converter={StaticResource FontWeightConverter},
          ConverterParameter={StaticResource WeightRange} }"
      />
    </DataTemplate>
  </c1:Column.CellTemplate>
</c1:Column>
3.Foregroundプロパティの設定内容を変更

Foregroundプロパティの定義部分を以下のコードに置き換えます。

Foreground="{c1:C1Binding
  Expression='if(Weight &lt; 200, |red|, if(Weight &gt; 1000, |green|, |black|))'}"

Bindingステートメントの代わりにC1Bindingを使用し、そのExpressionプロパティにインライン構文を記述するのがポイントです。

このインライン構文では、if関数を使用し、Weightフィールドの値が200より小さければ赤色、1000より大きければ緑色、それ以外は黒色を前景色として設定することを示しています。

C1Bindingでは&quot;とパイプ文字(|)の2つの形式のインライン引用符をサポートしており、「|green|」は「'green'」と同義となります。これにより、XAMLを崩すことなく連結式で引用符を使用できます。

なお、リテラル内には不等号(<、>)を直接記述することができないため、それぞれ&lt;&gt;として記述しています。

4.FontWeightプロパティの設定内容を変更

同様に、FontWeightプロパティの定義部分を以下のコードに置き換えます。

FontWeight="{c1:C1Binding
  Expression='if(OR(Weight &lt; 200, Weight &gt; 1000), |bold|, |normal|)'}"

このインライン構文では、if関数とOR関数を使用し、Weightフィールドの値が200より小さいか1000より大きければ太字、それ以外は標準のフォントスタイルを設定することを示しています。

ここまで完了すると、Weight2列の定義は以下のようになるはずです。

<c1:Column Binding="{Binding Weight}"  Header="Weight2">
  <c1:Column.CellTemplate>
    <DataTemplate>
      <TextBlock
        Text="{Binding Weight, StringFormat=n0}"
        HorizontalAlignment="Right"
        Foreground="{c1:C1Binding
          Expression='if(Weight &lt; 200, |red|, if(Weight &gt; 1000, |green|, |black|))'}"
        FontWeight="{c1:C1Binding
          Expression='if(OR(Weight &lt; 200, Weight &gt; 1000), |bold|, |normal|)'}" />
    </DataTemplate>
  </c1:Column.CellTemplate>
</c1:Column>

Bindingによる定義とは異なり、Converterプロパティの設定はありません。また、Expressionプロパティにインラインで構文が記述されていることで、ここでどういった処理が実行されるかかXamlコード上で理解しやすくなっているかと思います。また、わずかではありますが、XAMLコード自体も短くなりました。

実行してみる

ここまで完了したら、デバッグ実行して結果を確認してみましょう。設定等に誤りがなければ、添付画像のように、Weight列とWeight2列が同じ条件に基づいて書式設定されていることが確認できます。
f:id:ComponentOne_JP:20170925165141p:plain

まとめ

C1Bindingのインライン構文を活用することで、条件付き書式の実装がさらに簡単に行えることがご理解いただけたかと思います。

今回はC1FlexGridを例に挙げましたが、当然ながら利用範囲はそれに限定されるものではありません。標準あるいはサードパーティ製コンポーネントをはじめ、バインディングをサポートするあらゆる要素と組み合わせてC1Bindingをご利用いただくことが可能です。

今回紹介した以外にも、数学関数、統計関数、テキスト関数などさまざまな関数がサポートされているので、書式の変更に留まらず、データを集計あるいは加工して表示したいような場合にも威力を発揮します。そして何より、カスタムコンバーターを定義することなく、インライン構文のみで処理できるということは、開発時間を短縮し、XAMLコードを簡素化できる大きなメリットにもなります。

この機会に、Binding Expressions for WPFの利用をご検討いただければ幸いです。製品に関する詳細につきましては、Webサイトの製品情報をご覧ください。
Binding Expressions for WPF - ComponentOne Studio for WPF | グレープシティ コンポーネント製品

また、記事で使用した「ConditionalFormatting」サンプルおよびコードは、トライアル版からもお試しいただけます。トライアル版は下記リンク先からダウンロードできます。
ダウンロード - ComponentOne | グレープシティ コンポーネント製品

ComponentOne