在 Winform 中,为啥当我在一个数据源上调用 PropertyChanged 时所有绑定的属性都会更新?

Posted

技术标签:

【中文标题】在 Winform 中,为啥当我在一个数据源上调用 PropertyChanged 时所有绑定的属性都会更新?【英文标题】:In Winform why ALL bound properties are updated when I call PropertyChanged on ONE data source?在 Winform 中,为什么当我在一个数据源上调用 PropertyChanged 时所有绑定的属性都会更新? 【发布时间】:2021-03-10 08:17:21 【问题描述】:

我有两个按钮并将它们的属性绑定到数据对象的两个属性。 但是当我调用数据对象的PropertyChanged 时,每个属性都会更新。

public partial class Form1 : Form

    private DataClass data = new DataClass();

    public Form1()
    
        InitializeComponent();
        ButtonA.DataBindings.Add("Text", data, "DataA");
        ButtonB.DataBindings.Add("Text", data, "DataB");
        ButtonB.Click += new EventHandler(OnButtonBClicked);
    

    private void OnButtonBClicked(object sender, EventArgs e)
    
        data.DataA += "1";
        data.DataB += "1";
        data.Notify("DataB");
    


public class DataClass : INotifyPropertyChanged

    public string DataA  get; set; 
    public string DataB  get; set; 
    public event PropertyChangedEventHandler PropertyChanged;

    public DataClass() 

    public void Notify(string property_name)
    
        PropertyChanged(this, new PropertyChangedEventArgs(property_name));
    

当我按ButtonB(这意味着我呼叫PropertyChanged(this, new PropertyChangedEventArgs("DataB")))时,ButtonAButtonB 都会显示新文本。

如果我调用PropertyChanged(this, new PropertyChangedEventArgs("DataA")),两个按钮都会更新。

如果我不更改DataA / DataB 的值而只是调用PropertyChanged(this, new PropertyChangedEventArgs("DataB")),仍然会更新两个按钮(可以通过断点调试注意到)。

如果我调用PropertyChanged(this, new PropertyChangedEventArgs("QQQ")),则不会更新任何按钮。

PropertyChangedEventArgs 有一个名为propertyName 的属性,我以为它是用来指定一个要通知的属性,但它没有。

在我的真实代码中,DataB 的变化比DataA 更频繁。我不想每次更改DataB 都更新ButtonA,这太耗时了。

问题:为什么会发生这种情况?当数据源属性发生变化时,我如何才能只更新真正连接到它的属性?

(所有代码在 Windows 上都是 .Net Framework 4.7.1。)

【问题讨论】:

你愿意修改你的类结构来实现复杂的数据绑定吗?在这种情况下,您的 string 属性将更改为类对象。简单绑定会导致对所有绑定属性调用 getter,否则所有设置为新值的属性都会在 Binding 对象中引发更改事件。 @Jimi :哦,是的。这是一个好方法。我将每个属性都放在一个对象中,然后一切都按预期工作。thx。 【参考方案1】:

@Jimi的方法行得通。简单有效。我把每个属性都放在一个shell类中,并给shell绑定数据:

    public class MyProperty<T>: INotifyPropertyChanged
    
        public T Content  get; set; 
        public event PropertyChangedEventHandler PropertyChanged;
        public MyProperty(T _content)
        
            Content = _content;
        
        public void Notify()
        
            PropertyChanged(this, new PropertyChangedEventArgs("Content"));
        
    
    public class DataClass
    
        public MyProperty<string> DataA = new MyProperty<string>("");
        public MyProperty<string> DataB = new MyProperty<string>("");
        public DataClass() 
    

但是这样我必须在每个地方都使用DataA.Content+="1"而不是DataA+="1"

我决定使用基类来创建所有的shell。但是我真正的DataClass必须从其他类继承,而C#不支持多继承。所以我必须使用扩展类。

public class BindHandle<T> : INotifyPropertyChanged

    public T Content  get  return (T)parent.GetType().GetProperty(prop_name).GetValue(parent);  
    private object parent;
    private string prop_name;
    public event PropertyChangedEventHandler PropertyChanged;
    public BindHandle(object _parent, string _prop_name)
    
        parent = _parent;
        prop_name = _prop_name;
    
    public void NotifyChange()
    
        PropertyChanged(this, new PropertyChangedEventArgs("Content"));
    

public interface IBindHandleProvider

    BindHandleProvider provider  get; set; 

public class BindHandleProvider

    private Dictionary<string, object> handle_map = new Dictionary<string, object>();
    public BindHandle<T> GetBindHandle<T>(object obj,string property_name)
    
        if (!handle_map.ContainsKey(property_name))
            handle_map.Add(property_name, new BindHandle<T>(obj, property_name));
        return (BindHandle<T>)handle_map[property_name];
    
    public void NotifyChange<T>(string property_name)
    
        if (handle_map.ContainsKey(property_name))
            ((BindHandle<T>)handle_map[property_name]).NotifyChange();
    

public static class BindHandleProviderExtension

    public static void NotifyChange<T>(this IBindHandleProvider obj, string property_name)
    
        obj.provider.NotifyChange<T>(property_name);
    
    public static BindHandle<T> GetBindHandle<T>(this IBindHandleProvider obj, string property_name)
    
        return obj.provider.GetBindHandle<T>(obj,property_name);
    

public class DataClass:IBindHandleProvider

    public BindHandleProvider provider  get; set;  = new BindHandleProvider();
    public string DataA  get; set;  = "";
    public string DataB  get; set;  = "";
    public DataClass() 

然后像这样绑定

ButtonA.DataBindings.Add("Text", data.GetBindHandle<string>("DataA"), "Content");

并通知喜欢

data.NotifyChange<string>("DataB");

这有点复杂,但效果很好。

【讨论】:

以上是关于在 Winform 中,为啥当我在一个数据源上调用 PropertyChanged 时所有绑定的属性都会更新?的主要内容,如果未能解决你的问题,请参考以下文章

成功完成初始自动完成后,如何在 WinForm 文本框上重新启动自动完成?

为啥当我在 grails 上删除一对多关系上的父级时,会在子级上调用 beforeInsert 事件?

关于C# winform调用另一个窗口中的控件的问题

为啥当我在 mysql 中调用 STORED PROCEDURE 时 pdo->lastInsertId() 返回 0?

为啥 pool.map 会删除数据操作?

当我在括号中调用它时,Thymeleaf 不会解析我的应用程序中的片段。这是为啥?