GrapeCity.devlog

グレープシティ株式会社のDeveloper Tools〈開発支援ツール〉の最新情報をお届けします。製品のTIPSや発売情報、イベントのお知らせなどをいち早く発信中です。

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

最強の表計算・グリッドコントロールとして多くの開発現場で採用されてきたSPREAD(スプレッド)。この記事ではWPFプラットフォームに対応した「SPREAD for WPF 2.0J」の機能を紹介します。

MVVMパターン対応

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の機能を実際にお試しいただけます。