2017年9月7日
WPF
技術情報

SPREAD for WPF 2.0J 新機能紹介(3)- よりWPFらしく – MVVM対応を強化

こんにちは!WPF担当の高橋です。ここまで「よりExcelライクに」をテーマに新機能をご紹介してきました:

今日は少し違う視点で、「よりWPFらしく」をテーマに、新バージョンで可能になった、セルのバインディングをご紹介したいと思います。

MVVM対応の強化

本ブログでもご紹介したCodeZineの記事「SPREAD for WPFでMVVMパターンのアプリケーションを作成する」にもあるように、SPREADは旧バージョンにおいても、MVVMパターンに対応しています。ただし、新バージョンでは更にMVVM対応を強化しました。それが、セルのバインディングです。

セルのバインディング

セルのスタイルをXAMLで設定するにはCellPresenterクラスを使用しますが、2.0Jでは、セルの値を表すValueプロパティが、依存関係プロパティとして提供されるようになりました。

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

MVVMの実装では、コードビハインドに出来るだけ処理を記述せず、ビューとビューモデルがWPFのバインディングで対話することを要望されるお客様は少なくありません。そうしたお客様に、ぜひご活用いただきたい機能です。

セルのバインディングの使用例を、1つご紹介したいと思います。

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

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

ozu170831_1

このアプリケーションを構成するMVVM(モデル・ビュー・ビューモデル)の各要素は以下のとおりです。

モデル

モデルは、問合せを表す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; } 
}

※ここではコードの主要部分をご紹介します。本記事の最後にサンプルプロジェクトのダウンロードリンクがあります。

ビューモデル

ビューモデルの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();
    }
}

ビュー

ビューは、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>

実行

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

ozu170831_2

サンプルプロジェクト

本記事のサンプルプロジェクトを、こちらからダウンロードできます。

2017年10月6日 開催 「グレープシティECHO Tokyo 2017」

「よりWPFらしく」をテーマに新機能をご紹介しましたが、グレープシティでは「XAML」をテーマにイベントを開催します。

</>XAMLエキスパートが集結!- GrapeCity ECHO Tokyo 2017 開催 –

※本イベントですが、大変ありがたいことに満席(100名)となりました。誠に恐れ入りますが、参加ご希望の方はキャンセル待ちへのご登録をお願いいたします。