【问题标题】:Change property in PropertyChanged method not updating view在 PropertyChanged 方法中更改属性不更新视图
【发布时间】:2023-03-23 14:54:01
【问题描述】:

在某些情况下,如果用户在组合框中选择了一个项目,它必须自动更改为另一个项目

视图模型

public class VM : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string property)
    {
        if (PropertyChanged != null)
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(property));
        }
    }

    private string selected;
    public string Selected
    {
        get { return selected; }
        set
        {
            if (selected != value)
            {
                selected = value;
                OnPropertyChanged("Selected");
            }
        }
    }

    private ObservableCollection<string> collection;
    public ObservableCollection<string> Collection
    {
        get { return collection; }
        set
        {
            collection = value;
            OnPropertyChanged("Collection");
        }
    }

    public VM()
    {
        this.Collection = new ObservableCollection<string>(new string[] { "A", "B", "C" });
        this.Selected = "A";
        this.PropertyChanged += VM_PropertyChanged;
    }

    void VM_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        this.Selected = "C";
    }
}

查看

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
<StackPanel>
    <Grid>
      <ComboBox ItemsSource="{Binding Collection}" SelectedValue="{Binding Selected}"/>
    </Grid>
    <Label Content="{Binding Selected, UpdateSourceTrigger=PropertyChanged}"/>
 </StackPanel>
</Window>

所以,在这个例子中,无论我选择什么,它应该在组合框和标签上都显示“C”,但“C”只显示在标签上,这意味着 ViewModel 已更新但视图未更新.

这里的问题似乎是尝试从 PropertyChanged 方法更改属性。

可能出了什么问题?

【问题讨论】:

  • 您的视图模型使用属性 Collection 而您的视图绑定到 Coleccion 是否只是复制/粘贴错误?
  • 为什么将UpdateSourceTrigger=PropertyChanged 添加到无法更新源的属性的绑定中?为什么您希望您的 ComboBox 将新的 ObservableCollection 分配给 Collection
  • @wablab 是的,原始代码是西班牙语,谢谢
  • 我知道,你是对的,我已经习惯了这样写,已经成为一种不好的做法
  • 不确定您的最终目标是什么,但也许您可以实现并绑定到INotifyPropertyChanging.PropertyChanging,并为此在事件处理程序中设置Selected。这个我没试过,不知道有没有用。实际上,您甚至可能不需要绑定它;您可能能够在视图模型本身内处理所有这些。 (但如果你打算这样做,那么在设置器本身内更改 Selected 的值可能是有意义的。)

标签: c# wpf mvvm combobox propertychanged


【解决方案1】:

这就是我最有可能这样做的方法,但是可以从您的 PropertyChanged 处理程序中轻松调用具有魔力的 BeginInvoke() 调用。

它所做的基本上是在整个属性集业务完全完成后排队执行操作。 DispatcherPriority.ApplicationIdle 标志是一个关键点。

正如您所发现的,PropertyChanged 处理程序和属性设置器在 ComboBox 仍在更改其选择的过程中引发 PropertyChanged 是没有用的。这段代码让整个事情完成,然后立即将Selected 更改为其他内容。届时,ComboBox 将有空留意您的PropertyChanged 事件并更新自己的选择。

private string selected;
public string Selected
{
    get { return selected; }
    set
    {
        if (selected != value)
        {
            //  Don't let them select "B". 
            if (value == "B")
            {
                Dispatcher.CurrentDispatcher.
                    BeginInvoke(new Action(() => this.Selected = "C"),
                                DispatcherPriority.ApplicationIdle);
                return;
            }
            selected = value;
            OnPropertyChanged("Selected");
        }
    }
}

【讨论】:

    【解决方案2】:

    在某些情况下,如果用户在组合框中选择了一个项目,它必须自动更改为另一个项目

    就其价值而言,我认为重新审视该设计选择是个好主意。这可能会让用户感到困惑,并且可能有更好的方式向用户展示这种状态,而不是忽略他们给程序的输入。您的问题中没有足够的上下文来完全理解您最初是如何陷入这种情况的,所以我不能提供任何东西,只能建议修复设计可能更好,而不是让代码做你想做的事.

    也就是说……

    您遇到的问题是 WPF 会忽略它当前已更新的绑定源的属性更改事件。在您的场景中,绑定正在更新其绑定中的 Selected 值,因此在该绑定更新完成之前,对该属性的更改将被忽略。

    有多种方法可以让代码按照您想要的方式工作。可能最简单的方法是简单地推迟源属性的更新,直到用户输入的处理完成。您可以使用Dispatcher.InvokeAsync() 方法来做到这一点:

    void VM_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        Dispatcher.CurrentDispatcher.InvokeAsync(() => this.Selected = "C");
    }
    

    我不是上述内容的忠实拥护者,因为它采用理想情况下应该是与视图无关的对象、视图模型,并注入您特定视图 API 的知识,即使用 @987654326 @ 目的。也就是说,您可以使用其他类似的机制,它们不依赖于 Dispatcher 对象(例如,使用异步计时器)。

    在 Stack Overflow 上还有许多其他解决此问题的方法示例。例如,您可以查看this answer 以获得灵感。我认为它不会完全按照您想要的方式“直接开箱即用”,但附加属性方法可能是您认为更合适的方法,通过将逻辑从视图模型移动到视图代码,从而成为它所在的地方更适合使用Dispatcher。 (并且可以说,如果你要做这样的事情,那么逻辑可能无论如何都属于视图......这很奇怪,但我认为没有令人信服的理由这应该是视图模型中固有的。)

    另一种方法可以在问题Coerce a WPF TextBox not working anymore in .NET 4.0 中看到。 IE。事后手动强制更新视图的状态。

    【讨论】:

    • 只是为了进一步解释一下,我只在特定条件下从组合框中选择特定项目后才显示一个弹出对话框询问用户信息,如果用户取消对话框,则所选项目应该恢复到上一个​​。你的回答很好,我会重新设计逻辑以避免离开正确的设计轨道
    • @Tuco:我明白了。是的,至少如果您向用户展示设置正在恢复的一些通知,这将避免很多潜在的用户混淆。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-03-08
    • 2020-04-08
    • 1970-01-01
    • 2021-04-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多