WPF TwoWay绑定和PropertyChanged

时间:2017-12-01 16:06:58

标签: c# wpf binding propertychanged

假设有一个枚举

    enum SampleEnum 
    {
        Item1,
        Item2
    }

然后有一个ComboBox

    <ComboBox ItemsSource="{Binding SomeItemSource}"    
        SelectedItem="{Binding 
            Path=ItemX,
            Mode=TwoWay,
            UpdateSourceTrigger=PropertyChanged,
            Converter={StaticResource ResourceKey=SomeConverter}}">

组合框的ViewModel为DataContext

    ViewModel : INotifyPropertyChanged, ...
    {
        ...

        public SampleEnum ItemX
        {
            get => model.GetItemX();
            set
            {
                model.SetItemX(value);
                RaisePropertyChanged();
            }
        }

        ...
    }

RaisePropertyChanged([CallerMemberName] string caller = "")使用属性的名称调用PropertyChanged

但是

当我运行我的代码时,打开我的CheckBox,选择一个项目,我得到以下行为:我的代码进入setter,设置模型的值,引发PropertyChanged,然后调用我的getter,检索值, 但它永远不会达到 ComboBoxComboBox显示我手动选择的值,而不是访问者返回的值。

E.g。如果您重写get => SampleEnum.Item2以返回始终相同的值,ComboBox仍将显示我在UI中选择的值,而不是访问者返回的值,即使我100%确定调用getter,而不是传递给Converter和Convrter的值也返回正确的值。

但是如果从任何其他地方调用RaisePropertyChanged(nameof(ItemX))ComboBox会立即从访问者检索该值并显示它。

简而言之,ComboBox如果从setter中调用,则会忽略PropertyChanged,但在任何其他情况下,它都可以正常工作。直接指定属性的名称(而不是依赖于编译器服务)或在setter中连续调用多个RasiePropertyChanged不起作用。

通常,人们应该期望在组合框中选择的值和getter返回的值是相同的,但有时模型可以拒绝提供的值,而是返回默认值。不是最好的行为,但它是可能的。在这种情况下,用户将被误导为实际选择ComboBox的哪个项目。

我只是想知道ComboBox忽略它的这个访问器有什么特别之处。

1 个答案:

答案 0 :(得分:4)

tl; dr:您要做的是数据验证。这是一个已解决的问题:您可以使用IDataErrorInfo在视图模型中实现验证,或者在ValidationRules的视图中实现验证。其中一个使用 WPF而不是反对它。与WPF合作几乎总是失去主张。

您还可以为ComboBox编写一个禁用无效项的ItemContainerStyle,或者您的viewmodel可以更新ComboBox项集合以排除当前无法选择的任何项。我更喜欢这种方法:而不是“在这里,你可以选择任何这些选项 - BZZZT,LOL,错误的选择!”,只有用可以选择的选项呈现它们似乎更友好。

如果您在做出选择后知道哪些选项有效,那么您几乎肯定也可以事先知道。

  

如果从setter调用,ComboBox会忽略PropertyChanged,但在任何其他情况下,它都可以正常工作。

这是对的。 ComboBox仍在处理从用户那里获得的更改,并且在您的setter完成后的一段时间内才会完成。 ComboBox将忽略它当前正在更新的属性上的任何PropertyChanged事件。这是设计的。

标准解决方法是以ApplicationIdle优先级调用BeginInvoke(),并在委托中引发PropertyChanged。在ComboBox完全使用当前选择更改事件完成后,这将产生再次提升PropertyChanged的效果。但这并不是一个视角模特应该关注的事情。

正如你所说,“不是最好的行为”。最好先编写拒绝错误值的验证,而不是像上面那样编写一个奇怪的解决方法。