よりWPFらしくあるために ー MVVMパターンに対応したセルバインディング

WPFプラットフォームで使える表計算・グリッドコントロール「SPREAD for WPF(スプレッド)」は、MVVM(Model View View-Model)パターンに対応したプログラミングが可能です。
その内容を解説した記事がCodeZineに掲載されています。

初期バージョンからMVVMに対応していますが、SPREAD for WPF 2.0Jではデータバインド機能を強化しました。そのためSPREADコントロールだけでなくセルにバインディングが可能です。この記事ではセルバインディングについて解説します。

セルのバインディング

セルのスタイルをXAMLで設定するにはCellPresenterクラスを使用します。
SPREAD for WPF 2.0J以降では、セルの値を表すValueプロパティを、WPFのバインディング機能の前提である依存関係プロパティとして提供しています。

Valueプロパティを依存関係プロパティとして提供することで、セルの値に応じてセルのスタイルを変更できるようになります。その処理はXAMLで記述するため、コードビハインドの記述を削減できます。

MVVMの実装では、コードビハインドに出来るだけ処理を記述せず、ビューとビューモデルがWPFのバインディングで対話することを要望されるケースは少なくありません。そのためセルにもバインディング機能を用意しました。その機能を、具体的に利用した例で解説します。

コンボボックスの値に応じてセルの外観を変更

以下のサンプルでは、ウィンドウにコンボボックスとSPREADが配置されています。
SPREADは、お客様から受けた問い合わせを一覧で表示し、コンボボックスには、期間を表す値が設定されています。コンボボックスで選択された値に応じて、該当するSPREADのセルを、文字を赤色に変更して強調表示します。

このアプリケーションを構成するMVVM(モデル・ビュー・ビューモデル)の各要素は以下のとおりです。
記載しているコードの主要部分を抽出しています。本記事の最後にサンプルプロジェクトのダウンロードリンクがあります。

モデル(Model)

モデルは、問合せを表すCustomerRequestクラスです。

Visual Basic

Public Class CustomerRequest
Public Property ID() As String
Public Property Title() As String
Public Property OpenDate() As DateTime
Public Property Note() As String
End Class

C#

public class CustomerRequest
{
public string ID { get; set; }
public string Title { get; set; }
public DateTime OpenDate { get; set; }
public string Note { get; set; }
}

ビューモデル(View-Model)

ビューモデルのCustomerRequestViewModelクラスは、次の2つのプロパティをもちます。

  • 問合せのコレクション(CustomerRequestsプロパティ)
  • 強調表示する期間(FocusDaysプロパティ)

Visual Basic

Public Class CustomerRequestViewModel
Implements INotifyPropertyChanged
Private _requests As ObservableCollection(Of CustomerRequest)
Public Sub New()
_requests = (New CustomerRequestCollection()).GetCustomerRequests()
End Sub
Public Property CustomerRequests() As ObservableCollection(Of CustomerRequest)
Get
Return _requests
End Get
Set
If Not _requests.Equals(Value) Then
_requests = Value
NotifyPropertyChanged("CustomerRequests")
End If
End Set
End Property
Private _days As Integer
Public Property FocusDays() As Integer
Get
Return _days
End Get
Set
If _days <> Value Then
_days = Value
NotifyPropertyChanged("FocusDays")
End If
End Set
End Property
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Private Sub NotifyPropertyChanged(propertyName As [String])
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
End Class

C#

