简体   繁体   English

WPF双向绑定和PropertyChanged

[英]WPF TwoWay Binding and PropertyChanged

So suppose there is an enum 所以假设有一个枚举

    enum SampleEnum 
    {
        Item1,
        Item2
    }

Then there is a ComboBox 然后有一个组合框

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

Combo box has a ViewModel as its DataContext 组合框具有ViewModel作为其DataContext

    ViewModel : INotifyPropertyChanged, ...
    {
        ...

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

        ...
    }

And RaisePropertyChanged([CallerMemberName] string caller = "") invokes PropertyChanged with property's name. RaisePropertyChanged([CallerMemberName] string caller = "")调用带有属性名称的PropertyChanged

But . 但是

When I run my code, open my CheckBox , select one item, I get following behaviour: my code enters setter, sets value of the model, raises PropertyChanged, then my getter is invoked, value is retrieved, but it never reaches ComboBox . 当我运行代码时,打开我的CheckBox ,选择一项,我得到以下行为:我的代码进入setter,设置模型的值,引发PropertyChanged,然后调用我的getter,获取值, 但从未到达 ComboBox ComboBox displays the value I chose by hand, not the value returned by accessor. ComboBox显示我手动选择的值,而不是访问器返回的值。

Eg if you rewrite get => SampleEnum.Item2 to return always the same value, ComboBox will still display the value I picked by my hand in UI, not the one that is returned by accessor, even though I am 100% sure that getter is invoked, than the value travels to Converter and Convrter also returns proper value. 例如,如果您重写get => SampleEnum.Item2以始终返回相同的值,即使我100%确信getter是, ComboBox仍会显示我在UI中用我的手挑选的值,而不是访问器返回的值。调用该值之前,该值将传递到Converter和Convrter也会返回正确的值。

But if RaisePropertyChanged(nameof(ItemX)) is invoked from any other place, ComboBox immediately retrieves the value from accessor and displays it. 但是,如果从任何其他位置调用RaisePropertyChanged(nameof(ItemX)) ,则ComboBox立即从访问器中检索值并显示它。

In short, ComboBox ignores PropertyChanged if invoked from setter, but in any other case it works perfectly fine. 简而言之,如果从setter调用ComboBox则将忽略PropertyChanged ,但是在任何其他情况下,它都可以正常工作。 Specifying directly the name of property (instead of relying on compiler services) or invoking several RasiePropertyChanged in a row in setter does not work. 直接指定属性名称(而不是依赖编译器服务)或在setter的一行中调用多个RasiePropertyChanged无效。

In general, one should expect that the value selected in combo box and the one returned by getter are the same, but sometimes model can reject provided value and instead return a default one. 通常,应该期望在组合框中选择的值与由getter返回的值相同,但是有时模型可以拒绝提供的值,而是返回默认值。 Not the best behaviour, but it is possible. 不是最好的行为,但有可能。 And in this case the user will be misinformed which item of a ComboBox is actually selected. 在这种情况下,用户将被错误ComboBox实际选择了ComboBox哪个项目。

I am just wondering what is so special about this accessor that ComboBox ignores it. 我只是想知道此访问器有什么特别之处,以至于ComboBox会忽略它。

tl;dr: What you're trying to do is data validation. tl; dr:您要尝试做的是数据验证。 That's a solved problem: You can implement validation in your viewmodel with IDataErrorInfo , or in your view with ValidationRules . 这是一个已解决的问题:您可以使用IDataErrorInfo在视图模型中或通过ValidationRules在视图中实现验证 Either one of those works with WPF instead of against it. 其中任何一种都可以 WPF 配合使用,而不是与之相反。 Working against WPF is almost invariably losing proposition. 与WPF对抗几乎总是会失去命题。

You could also write an ItemContainerStyle for the ComboBox that disables invalid items, or your viewmodel could update the ComboBox item collection to exclude any items that are currently unselectable. 您也可以为ComboBox编写一个ItemContainerStyle来禁用无效的项,或者您的视图模型可以更新ComboBox项集合以排除当前无法选择的任何项。 I prefer that approach: Rather than "here, you can choose any of these options -- BZZZT, LOL, WRONG CHOICE!", it seems friendlier to present them only with the options that they can choose. 我更喜欢这种方法:与其说“您可以在这里选择任何一个选项-BZZZT,LOL和WRONG CHOICE!”,不如说只为他们提供可以选择的选项,这似乎更友好。

And if you'll know which options are valid after they make their choice, you can almost certainly know beforehand as well. 而且,如果您在选择了哪些选项后就知道它们是有效的,那么几乎可以肯定地事先知道。


ComboBox ignores PropertyChanged if invoked from setter, but in any other case it works perfectly fine. 如果从setter调用,则ComboBox会忽略PropertyChanged,但在其他任何情况下,它都可以正常工作。

That's correct. 没错 The ComboBox is still handling the change it got from the user, and won't be finished doing so until some time after your setter is complete. ComboBox仍在处理它从用户那里获得的更改,并且直到您的设置员完成后一段时间才能完成。 The ComboBox will ignore any PropertyChanged events on a property it's currently updating. ComboBox将忽略当前正在更新的属性上的所有PropertyChanged事件。 This is by design. 这是设计使然。

The standard workaround is to call BeginInvoke() with ApplicationIdle priority, and raise PropertyChanged in the delegate. 标准的解决方法是使用ApplicationIdle优先级调用BeginInvoke(),并在委托中引发PropertyChanged。 That will have the effect of raising PropertyChanged again after the ComboBox is entirely finished with the current selection-change event. 在ComboBox完全完成当前的select-change事件后,将具有再次提高PropertyChanged的效果。 But that's nothing a viewmodel should ever be concerning itself with. 但这绝不是视图模型应该关注的问题。

As you say, "Not the best behaviour" . 如您所说, “不是最好的行为” It would be preferable to write validation which rejects the wrong value in the first place, rather than writing a strange workaround like the above. 最好写一个首先拒绝错误值的验证,而不是像上面那样编写一个奇怪的解决方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM