[Я публикую это как ответ только потому, что он обеспечивает желаемое поведение. Тем не менее, я все еще надеюсь, что кто-то другой имеет правильный ответ.]
Я закончил тем, что добавил к 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>
Изменение BankTransaction
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;
// 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;
}
}