public class CustomerRequestViewModel : INotifyPropertyChanged
{
// 問合せのコレクション
private ObservableCollection<CustomerRequest> _requests;
public CustomerRequestViewModel()
{
_requests = (new CustomerRequestCollection()).GetCustomerRequests();
}
public ObservableCollection<CustomerRequest> CustomerRequests
{
get { return _requests; }
set
{
if (_requests != value)
{
_requests = value;
NotifyPropertyChanged("CustomerRequests");
}
}
}
// 強調する期間
private int _days;
public int FocusDays
{
get { return _days; }
set
{
if (_days != value)
{
_days = value;
NotifyPropertyChanged("FocusDays");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}

また、ビューとのバインディングで使用するコンバーターを作成します。
コンバーターは、SPREADのセルと、強調表示する期間の2つの値を扱えるよう、IMultiValueConverterインタフェースを実装します。

Visual Basic

Public Class FocusDaysConverter
Implements IMultiValueConverter
Public Function Convert(values As Object(), targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IMultiValueConverter.Convert
Dim result As Boolean = False
If TypeOf values(0) Is DateTime Then
Dim [date] As DateTime = DirectCast(values(0), DateTime)
If values(1) IsNot Nothing Then
Dim days As Integer = Integer.Parse(values(1).ToString())
result = DateTime.Today.Subtract([date]).Days < days
End If
End If
Return result
End Function
Public Function ConvertBack(value As Object, targetTypes As Type(), parameter As Object, culture As CultureInfo) As Object() Implements IMultiValueConverter.ConvertBack
Throw New NotImplementedException()
End Function
End Class

C#

public class FocusDaysConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
bool result = false;
if (values[0] is DateTime)
{
// SPREADセルの日付値
DateTime date = (DateTime)values[0];
if (values[1] != null)
{
// 強調する期間に該当するか
int days = int.Parse(values[1].ToString());
result = DateTime.Today.Subtract(date).Days < days;
}
}
return result;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

ビュー(View)

ビューは、WPFのウィンドウを表すMainWindow.xamlと、そのコードビハインド(MainWindow.xaml.csまたはMainWindow.xaml.vb)です。

ウィンドウのDataContextプロパティにビューモデルを設定します。
これで、ウィンドウのコンボボックスとSPREADに、ビューモデルのプロパティをバインドできます。

<Window.DataContext>
<!--ビューモデルを設定-->
<local:CustomerRequestViewModel/>
</Window.DataContext>

コンボボックス

コンボボックスで選択された値が、強調表示する期間です。
これを実現するために、コンボボックスのSelectedValueプロパティを、ビューモデルのFocusDaysプロパティにバインドします。

<!--選択された値をビューモデルのFocusDaysプロパティにバインド-->
<ComboBox SelectedValue="{Binding FocusDays, Mode=TwoWay}" …省略…>

SPREAD

SPREADには問い合わせのコレクションを表示します。
これを実現するために、SPREADのItemsSourceプロパティを、ビューモデルのCustomerRequestsプロパティにバインドします。
また、強調表示する期間に該当するセルの文字を赤色に変更します。
これを実現するために、SPREADのCellStyleプロパティにDataTriggerを設定します。MultiBindingを使用して、セルの値と、ビューモデルのFocusDaysプロパティをバインドします。

<sg:GcSpreadGrid ItemsSource="{Binding CustomerRequests}" …省略…>
<!--セルのバインディングを設定-->
<sg:GcSpreadGrid.CellStyle>
<Style TargetType="{x:Type sg:CellPresenter}">
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource FocusDaysConverter}">
<!--セルの値-->
<Binding RelativeSource="{RelativeSource Self}" Path="Value"/>
<!--ビューモデルのFocusDaysプロパティ-->
<Binding Path="FocusDays"/>
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="FontSize" Value="13"/>
<Setter Property="Foreground" Value="Red"/>
</DataTrigger>
</Style.Triggers>
</Style>
</sg:GcSpreadGrid.CellStyle>
<sg:GcSpreadGrid.Columns …以降、省略…
</sg:GcSpreadGrid>

実行

アプリケーションを実行すると、コンボボックスの値に応じて、対象のセルの文字が赤色に変化します。

セルバインディング実行結果

サンプルプロジェクト

本記事のサンプルプロジェクトは以下からダウンロードできます。Zip形式(27KB)

業務アプリケーションのアーキテクチャ

実践的な業務アプリケーションのアーキテクチャを解説した記事がCodeZineに記載されています。
実際の開発現場で実践的なMVVMパターンの開発の解説です。こちらの記事中のサンプルでSPREAD for WPFが利用されています。

SPREAD for WPFの機能を体感

.NET FrameworkのClick Once機能を利用したデモを公開しています。
Windows PCにWebサイトからインストールして、SPREAD for WPFの機能を実際にお試しいただけます。