c# - WPF:当 DataGridComboBox SelectedValueBinding 更改时,已触发 ValidationRule 但 IsDirty == false
问题描述
我的 XAML 显示绑定到配置的ObservableCollection
DataGrid RowValidationRule
。每当更改 a 中的值时,DataGridTextColumn
就会触发 ValidationRule 并且IsDirty == true
. 但是,对于一个DataGridComboBoxColumn
ValidationRule 被触发,但IsDirty == false
即使该SelectedValueBinding
属性实际上已被更新。我不明白为什么或应该做些什么不同的DataGridComboBoxColumn
条目才能正确导致IsDirty == true
.
更新
我通过运行ValidationRule
所有 ValidationSteps 发现 Payees ComboBox 的值已经在ValidationStep.RawProposedValue
. 这与在 之前不出现更改的所有其他列不同ValidationStep.UpdatedValue
。即使是状态组合框(对枚举起作用)也不会更改,直到ValidationStep.UpdatedValue
第一步时 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>
验证规则
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
true
PayeeName
DataGridComboBox
XAML 更改
<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;
}
}
...
}
验证规则更改
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;
// A change in a DataGridComboBoxColumn immediately changes the value of the SelectedValue (even at RawProposedValue)
// so use hidden properties that are set here and evaluated in the view model class to set its IsDirty property.
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;
}
}
推荐阅读
- google-cloud-storage - 将 GCS 存储桶备份到存档级 GCS 存储桶
- reactjs - 无法在打包的电子应用程序中加载 svg
- c# - C# gRPC localhost - 启动 gRPC 调用时出错:不知道这样的主机
- pyspark - 如何在 PySpark 数据框中转换 yyyymmddhhmmss
- javascript - 在javascript中从数组中构建唯一的选择选项列表?
- c++ - DLL 纯虚函数
- generics - 如何使用可在编译时检测到的有限长度范围的 GenericArray?
- android - 如何从firebase android中删除数据
- r - 为拆分条形图 ggplot2 着色 x 轴标签
- spring-boot - 如何防止 Spring Boot 在模态中为某些特定字段创建列