前回は単票形式でのWPFのデータバインドとエラー表示を行いました。
一方、自分のまわりではグリッド形式のウィンドウなども見かけるため、今度はグリッド形式でのWPFのデータバインドとエラー表示を行ってみました。
なお、サンプル的なコードなので、厳密なエラーチェックや表示はしていません...
2015/12/10 追記 ここから
公式Blogに以下の記事が掲載されましたので、Microsoft.TeamFoundation.MVVM
の使用前に記事を確認してみてください。
Microsoft.TeamFoundation.MVVM 名前空間の利用について - Visual Studio サポート チーム blog - Site Home - MSDN Blogs
2015/12/10 追記 ここまで
環境
- Windows7
- .NET Framework 4.5
- Microsoft.TeamFoundation.MVVM
- ObservableCollection
- INotifyPropertyChanged
- INotifyDataErrorInfo
作るもの
View-ViewModel-Modelの構成
種類 | クラス名 | 備考 |
---|---|---|
View | DataGridView | |
ViewModel | DataGridViewModel | データバインド用のプロパティとして、ModelをObservableCollection<Order> として持つ |
Model | Order | INotifyPropertyChanged とINotifyDataErrorInfo を実装 |
なお、今回はModelにデータベースを使いません。
また、DataGridViewModelは前回作成したVMBaseを継承して作ります。
VMBaseのソースコード: CSharp-Sample/MVVMApp/MVVMApp/VMBase.cs at master - thinkAmi/CSharp-Sample - GitHub
画面イメージ
起動時
エラー表示(その1)
エラー表示(その2)
流れ
Modelの作成 (Order.cs
)
DataGridにて表示する列をプロパティとして持つ、以下のようなOrderクラスを作成します。
プロパティ名 | 日本語名 | エラーチェック |
---|---|---|
ID | 連番 | - |
ItemName | 商品名 | - |
Quantity | 数量 | 0以外 |
UnitPrice | 単価 | 0以外 |
TotalPrice | 合計 | - |
なお、ModelでエラーチェックしてViewに反映させるため、INotifyDataErrorInfo
インタフェースを実装しています。
また、Model内部での変更(数量or単価が変更されたら、合計が更新)をViewへと反映させるため、INotifyPropertyChanged
インタフェースも実装しています。
ViewModelの作成 (DataGridViewModel.cs
)
今回はデータベースを使わないので、適当にModelの初期値を設定しておきます。
また、今回のエラーチェックはModelで行っているため、ViewModelではエラーに関する実装はしていません。
Viewの作成 (DataGridView.xaml
)
データバインドの設定を記載
前回同様、Window要素とDataContext要素を追加します。
<Window ... xmlns:local="clr-namespace:MVVMApp" ...> <Window.DataContext> <local:DataGridViewModel /> </Window.DataContext>
データバインド対象のDataGridを記述
後で変更しますが、最初は以下のような感じで記述します。
<DataGrid AutoGenerateColumns="False" Margin="10" ItemsSource="{Binding UpdateSourceTrigger=PropertyChanged, Path=DataGrid}"> <DataGrid.Columns> <DataGridTextColumn Header="ID" IsReadOnly="True" Binding="{Binding Path=ID}" /> <DataGridTextColumn Header="商品名" Binding="{Binding Path=ItemName}" /> <DataGridTextColumn Header="数量" Binding="{Binding Path=Quantity, UpdateSourceTrigger=PropertyChanged}" /> <DataGridTextColumn Header="単価" Binding="{Binding Path=UnitPrice, UpdateSourceTrigger=PropertyChanged}" /> <DataGridTextColumn Header="合計" Binding="{Binding Path=TotalPrice}" /> </DataGrid.Columns> </DataGrid>
エラー列を示す、!
(exclamation mark - error indicator)への対応
この時点でのViewでは、以下のように、エラーを修正してもエラー列を示す !
は消えません。
↓
↓
この挙動についていろいろと調べたところ、.NET4.5のWPFのバグのような情報もありました。
- c# - DataGridRow error indicator not working with INotifyDataErrorInfo - Stack Overflow
- DataGridRow error indicator not working with INotifyDataErrorInfo | Microsoft Connect
そのため、以下を参考に、error indicatorは非表示にする記述を追加しました。
WPF DataGrid validation errors not clearing - Stack Overflow
<DataGrid.RowStyle> <Style TargetType="DataGridRow"> <Setter Property="ValidationErrorTemplate" Value="{x:Null}"/> </Style> </DataGrid.RowStyle>
エラーのあるセルの表示変更の検討
今のままではエラーのあるセルの表示が赤枠のみで分かりづらいため、セルの表示変更を行います。
なお、DataGridのColumnでDataGridTextColumn
を使っている時と、DataGridTemplateColumn
を使っている時とでやり方が異なったので、両方の実装を試してみました。
エラーのあるセルの表示変更 (DataGridTextColumn
編)
DataGridTextColumnは
- ElementStyle: フォーカスがないときのStyle
- EditingElementStyle: フォーカスがあるときのStyle
とそれぞれStyleを指定できるので、以下のようにDataGridTextColumn要素を修正します。
<DataGridTextColumn Header="単価" ElementStyle="{StaticResource errorStyleLostFocus}" EditingElementStyle="{StaticResource errorStyleGotFocus}" Binding="{Binding Path=UnitPrice, UpdateSourceTrigger=PropertyChanged}" />
次に、上記で指定したerrorStyleLostFocusとerrorStyleGotFocusのStyleを作成します。
Styleで必要な内容は、エラー時は常に
- 背景色を変更して表示
- ToolTipでエラー内容を表示
とすることから、両方とも同じようなものになります。
次にDataGridTextColumnの挙動を調べてみたところ、
メモ DataGridTextColumn は、TextBlock 要素を非編集モードで作成し、TextBox 要素を編集モードで作成します。
DataGridTextColumn クラス (System.Windows.Controls) - MSDN
とのでした。
TextBlockとTextBoxの共通の祖先にあたるFrameworkElement
を見ましたが、背景色を変更するBackground
プロパティがなく、完全な共通化は難しそうでした。
- FrameworkElement クラス (System.Windows)
- TextBlock クラス (System.Windows.Controls)
- TextBox クラス (System.Windows.Controls)
そのため、ToolTipのみ共通化することにしました。
参考: .net - How to apply multiple styles in WPF - Stack Overflow
まずは、エラーがあるときにTootlTipを表示するStyleを記述します。
<Style TargetType="{x:Type FrameworkElement}"> <Style.Triggers> <Trigger Property="Validation.HasError" Value="True"> <Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}"/> </Trigger> </Style.Triggers> </Style>
次に、上記のFrameworkElementのStyleをBasedOn
を使って拡張し、TextBox向けとTextBlock向けのStyleを記述します。
<Style x:Key="errorStyleLostFocus" TargetType="{x:Type TextBlock}" BasedOn="{StaticResource {x:Type FrameworkElement}}" > <Style.Triggers> <Trigger Property="Validation.HasError" Value="True"> <Setter Property="Background" Value="Red"/> </Trigger> </Style.Triggers> </Style> <Style x:Key="errorStyleGotFocus" TargetType="{x:Type TextBox}" BasedOn="{StaticResource {x:Type FrameworkElement}}" > <Style.Triggers> <Trigger Property="Validation.HasError" Value="True"> <Setter Property="Background" Value="Red"/> </Trigger> </Style.Triggers> </Style>
以上で、DataGridTextColumnを使った時のエラー表示ができました。
エラーのあるセルの表示変更 (DataGridTemplateColumn
編)
以下を参考に、xamlにDataGridTemplateColumn部分とStyle部分を記述することになります。
DataGrid Validation using IDataErrorInfo in MVVM - WPF | Haris Hasan Blog
まずは、数量の列をDataGridTemplateColumnへと変更します。
<DataGridTemplateColumn Header="数量"> <DataGridTemplateColumn.CellTemplate> <DataTemplate> <TextBox Style="{StaticResource ResourceKey=textbox}" Text="{Binding Path=Quantity, UpdateSourceTrigger=PropertyChanged}" /> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn>
次にStyleを作成します。中身はDataGridTextColumn編とほぼ一緒です(両者で色の区別をつけるためにこちらは背景色をPinkにしてあります)。
<Style x:Key="textbox" TargetType="{x:Type TextBox}"> <Style.Triggers> <Trigger Property="Validation.HasError" Value="True"> <Setter Property="ToolTip" Value="{Binding Path=(Validation.Errors)[0].ErrorContent, RelativeSource={RelativeSource Self}}" /> <Setter Property="Background" Value="Pink" /> </Trigger> </Style.Triggers> </Style>
以上で、DataGridTemplateColumnを使った時のエラー表示ができました。
DataGridTextColumnとDataGridTemplateColumnを使った時の違い
見た目が異なる他、セルにフォーカスを合わせた際、
- DataGridTextColumn: 初回クリックでセルが選択され、次回クリックで入力が可能になる
- DataGridTemplateColumn: 初回クリックで入力が可能になる(普通のTextBoxっぽい)
という違いがありました。
他にもあるかもしれませんので、気づいたら追記します。
ソースコード
GitHubに上げてあります。
CSharp-Sample/MVVMApp at master - thinkAmi/CSharp-Sample - GitHub
参考
- WPF4.5入門 その23 「DataGridコントロール その1」 - かずきのBlog@hatena
- WPF4.5入門 その24 「DataGridコントロール その2」 - かずきのBlog@hatena
- 方法: DataGrid コントロールを使用して検証を実装する - MSDN
- WPF DataGrid Practical Examples - CodeProject
- WPF Tutorial | DataGrid
- Styling Microsoft’s WPF datagrid - Jaime Rodriguez - Site Home - MSDN Blogs
- WPFデータグリッドの選択色を変更する - Yuya Yamaki’s blog
- 汎用ジェネリックコレクション その2 ObservableCollection/ReadOnlyObservableCollection (System.Collections.ObjectModel) - Programming/.NET Framework/コレクション - 総武ソフトウェア推進所