&tag(WPF, DataGrid); *目次 [#d41ba80f] #contents *参考情報 [#oa3ed1b7] -[[WPF]] -[[WPF DataGrid Practical Examples]]…入門記事。 -[[WPF DataGrid – Committing changes cell-by-cell | Colin Eberhardt's Adventures in WPF:http://www.scottlogic.co.uk/blog/colin/2009/01/wpf-datagrid-committing-changes-cell-by-cell/]] -[[Pasting content to new rows on the WPF DataGrid - Vincent Sibal's Blog - Site Home - MSDN Blogs:http://blogs.msdn.com/b/vinsibal/archive/2008/09/25/pasting-content-to-new-rows-on-the-wpf-datagrid.aspx]]…DataGridの新規行追加位置(NewItemPlaceHolder)にペーストするコード。 -[[Nigel Spencer's Blog: WPF DataGrid Tips:http://blog.spencen.com/2009/04/13/wpf-datagrid-tips.aspx]] *スタイル・見た目 [#w8daed64] -[[./スタイル]] *編集コマンド [#wc4bb535] **キーボードによる編集 [#v0ab521e] -[[Overview of the editing features in the WPF DataGrid - Vincent Sibal's Blog - Site Home - MSDN Blogs:http://blogs.msdn.com/b/vinsibal/archive/2008/10/01/overview-of-the-editing-features-in-the-wpf-datagrid.aspx]]によると次の編集コマンドが有効。 ,BeginEditCommand,F2 ,CancelEditCommand,Esc ,CommitEditCommand,Enter ,DeleteCommand,Delete **編集コマンドのハンドリング方法 [#cdc414ff] ***参考リンク [#z06e3b62] -[[Inserting, Updating, and Deleting from a WPF DataGrid:http://blogs.u2u.be/diederik/post/2009/09/29/Inserting-Updating-and-Deleting-from-a-WPF-DataGrid.aspx]]…編集コマンド実行時にYES/NOダイアログとかを表示する。 ***基本 [#k7719458] -DataGridで何も指定しないとDeleteキーによる削除が行われてしまう。これは単に画面・メモリ上の行を削除するだけ。 -ユーザーに削除前の確認を行ったり、関連する情報をまとめて削除したりする必要がある場合、デフォルトの削除処理ではなく、自前の削除処理を呼び出す必要がある。 -これを行うために、PreviewKeyDownイベントハンドラを使ったり、自前のInputBindingを使うことができる。(CommandManager.RegisterClassInputBinding()なども) ***PreviewKeyDownイベントハンドラを使う。 [#a203ca7b] Deleteキーがおされた時にデフォルトのイベントハンドラが呼ばれる前に自前の処理を行う。 ''xaml'' #pre{{ <DataGrid PreviewKeyDown="dataGrid_PreviewKeyDown"> </DataGrid> }} ''コードビハインド'' #pre{{ private void dataGrid_PreviewKeyDown(object sender, KeyEventArgs e) { if (e.Key == Key.Delete) { var dataGrid = (DataGrid)sender; MessageBox.Show("DELETEキーが押されました"); e.Handled = true; //trueを設定するとデフォルト動作は行われない。 } } }} *DataGridComboBoxColumn [#s17505ee] **基本 [#ndd44f7c] -[[WPF DataGrid – Dynamically updating DataGridComboBoxColumn - Vincent Sibal's Blog - Site Home - MSDN Blogs:http://blogs.msdn.com/b/vinsibal/archive/2008/12/17/wpf-datagrid-dynamically-updating-datagridcomboboxcolumn.aspx]]によると、DataBindingを使っている場合普通に表示するだけでも結構めんどくさい。ElementStyleやEditingElementStyleを定義しないと表示できない? **編集可能にする [#l4fbc04e] -[[combobox - using wpf datagridcomboboxcolumn's IsSynchronizedWithCurrentItem - Stack Overflow:http://stackoverflow.com/questions/4700346/using-wpf-datagridcomboboxcolumns-issynchronizedwithcurrentitem]]では結局DataGridTemplateColumnを使っている。DataGridComboBoxColumnでは不可能なのかも。 **共通のItemsSourceを使う [#z5263074] -各行に選択肢リストをもたせるのではなく、共通の選択肢リストをもたせたい。 -[[Using WPF DataGridComboBoxColumn with MVVM - Binding to Property in ViewModel - Stack Overflow:http://stackoverflow.com/questions/3562934/using-wpf-datagridcomboboxcolumn-with-mvvm-binding-to-property-in-viewmodel]]ではRelativeSourceを使っておやのDataContextを探す方法で実現している。 **値が表示されたり消えたり妙な動作 [#i3691ad8] DataGridComboBoxでElementStyleやEditingElementStyleを指定し、かつIsSynchronizedWithCurrentItem=Trueを使うと同列の他の行の表示がみだれるのでやめたほうがよいっぽい。 #pre{{ <DataGridComboBoxColumn Width="*" Header="変数の型"> <DataGridComboBoxColumn.ElementStyle> <Style TargetType="ComboBox"> <Setter Property="ItemsSource" Value="{Binding Path=Types}"/> <!-- これ --> <Setter Property="IsSynchronizedWithCurrentItem" Value="True"/> <Setter Property="DisplayMemberPath" Value="Label"/> <Setter Property="IsReadOnly" Value="True"/> <Setter Property="SelectedItem" Value="{Binding Type}"/> </Style> </DataGridComboBoxColumn.ElementStyle> <DataGridComboBoxColumn.EditingElementStyle> <Style TargetType="ComboBox"> <Setter Property="ItemsSource" Value="{Binding Path=Types}"/> <!-- これ --> <Setter Property="IsSynchronizedWithCurrentItem" Value="True"/> <Setter Property="DisplayMemberPath" Value="Label"/> <Setter Property="SelectedItem" Value="{Binding Type}"/> </Style> </DataGridComboBoxColumn.EditingElementStyle> </DataGridComboBoxColumn> }} *DataGridTemplateColumn [#qc97e048] **ユーザーコントロールを表示する [#b1206246] -[[wpf - custom control in DataGridTemplateColumn - Stack Overflow:http://stackoverflow.com/questions/4536987/custom-control-in-datagridtemplatecolumn]] *Validation [#na59d971] **値入力ごとの検証 [#u8abbfb1] DataGridColumnので設定する。以下の例ではValidatesOnDataErrorsがTrueなのでIDataErrorInfoを使って検証される。 <DataGridTextColumn Width="100" Header="名前" Binding="{Binding Name, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"/> **行全体の検証 [#ke5bb539] RowValidationRulesにValidationRuleを設定する。 #pre{{ <DataGrid.RowValidationRules> <local:RowDataInfoValidationRule ValidationStep="UpdatedValue"/> </DataGrid.RowValidationRules> }} **ValidationTemplate [#za61a3e6] -[[wpf datagrid cell validation template:http://social.msdn.microsoft.com/Forums/en/wpf/thread/8eded680-08e3-46a7-8779-f07b31b7af42]]…手動でMakeInvalidしてエラー表示しようとしている。 *その他 [#f4b44d99] -[[DataGridの一括検証:http://social.msdn.microsoft.com/Forums/ja-JP/wpfja/thread/b6fd8b81-0e81-4764-835a-db9ea36009ae/]] *Tips [#ccab84b4] **DataGridRow、DataGridCellを検索する [#o3493e81] -[[Techie things: Get WPF DataGrid row and cell:http://techiethings.blogspot.com/2010/05/get-wpf-datagrid-row-and-cell.html]] **列を動的に生成する [#z7f7562c] -[[c# - How can I programatically create a WPF Toolkit DataGridTemplateColumn? - Stack Overflow:http://stackoverflow.com/questions/1757339/how-can-i-programatically-create-a-wpf-toolkit-datagridtemplatecolumn]]にあるようにFrameworkElementFactoryを使って自力で作る。 -[[wpf - dynamic datatemplate with valueconverter - Stack Overflow:http://stackoverflow.com/questions/652304/dynamic-datatemplate-with-valueconverter]]…XamlReader.Parseを使う。 **シングルクリックで編集する [#kc0801ee] ***基本 [#ua6f39ad] -[[Windows Presentation Foundation (WPF) - Single-Click Editing:http://wpf.codeplex.com/wikipage?title=Single-Click%20Editing]]にある方法で可能。 -DataGridCellにPreviewMouseLeftButtonDownをセットし、事前にセルにフォーカスをあわせ選択状態にすることでシングルクリックで編集可能になる。 -Application全体でDataGridに共通のスタイルを設定している場合、EventSetterで設定するとスタイルが適用されなくなってしまうので、ResourceDictionaryを使って設定する(下記参照) ***すべてのDataGridでシングルクリック編集を可能にする [#l182e8fd] -[[c# - Single click edit in WPF DataGrid - Stack Overflow:http://stackoverflow.com/questions/3426765/single-click-edit-in-wpf-datagrid]]に記述あり。 -ResourceDictionaryを使う。新規→リソースディクショナリでDataGrydStyles.xamlを追加。 #pre{{ <ResourceDictionary x:Class="ControlDemo.DataGridDemo.SingleClick.DataGridStyles" x:ClassModifier="public" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <Style TargetType="DataGrid"> <!-- Your DataGrid style definition goes here --> <Setter Property="CellStyle"> <Setter.Value> <Style TargetType="DataGridCell"> <EventSetter Event="PreviewMouseLeftButtonDown" Handler="DataGridCell_PreviewMouseLeftButtonDown"/> </Style> </Setter.Value> </Setter> </Style> </ResourceDictionary> }} -DataGridStyles.xaml.csを追加し、ここにDataGridCell_PreviewMouseLeftButtonDownを追加する。 #pre{{ namespace ControlDemo.DataGridDemo.SingleClick { partial class DataGridStyles : ResourceDictionary { // // SINGLE CLICK EDITING // private void DataGridCell_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { DataGridCell cell = sender as DataGridCell; if (cell != null && !cell.IsEditing && !cell.IsReadOnly) { if (!cell.IsFocused) { cell.Focus(); } DataGrid dataGrid = FindVisualParent<DataGrid>(cell); if (dataGrid != null) { if (dataGrid.SelectionUnit != DataGridSelectionUnit.FullRow) { if (!cell.IsSelected) cell.IsSelected = true; } else { DataGridRow row = FindVisualParent<DataGridRow>(cell); if (row != null && !row.IsSelected) { row.IsSelected = true; } } } } } static T FindVisualParent<T>(UIElement element) where T : UIElement { UIElement parent = element; while (parent != null) { T correctlyTyped = parent as T; if (correctlyTyped != null) { return correctlyTyped; } parent = VisualTreeHelper.GetParent(parent) as UIElement; } return null; } } } }} ***DataGridTemplateColumnで内部のコントロールにフォーカスするようにする [#n36c88d9] -DataGridTextColumnなどでは上記シングルクリック対応により一発でセルが編集可能になるが、DataGridTemplateColumnでは編集状態になるものの、内部のコントロールにフォーカスしないため、もう一回クリックする必要がでてくる。 -これを解消するには、DataGridTemplateColumnを継承したクラスを使う。 #pre{{ public class CustomDataGridTemplateColumn :DataGridTemplateColumn { protected override object PrepareCellForEdit(FrameworkElement editingElement, RoutedEventArgs editingEventArgs) { editingElement.MoveFocus(new TraversalRequest(FocusNavigationDirection.First)); return base.PrepareCellForEdit(editingElement, editingEventArgs); } } }} -xamlでDataGridTemplateColumn→CustomDataGridTemplateColumnに変更する。 **行をDrag and Dropで入れ替える。セルベースで選択する [#m3db9f50] -[[Common DataGrid Add-Ons:http://code.msdn.microsoft.com/Common-DataGrid-Add-Ons-4f64fcee]]ここにサンプルあり。 **三つの状態をもったソート [#o9daccd7] -[[WPF DataGrid: Tri-state Sorting sample - Vincent Sibal's Blog - Site Home - MSDN Blogs:http://blogs.msdn.com/b/vinsibal/archive/2008/08/29/wpf-datagrid-tri-state-sorting-sample.aspx]] *トラブルシューティング [#t9a37977] **追加情報: 'DeferRefresh'は、AddNewトランザクションまたはEditItemトランザクションの実行中は許可されません。 [#d2c201d7] ***再現方法 [#ca582bcf] -DataGridでセル編集中に、DataContextが切り替わると発生。 -例えばTabControlの上にDataGridを乗せタブ切り替えにともなってバインドされたデータが切り替わる場合に発生する。 #ref(cell.png) #ref(error.png) ***解決方法 [#z8f2ed33] -[[DataGrid exception on validation failure (DeferRefresh is not allowed...) | Microsoft Connect:https://connect.microsoft.com/VisualStudio/feedback/details/591125/datagrid-exception-on-validation-failure-deferrefresh-is-not-allowed]] -上記リンクの回避策ではViewModelにIEditableObjectを実装し、DataGridに、DataGridRollbackOnUnfocusedBehaviourというビヘイビアーを定義する回避策が紹介されている。