【问题标题】:WPF TwoWay Binding and PropertyChangedWPF 双向绑定和 PropertyChanged
【发布时间】:2017-12-01 16:06:58
【问题描述】:

所以假设有一个枚举

    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 显示的是我手动选择的值,而不是访问器返回的值。

例如如果您重写 get =&gt; SampleEnum.Item2 以始终返回相同的值,ComboBox 仍将显示我在 UI 中选择的值,而不是访问器返回的值,即使我 100% 确定调用了 getter,比值传播到 Converter 和 Convrter 也返回正确的值。

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

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

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

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

【问题讨论】:

    标签: c# wpf binding propertychanged


    【解决方案1】:

    tl;dr:您要做的是数据验证。这是一个已解决的问题:您可以在您的视图模型中使用IDataErrorInfo 或在您的视图中使用ValidationRules 实现验证。这些中的任何一个都 WPF一起工作,而不是反对它。与 WPF 对抗几乎总是会失败。

    您还可以为 ComboBox 编写一个 ItemContainerStyle 来禁用无效项,或者您的视图模型可以更新 ComboBox 项集合以排除当前不可选择的任何项。我更喜欢这种方法:与其说“在这里,你可以选择这些选项中的任何一个——BZZZT、LOL、错误的选择!”,而是只向他们展示他们可以选择的选项似乎更友好。

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


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

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

    标准的解决方法是使用ApplicationIdle 优先级调用 BeginInvoke(),并在委托中引发 PropertyChanged。在 ComboBox 完全完成当前的选择更改事件后,这将具有再次引发 PropertyChanged 的​​效果。但这不是视图模型应该关心的。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-09-24
      • 2013-08-13
      • 2011-07-29
      • 2015-07-10
      • 2015-05-21
      • 1970-01-01
      • 2020-12-04
      相关资源
      最近更新 更多