我的XAML显示与配置为ObservableCollection
的RowValidationRule
绑定的DataGrid。每当DataGridTextColumn
中的值更改时,都会触发ValidationRule并触发IsDirty == true
。但是,对于DataGridComboBoxColumn
,即使实际上已更新IsDirty == false
属性,也会触发ValidationRule,但会触发SelectedValueBinding
。我不明白为什么或应该为DataGridComboBoxColumn
条目正确执行IsDirty == true
而应该做些什么。
UPDATE
我在所有ValidationSteps中运行了ValidationRule
,发现收款人ComboBox的值在ValidationStep.RawProposedValue
中为已经更改。这与直到ValidationStep.UpdatedValue
才显示更改的所有其他列不同。直到ValidationStep.UpdatedValue
都不会更改Status ComboBox(可用于枚举),但第一步已存在Payees值。也许它假设ComboBox本身应该已经验证过了?
XAML
<Window.Resources>
<ObjectDataProvider x:Key="statusEnum" MethodName="GetValues" ObjectType="{x:Type sys:Enum}">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="data:TransactionStatus" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
<CollectionViewSource x:Key="PayeesView" Source="{Binding Payees}" />
<convert:BankTransactionConverter x:Key="TransConverter" />
</Window.Resources>
<Grid>
...
<DataGrid x:Name="BankDataGrid" Grid.Row="2"
ItemsSource="{Binding BankTransactions}"
SelectedItem="{Binding SelectedBankTransaction, Converter={StaticResource TransConverter}}"
<DataGrid.RowValidationRules>
<valid:BankTransactionValidationRule ValidationStep="UpdatedValue"/>
</DataGrid.RowValidationRules>
<DataGrid.Columns>
<DataGridTextColumn x:Name="BankTransReference" Header="Ref" Width="70"
Binding="{Binding Reference}" />
<DataGridTemplateColumn x:Name="BankTransDate" Header="Date" Width="100" SortMemberPath="Date" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Date, StringFormat=\{0:MM/dd/yyyy\}}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<DatePicker SelectedDate="{Binding Date}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridComboBoxColumn x:Name="BankTransPayee" Header="Payee"
ItemsSource="{Binding Source={StaticResource PayeesView}}" DisplayMemberPath="Name"
SelectedValueBinding="{Binding PayeeName, UpdateSourceTrigger=LostFocus}" SelectedValuePath="Name" >
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="ComboBox">
<Setter Property="IsEditable" Value="True" />
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>
<DataGridTextColumn x:Name="BankTransAmount" Header="Amount" IsReadOnly="True" Width="100"
Binding="{Binding Amount, StringFormat=\{0:N2\}}" >
<DataGridTextColumn.CellStyle>
<Style>
<Setter Property="TextBlock.TextAlignment" Value="Right" />
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridComboBoxColumn x:Name="BankTransStatus" Header="?" Width="18"
ItemsSource="{Binding Source={StaticResource statusEnum}, Mode=OneWay}"
SelectedItemBinding="{Binding Status}" />
<DataGridTextColumn x:Name="BankTransMemo" Header="Memo" Width="220"
Binding="{Binding Memo}" />
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Click="Button_ShowHideSub">Subs</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
ValidationRule
public class BankTransactionValidationRule : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
var bg = (BindingGroup)value;
var row = (DataGridRow)bg.Owner;
var item = (BankTransaction)row.Item;
switch (ValidationStep)
{
case ValidationStep.RawProposedValue:
System.Diagnostics.Debug.WriteLine($"{nameof(BankTransactionTestValidationRule)}: ValidationStep={ValidationStep.RawProposedValue}");
ShowInfo(item);
break;
case ValidationStep.ConvertedProposedValue:
System.Diagnostics.Debug.WriteLine($"{nameof(BankTransactionTestValidationRule)}: ValidationStep={ValidationStep.ConvertedProposedValue}");
ShowInfo(item);
break;
case ValidationStep.UpdatedValue:
System.Diagnostics.Debug.WriteLine($"{nameof(BankTransactionTestValidationRule)}: ValidationStep={ValidationStep.UpdatedValue}");
ShowInfo(item);
break;
case ValidationStep.CommittedValue:
System.Diagnostics.Debug.WriteLine($"{nameof(BankTransactionTestValidationRule)}: ValidationStep={ValidationStep.CommittedValue}");
ShowInfo(item);
break;
default: // Should not get here
System.Diagnostics.Debug.WriteLine($"{nameof(BankTransactionTestValidationRule)}: DEFAULT\n");
break;
}
if (bg.IsDirty)
{
System.Diagnostics.Debug.WriteLine($"{nameof(BankTransactionTestValidationRule)}: {item.TransactionId} is dirty\n");
}
else
{
System.Diagnostics.Debug.WriteLine($"{nameof(BankTransactionTestValidationRule)}: {item.TransactionId} is NOT dirty\n");
}
return ValidationResult.ValidResult;
}
private void ShowInfo(BankTransaction trans)
{
System.Diagnostics.Debug.WriteLine($"{nameof(BankTransactionTestValidationRule)}: Reference={trans.Reference}");
System.Diagnostics.Debug.WriteLine($"{nameof(BankTransactionTestValidationRule)}: Date={trans.Date}");
System.Diagnostics.Debug.WriteLine($"{nameof(BankTransactionTestValidationRule)}: PayeeName={trans.PayeeName}");
System.Diagnostics.Debug.WriteLine($"{nameof(BankTransactionTestValidationRule)}: Status={trans.Status}");
System.Diagnostics.Debug.WriteLine($"{nameof(BankTransactionTestValidationRule)}: Memo={trans.Memo}");
}
}
输出
BankTransactionTestValidationRule: ValidationStep=RawProposedValue
>> BankTransactionTestValidationRule: Reference=
BankTransactionTestValidationRule: Date=12/24/2019 12:00:00 AM
BankTransactionTestValidationRule: PayeeName=Amazon
BankTransactionTestValidationRule: Status=N
BankTransactionTestValidationRule: Memo=
BankTransactionTestValidationRule: 2 is dirty
BankTransactionTestValidationRule: ValidationStep=ConvertedProposedValue
>> BankTransactionTestValidationRule: Reference=
BankTransactionTestValidationRule: Date=12/24/2019 12:00:00 AM
BankTransactionTestValidationRule: PayeeName=Amazon
BankTransactionTestValidationRule: Status=N
BankTransactionTestValidationRule: Memo=
BankTransactionTestValidationRule: 2 is dirty
BankTransactionTestValidationRule: ValidationStep=UpdatedValue
>> BankTransactionTestValidationRule: Reference=2nd row
BankTransactionTestValidationRule: Date=12/24/2019 12:00:00 AM
BankTransactionTestValidationRule: PayeeName=Amazon
BankTransactionTestValidationRule: Status=N
BankTransactionTestValidationRule: Memo=
BankTransactionTestValidationRule: 2 is dirty
BankTransactionTestValidationRule: ValidationStep=CommittedValue
>> BankTransactionTestValidationRule: Reference=2nd row
BankTransactionTestValidationRule: Date=12/24/2019 12:00:00 AM
BankTransactionTestValidationRule: PayeeName=Amazon
BankTransactionTestValidationRule: Status=N
BankTransactionTestValidationRule: Memo=
BankTransactionTestValidationRule: 2 is dirty
BankTransactionTestValidationRule: ValidationStep=RawProposedValue
BankTransactionTestValidationRule: Reference=2nd row
>> BankTransactionTestValidationRule: Date=12/24/2019 12:00:00 AM
BankTransactionTestValidationRule: PayeeName=Amazon
BankTransactionTestValidationRule: Status=N
BankTransactionTestValidationRule: Memo=
BankTransactionTestValidationRule: 2 is dirty
BankTransactionTestValidationRule: ValidationStep=ConvertedProposedValue
BankTransactionTestValidationRule: Reference=2nd row
>> BankTransactionTestValidationRule: Date=12/24/2019 12:00:00 AM
BankTransactionTestValidationRule: PayeeName=Amazon
BankTransactionTestValidationRule: Status=N
BankTransactionTestValidationRule: Memo=
BankTransactionTestValidationRule: 2 is dirty
BankTransactionTestValidationRule: ValidationStep=UpdatedValue
BankTransactionTestValidationRule: Reference=2nd row
>> BankTransactionTestValidationRule: Date=10/6/2020 12:00:00 AM
BankTransactionTestValidationRule: PayeeName=Amazon
BankTransactionTestValidationRule: Status=N
BankTransactionTestValidationRule: Memo=
BankTransactionTestValidationRule: 2 is dirty
BankTransactionTestValidationRule: ValidationStep=CommittedValue
BankTransactionTestValidationRule: Reference=2nd row
>> BankTransactionTestValidationRule: Date=10/6/2020 12:00:00 AM
BankTransactionTestValidationRule: PayeeName=Amazon
BankTransactionTestValidationRule: Status=N
BankTransactionTestValidationRule: Memo=
BankTransactionTestValidationRule: 2 is dirty
BankTransactionTestValidationRule: ValidationStep=RawProposedValue
BankTransactionTestValidationRule: Reference=2nd row
BankTransactionTestValidationRule: Date=10/6/2020 12:00:00 AM
>> BankTransactionTestValidationRule: PayeeName=PenFed
BankTransactionTestValidationRule: Status=N
BankTransactionTestValidationRule: Memo=
BankTransactionTestValidationRule: 2 is NOT dirty
BankTransactionTestValidationRule: ValidationStep=ConvertedProposedValue
BankTransactionTestValidationRule: Reference=2nd row
BankTransactionTestValidationRule: Date=10/6/2020 12:00:00 AM
>> BankTransactionTestValidationRule: PayeeName=PenFed
BankTransactionTestValidationRule: Status=N
BankTransactionTestValidationRule: Memo=
BankTransactionTestValidationRule: 2 is NOT dirty
BankTransactionTestValidationRule: ValidationStep=UpdatedValue
BankTransactionTestValidationRule: Reference=2nd row
BankTransactionTestValidationRule: Date=10/6/2020 12:00:00 AM
>> BankTransactionTestValidationRule: PayeeName=PenFed
BankTransactionTestValidationRule: Status=N
BankTransactionTestValidationRule: Memo=
BankTransactionTestValidationRule: 2 is NOT dirty
BankTransactionTestValidationRule: ValidationStep=CommittedValue
BankTransactionTestValidationRule: Reference=2nd row
BankTransactionTestValidationRule: Date=10/6/2020 12:00:00 AM
>> BankTransactionTestValidationRule: PayeeName=PenFed
BankTransactionTestValidationRule: Status=N
BankTransactionTestValidationRule: Memo=
BankTransactionTestValidationRule: 2 is NOT dirty
BankTransactionTestValidationRule: ValidationStep=RawProposedValue
BankTransactionTestValidationRule: Reference=2nd row
BankTransactionTestValidationRule: Date=10/6/2020 12:00:00 AM
BankTransactionTestValidationRule: PayeeName=PenFed
>> BankTransactionTestValidationRule: Status=N
BankTransactionTestValidationRule: Memo=
BankTransactionTestValidationRule: 2 is dirty
BankTransactionTestValidationRule: ValidationStep=ConvertedProposedValue
BankTransactionTestValidationRule: Reference=2nd row
BankTransactionTestValidationRule: Date=10/6/2020 12:00:00 AM
BankTransactionTestValidationRule: PayeeName=PenFed
>> BankTransactionTestValidationRule: Status=N
BankTransactionTestValidationRule: Memo=
BankTransactionTestValidationRule: 2 is dirty
BankTransactionTestValidationRule: ValidationStep=UpdatedValue
BankTransactionTestValidationRule: Reference=2nd row
BankTransactionTestValidationRule: Date=10/6/2020 12:00:00 AM
BankTransactionTestValidationRule: PayeeName=PenFed
>> BankTransactionTestValidationRule: Status=V
BankTransactionTestValidationRule: Memo=
BankTransactionTestValidationRule: 2 is NOT dirty
BankTransactionTestValidationRule: ValidationStep=CommittedValue
BankTransactionTestValidationRule: Reference=2nd row
BankTransactionTestValidationRule: Date=10/6/2020 12:00:00 AM
BankTransactionTestValidationRule: PayeeName=PenFed
>> BankTransactionTestValidationRule: Status=V
BankTransactionTestValidationRule: Memo=
BankTransactionTestValidationRule: 2 is NOT dirty
BankTransactionTestValidationRule: ValidationStep=RawProposedValue
BankTransactionTestValidationRule: Reference=2nd row
BankTransactionTestValidationRule: Date=10/6/2020 12:00:00 AM
BankTransactionTestValidationRule: PayeeName=PenFed
BankTransactionTestValidationRule: Status=V
>> BankTransactionTestValidationRule: Memo=
BankTransactionTestValidationRule: 2 is dirty
BankTransactionTestValidationRule: ValidationStep=ConvertedProposedValue
BankTransactionTestValidationRule: Reference=2nd row
BankTransactionTestValidationRule: Date=10/6/2020 12:00:00 AM
BankTransactionTestValidationRule: PayeeName=PenFed
BankTransactionTestValidationRule: Status=V
>> BankTransactionTestValidationRule: Memo=
BankTransactionTestValidationRule: 2 is dirty
BankTransactionTestValidationRule: ValidationStep=UpdatedValue
BankTransactionTestValidationRule: Reference=2nd row
BankTransactionTestValidationRule: Date=10/6/2020 12:00:00 AM
BankTransactionTestValidationRule: PayeeName=PenFed
BankTransactionTestValidationRule: Status=V
>> BankTransactionTestValidationRule: Memo=give me some memo
BankTransactionTestValidationRule: 2 is dirty
BankTransactionTestValidationRule: ValidationStep=CommittedValue
BankTransactionTestValidationRule: Reference=2nd row
BankTransactionTestValidationRule: Date=10/6/2020 12:00:00 AM
BankTransactionTestValidationRule: PayeeName=PenFed
BankTransactionTestValidationRule: Status=V
>> BankTransactionTestValidationRule: Memo=give me some memo
BankTransactionTestValidationRule: 2 is dirty
[我将其发布为答案只是因为它提供了所需的行为。但是,我仍然希望其他人有正确的答案。]
我最终在DataGrid
中添加了一个隐藏列,该隐藏列绑定到HiddenPayee
的行视图模型中的新属性ItemSource
。通过将其设置为等于ValidationRule
,可以在PayeeName
中更新此[IsDirty
属性(带有私有集),当它们更改except绑定到true
的PayeeName
属性时,该属性由所有其他属性设置为DataGridComboBox
。见下文...
<DataGrid.RowValidationRules>
<valid:BankTransactionValidationRule ValidationStep="UpdatedValue" />
</DataGrid.RowValidationRules>
<DataGrid.Columns>
...
<DataGridComboBoxColumn x:Name="BankTransPayee" Header="Payee"
ItemsSource="{Binding Source={StaticResource PayeesView}}" DisplayMemberPath="Name"
SelectedValueBinding="{Binding PayeeName}" SelectedValuePath="Name" >
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="ComboBox">
<Setter Property="IsEditable" Value="True" />
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>
<DataGridTextColumn x:Name="BankTransHiddenPayee" Binding="{Binding HiddenPayee}" Visibility="Hidden" />
...
</DataGrid.Columns>
银行交易更改
public class BankTransaction : INotifyPropertyChanged, ICloneable { private bool loading = false; // Default constructor is required by DataGrid in order to add new empty row public BankTransaction() { IsDirty = false; Subtransactions.CollectionChanged += Subtransactions_CollectionChanged; } public BankTransaction(int accountId, int transactionId, DateTime date, string memo, TransactionStatus status, string payee, string reference) : this() { loading = true; // For reading from the database AccountId = accountId; TransactionId = transactionId; Date = date; Memo = memo; Status = status; PayeeName = payee; HiddenPayee = payee; Reference = reference; loading = false; } public bool IsDirty { get; private set; } private string hiddenPayee; public string HiddenPayee { get => hiddenPayee; set { if (hiddenPayee != value) { hiddenPayee = value; NotifyPropertyChanged(); IsDirty = !loading; } } } private string payeeName = ""; public string PayeeName { get => payeeName; set { payeeName = value; NotifyPropertyChanged(); // Don't set IsDirty here, it is handled // in HiddenPayee } } private string reference = ""; public string Reference { get => reference; set { reference = value; NotifyPropertyChanged(); IsDirty = !loading; } } ... }
ValidationRule更改
public class BankTransactionValidationRule : ValidationRule { public override ValidationResult Validate(object value, CultureInfo cultureInfo) { var bg = (BindingGroup)value; var row = (DataGridRow)bg.Owner; var item = (BankTransaction)row.Item; // This, because a change in the DataGridComboBoxColumn immediately changes the value // and it does not set bg.IsDirty. So, forced to manually handle this in the item item.HiddenPayee = item.PayeeName; if (item.IsDirty) { System.Diagnostics.Debug.WriteLine($"{nameof(BankTransactionValidationRule)}: {item.TransactionId} is dirty"); if (row.IsNewItem) { item.Insert(); } else { item.Update(); } } else { System.Diagnostics.Debug.WriteLine($"{nameof(BankTransactionValidationRule)}: {item.TransactionId} is NOT dirty"); } return ValidationResult.ValidResult; } }