WPF: ValidationRule сработал, но IsDirty == false при изменении DataGridComboBox SelectedValueBinding - PullRequest
1 голос
/ 25 апреля 2020

Мой XAML отображает сетку данных, привязанную к ObservableCollection с настроенным RowValidationRule. Всякий раз, когда значение в DataGridTextColumn изменяется, запускается ValidationRule и IsDirty == true. Однако для DataGridComboBoxColumn ValidationRule срабатывает, но IsDirty == false, хотя свойство SelectedValueBinding действительно обновлено. Я не понимаю, почему или что я должен сделать по-другому для DataGridComboBoxColumn записей, чтобы правильно вызвать IsDirty == true.

ОБНОВЛЕНИЕ

Я нашел, запустив ValidationRule на всех этапах ValidationSteps, что значение поля со списком получателей уже изменилось в ValidationStep.RawProposedValue. Это отличается от всех других столбцов, где изменения не появляются до ValidationStep.UpdatedValue. Даже ComboBox Status (который работает с Перечислением) не изменяется до 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>

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

1 Ответ

0 голосов
/ 26 апреля 2020

[Я публикую это как ответ только потому, что он обеспечивает желаемое поведение. Тем не менее, я все еще надеюсь, что кто-то другой имеет правильный ответ.]

Я закончил тем, что добавил к 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;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...