强制所有WPF绑定控件通过GetBindingExpression().UpdateTarget()进行更新

4

我有一个WPF应用程序,其中包含约50个控件,这些控件绑定到实现了INotifyPropertyChanged接口的业务对象上的属性。以下是我的业务对象的简要代码片段:

public class MyBusinessObject : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, e);
        }
    }

    // properties begin here
    private string _name;
    public string Name
    {
        get { return _name; }
        set
        {
            if (_name == value)
            {
                return;
            }

            _name = value;
            OnPropertyChanged(new PropertyChangedEventArgs("Name"));
        }
    }

    // constructor, etc. not shown
}

我还有几条验证规则用于验证这些控件中的用户输入。我使用命令绑定来防止用户在存在任何验证错误时保存数据。我的应用程序还包括一个“重置默认值”按钮,显然,它将重置业务对象上所有属性的默认值。除一种情况外,所有操作都正常工作。如果用户向一个或多个控件输入无效数据,然后单击“重置默认值”按钮,则包含无效数据的控件不会总是按照我的预期更新。这是由于我的属性设置器中以下代码引起的:
if (_name == value)
{
    return;
}

这段代码的作用是防止当用户在绑定的UI控件中输入的值与属性已经设置的相同时发生不必要的属性更改通知。例如,我在我的UI中有一个IntegerUpDown控件(这个控件是来自Xceed的Extended WPF Toolkit),我的控件绑定的属性的默认值是10。我的用户从控件中删除该值,我的验证规则被触发,导致验证错误,并适当地更新UI,包括错误装饰器等。这个控件映射到的属性的值还没有改变,所以它仍然设置为10。现在,我的用户点击“重置默认值”按钮,这将导致属性的默认值(10)被重置。然而,属性的值已经设置为10,因此我的setter中的短路逻辑将返回而不是设置属性值。

因此,现在,在我的用户点击“重置默认值”之后,我也强制更新我的绑定目标,如下所示:

this.myIntegerUpDown.GetBindingExpression(Xceed.Wpf.Toolkit.IntegerUpDown.ValueProperty).UpdateTarget();

这解决了我的问题,但只适用于特定控件。有没有简单的方法可以对我绑定的所有控件都这样做,而不必指定每个控件呢?谢谢。

1
为什么不在Reset中调用NotifyPropertyChanged(),并强制更新所有内容,这样做50个控件不应该花费太长时间。 - paparazzo
@Blam - 也是个好主意。感谢您的建议。 - bmt22033
4个回答

7

OnPropertyChanged(new PropertyChangedEventArgs(string.Empty));

这意味着该对象上的所有属性都已更改。


注:该句代码为C#语言中用于触发属性更改事件的语句。

5

你能做以下之一吗?

1)重置 DataContext - 重新创建或重新设置属性

var context = this.DataContext;
this.DataContext = null;
this.DataContext = context;

2) 通过反射循环遍历所有属性,并手动使用相关属性名称调用OnPropertyChanged

var properties = typeof(ViewModel).GetProperties(BindingFlags.Instance | BindingFlags.Public);
foreach (var property in properties)
{
    this.OnPropertyChanged(new PropertyChangedEventArgs(property.Name));
}

感谢您的建议。 - bmt22033

1
您提到了验证和重置值,当然显而易见的是要持久化它。
为什么不在实体上实现IEditableObject接口,该接口具有三个签名方法。BeginEdit()、CancelEdit()和EndEdit()。
这样,您就可以轻松地将实体回滚到任何您想要的状态,或者验证它,最后持久化它。一个很好的例子可以在这里找到。
示例代码
public class Customer : IEditableObject 
{

    struct CustomerData 
    {
        internal string id ;
        internal string firstName ;
        internal string lastName ;
    }

    private CustomersList parent;
    private CustomerData custData; 
    private CustomerData backupData; 
    private bool inTxn = false;

    // Implements IEditableObject 
    void IEditableObject.BeginEdit() 
    {
        Console.WriteLine("Start BeginEdit");
        if (!inTxn) 
        {
            this.backupData = custData;
            inTxn = true;
            Console.WriteLine("BeginEdit - " + this.backupData.lastName);
        }
        Console.WriteLine("End BeginEdit");
    }

    void IEditableObject.CancelEdit() 
    {
        Console.WriteLine("Start CancelEdit");
        if (inTxn) 
        {
            this.custData = backupData;
            inTxn = false;
            Console.WriteLine("CancelEdit - " + this.custData.lastName);
        }
        Console.WriteLine("End CancelEdit");
    }

    void IEditableObject.EndEdit() 
    {
        Console.WriteLine("Start EndEdit" + this.custData.id + this.custData.lastName);
        if (inTxn) 
        {
            backupData = new CustomerData();
            inTxn = false;
            Console.WriteLine("Done EndEdit - " + this.custData.id + this.custData.lastName);
        }
        Console.WriteLine("End EndEdit");
    }

    public Customer(string ID) : base() 
    {
        this.custData = new CustomerData();
        this.custData.id = ID;
        this.custData.firstName = "";
        this.custData.lastName = "";
    }

    public string ID 
    {
        get 
        {
            return this.custData.id;
        }
    }

    public string FirstName 
    {
        get 
        {
            return this.custData.firstName;
        }
        set 
        {
            this.custData.firstName = value;
            this.OnCustomerChanged();
        }
    }

    public string LastName 
    {
        get 
        {
            return this.custData.lastName;
        }
        set 
        {
            this.custData.lastName = value;
            this.OnCustomerChanged();
        }
    }

    internal CustomersList Parent 
    {
        get 
        {
            return parent;
        }
        set 
        {
            parent = value ;
        }
    }

    private void OnCustomerChanged() 
    {
        if (!inTxn && Parent != null) 
        {
            Parent.CustomerChanged(this);
        }
    }

    public override string ToString() 
    {
        StringWriter sb = new StringWriter();
        sb.Write(this.FirstName);
        sb.Write(" ");
        sb.Write(this.LastName);
        return sb.ToString();
    }   
}

0

无论是否相同,总是调用OnPropertyChanged会不会更容易?这样做能给你带来多少性能提升?


肯定会更容易。 :-) 说实话,我不确定在setter中使用短路逻辑能带来多少性能提升,但在编写代码时似乎是个好主意。直到我遇到这个问题为止... - bmt22033
我可能会先弄清楚你从中获得了多少优势。我想可能不是很多。 - TylerD87

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接