回答編集履歴

1

実装例追記

2024/03/14 11:17

投稿

TN8001
TN8001

スコア9441

test CHANGED
@@ -34,3 +34,153 @@
34
34
 
35
35
  個人的には良い(MVVMを逸脱していない)と思っていますが、ビヘイビアに追い出せばMVVM厳格派の方も納得するでしょうw
36
36
  [【WPF】Behaviorの実装方法【MVVM】 #WPF - Qiita](https://qiita.com/tkhshiq/items/89b13dc293fb3cd32849)
37
+
38
+ ## 実装例追記
39
+
40
+ > 無効な値(重複など)が含まれている場合にコミットをせずに編集状態を続行したい。
41
+
42
+ ユニークな値は`Id`のつもりでしょうか?
43
+ そのような値はデータベースの自動採番等で、ユーザーが編集する類のものではないような...
44
+
45
+ それともユーザー名のような、任意の値でいいけど被ってはいけないものでしょうか?
46
+ [validation - Wpf datagrid validationrule for unique field - Stack Overflow](https://stackoverflow.com/questions/10548357/wpf-datagrid-validationrule-for-unique-field)
47
+
48
+ ```xml
49
+ <Window
50
+ x:Class="Q3g1ql395qrqjbx.MainWindow"
51
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
52
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
53
+ xmlns:local="clr-namespace:Q3g1ql395qrqjbx"
54
+ Width="400"
55
+ Height="400">
56
+ <Window.Resources>
57
+ <Style
58
+ x:Key="errorStyle"
59
+ BasedOn="{x:Static DataGridTextColumn.DefaultEditingElementStyle}"
60
+ TargetType="{x:Type TextBox}">
61
+ <Setter Property="Validation.ErrorTemplate">
62
+ <Setter.Value>
63
+ <ControlTemplate>
64
+ <StackPanel>
65
+ <Grid>
66
+ <AdornedElementPlaceholder x:Name="element" />
67
+ <Border
68
+ Width="{Binding AdornedElement.ActualWidth, ElementName=element}"
69
+ HorizontalAlignment="Left"
70
+ BorderBrush="Red"
71
+ BorderThickness="1" />
72
+ </Grid>
73
+ <ItemsControl
74
+ Background="Snow"
75
+ DisplayMemberPath="ErrorContent"
76
+ Foreground="Red"
77
+ ItemsSource="{Binding}" />
78
+ </StackPanel>
79
+ </ControlTemplate>
80
+ </Setter.Value>
81
+ </Setter>
82
+ </Style>
83
+ </Window.Resources>
84
+
85
+ <Grid>
86
+ <DataGrid
87
+ AutoGenerateColumns="False"
88
+ InitializingNewItem="DataGrid_InitializingNewItem"
89
+ ItemsSource="{Binding Examples}"
90
+ RowValidationErrorTemplate="{x:Null}">
91
+ <DataGrid.Resources>
92
+ <CollectionViewSource x:Key="examples" Source="{Binding Examples}" />
93
+ </DataGrid.Resources>
94
+ <DataGrid.Columns>
95
+ <DataGridTextColumn
96
+ Width="*"
97
+ Binding="{Binding Id}"
98
+ Header="Id"
99
+ IsReadOnly="True" />
100
+
101
+ <DataGridTextColumn
102
+ Width="5*"
103
+ EditingElementStyle="{StaticResource errorStyle}"
104
+ Header="Name">
105
+ <DataGridTextColumn.Binding>
106
+ <Binding Path="Name" UpdateSourceTrigger="PropertyChanged">
107
+ <Binding.ValidationRules>
108
+ <local:UniqueNameRule
109
+ CurrentCollection="{StaticResource examples}"
110
+ ValidatesOnTargetUpdated="True"
111
+ ValidationStep="UpdatedValue" />
112
+ </Binding.ValidationRules>
113
+ </Binding>
114
+ </DataGridTextColumn.Binding>
115
+ </DataGridTextColumn>
116
+
117
+ <DataGridCheckBoxColumn Binding="{Binding Enabled}" Header="Enabled" />
118
+ </DataGrid.Columns>
119
+ </DataGrid>
120
+ </Grid>
121
+ </Window>
122
+ ```
123
+ ```cs
124
+ using System.Collections.ObjectModel;
125
+ using System.ComponentModel;
126
+ using System.Globalization;
127
+ using System.Windows;
128
+ using System.Windows.Controls;
129
+ using System.Windows.Data;
130
+
131
+ namespace Q3g1ql395qrqjbx;
132
+
133
+
134
+ public partial class MainWindow : Window
135
+ {
136
+ public ObservableCollection<Example> Examples { get; }
137
+
138
+ public MainWindow()
139
+ {
140
+ InitializeComponent();
141
+ DataContext = this;
142
+
143
+ Examples = new(Enumerable.Range(1, 5).Select(x => new Example { Id = x, Name = $"Example{x}", }));
144
+ }
145
+
146
+ private void DataGrid_InitializingNewItem(object sender, InitializingNewItemEventArgs e)
147
+ {
148
+ if (e.NewItem is Example example)
149
+ example.Id = Examples.Max(x => x.Id) + 1;
150
+ }
151
+ }
152
+
153
+ public record Example : IEditableObject
154
+ {
155
+ public int Id { get; set; }
156
+ public string? Name { get; set; }
157
+ public bool Enabled { get; set; }
158
+
159
+ private Example? backup;
160
+ public void BeginEdit() => backup = this with { };
161
+ public void CancelEdit() => (Id, Name, Enabled) = (backup!.Id, backup.Name, backup.Enabled);
162
+ public void EndEdit() => backup = null;
163
+ }
164
+
165
+ // [validation - Wpf datagrid validationrule for unique field - Stack Overflow](https://stackoverflow.com/questions/10548357/wpf-datagrid-validationrule-for-unique-field)
166
+ public class UniqueNameRule : ValidationRule
167
+ {
168
+ public CollectionViewSource? CurrentCollection { get; set; }
169
+
170
+ public override ValidationResult Validate(object value, CultureInfo cultureInfo)
171
+ {
172
+ if (value is BindingExpression { DataItem: Example current })
173
+ {
174
+ foreach (var other in (Collection<Example>)CurrentCollection!.Source)
175
+ {
176
+ if (ReferenceEquals(current, other)) continue;
177
+ if (current.Name == other.Name)
178
+ return new ValidationResult(false, $"{current.Name}はすでに使用されています。");
179
+ }
180
+ }
181
+
182
+ return ValidationResult.ValidResult;
183
+ }
184
+ }
185
+ ```
186
+ ![アプリ動画](https://ddjkaamml8q8x.cloudfront.net/questions/2024-03-14/ec679ebb-95c9-4d6b-a6da-ef8a9c40d2af.gif)