不使用DependencyProperty更新绑定

时间:2011-03-29 12:05:48

标签: c# wpf xaml data-binding dependency-properties

我有很多现有的业务对象,其中包含许多属性和集合,我想在其中绑定用户接口。在这些对象中使​​用DependencyPropertyObservableCollections不是一种选择。据我所知,当我修改这些对象时,我希望有一种机制来在我执行此操作时更新所有UI控件。另外,我也不知道哪些UI控件绑定到这些对象以及哪些属性。

这是我现在尝试做的简化代码:

public class Artikel
{
   public int MyProperty {get;set;}
}

public partial class MainWindow : Window
{
    public Artikel artikel
    {
        get { return (Artikel)GetValue(artikelProperty); }
        set { SetValue(artikelProperty, value); }
    }

    public static readonly DependencyProperty artikelProperty =
        DependencyProperty.Register("artikel", typeof(Artikel), typeof(MainWindow), new UIPropertyMetadata(new Artikel()));

    public MainWindow()
    {
        InitializeComponent();
        test.DataContext = this;
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
         artikel.MyProperty += 1;
         // What can I do at this point to update all bindings?
         // What I know at this point is that control test or some of it's 
         // child controls bind to some property of artikel.
    }
}
<Grid Name="test">
    <TextBlock Text="{Binding Path=artikel.MyProperty}" />
</Grid>

这是,我试图将我的对象打包到DependencyProperty并尝试在此调用UpdateTarget,但没有成功。

如何更新相应的UI控件?

我希望我描述的情况足够好。

3 个答案:

答案 0 :(得分:8)

使用INotifyPropertyChanged是DependencyProperties的一个很好的替代方案。

如果您实现了界面,则可以使用null作为参数引发PropertyChanged事件,以通知UI所有属性都已更改。

答案 1 :(得分:1)

(我假设您不能将INotifyPropertyChanged添加到业务对象中,并且您不希望在MVVM中添加另一个“数据模型视图”包装对象层。)

您可以通过调用BindingExpression.UpdateTarget()手动更新其数据源中的绑定属性。

myTextBlock.GetBindingExpression(TextBlock.TextProperty).UpdateTarget();

要更新控件或窗口上的所有绑定,您可以使用以下内容:

using System.Windows.Media;
...
static void UpdateBindings(this DependencyObject obj)
{
    for (var i=0; i<VisualTreeHelper.GetChildrenCount(obj); ++i)
    {
        var child = VisualTreeHelper.GetChild(obj, i);
        if (child is TextBox)
        {
            var expression = (child as TextBox).GetBindingExpression(TextBox.TextProperty);
            if (expression != null)
            {
                expression.UpdateTarget();
            }
        }
        else if (...) { ... }
        UpdateBindings(child);        
    }
}

如果你绑定了一组不同的属性,而不是像上面那样单独处理它们,你可以将上面的内容与this approach结合起来枚举一个控件上的所有依赖属性,然后从每个属性中获取任何BindingExpression;但这依赖于不会特别有效的反思。

作为脚注,如果要显式写回数据源,还可以使用BindingExpression.UpdateSource()。控件通常会在值变化或失去焦点时执行此操作,但您可以控制此操作并使用{Binding Foo, UpdateSourceTrigger=Explicit}手动执行。

答案 2 :(得分:1)

  

正如我所知,当我修改这些对象时,我希望有一种机制来在我执行此操作时更新所有UI控件。

您会发现处理此问题的最简单和可维护的方法是为要在UI中呈现的每个类实现视图模型类。如果您可以修改基础类,则可能是这样,如果不能,则几乎可以肯定。

您无需为此使用依赖项属性。只有绑定的目标需要依赖属性,也就是说UI中的控件。您的视图模型对象是源;他们只需要实施INotifyPropertyChanged

是的,这意味着您需要构建包含UI中公开的每个属性的属性的类,并且这些类需要包含可观察的子视图模型集合,并且您必须实例化和填充这些类及其在运行时的集合。

这通常不像听起来那么大,而且在你的情况下它可能更少。构建绑定到数据模型的视图模型的传统方法是构建如下属性:

public string Foo
{
   get { return _Model.Foo; }
   set
   {
      if (value != _Model.Foo)
      {
         _Model.Foo = value;
         OnPropertyChanged("Foo");
      }
   }
}

但是,正如您所声称的,您知道何时更新对象,并且您只想将更新推送到UI,您可以实现只读属性,以及何时更新基础数据模型使视图模型引发PropertyChanged,并将事件args的PropertyName属性设置为null,这将告诉绑定“此对象上的每个属性都已更改;更新所有绑定目标。”