【问题标题】:Update binding without DependencyProperty更新没有 DependencyProperty 的绑定
【发布时间】:2011-07-25 05:44:03
【问题描述】:

我有很多现有的业务对象,其中有很多属性和集合,我想在其中绑定用户界面。在这些对象中使​​用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 控件?

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

【问题讨论】:

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


    【解决方案1】:

    正如我在修改这些对象时所知道的那样,我希望有一种机制在我这样做时更新所有 UI 控件。

    您会发现处理此问题的最直接和可维护的方法是为您希望在 UI 中呈现的每个类实现视图模型类。如果您可以修改底层类,这可能是正确的,如果您不能修改,则几乎肯定是正确的。

    您不需要为此使用依赖属性。依赖属性仅在绑定的targets 上是必需的,也就是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,这告诉绑定,“此对象上的每个属性都已更改;更新所有绑定目标。”

    【讨论】:

      【解决方案2】:

      (我将假设您也不能将 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} 手动控制此操作。

      【讨论】:

      • 这样做并不是一个好主意,因为您无法访问框架的内部绑定机制,这意味着事情会变得非常复杂。我想这段代码甚至不能正常工作,因为它假定所有内容都是一个几乎没有涵盖所有内容的 TextBox。如果您尝试使该方法完全通用,我认为您最终会得到一个复杂度为 O(n^3) 的算法,这非常疯狂。
      • 不,这是 O(n),其中 n 是控件的数量。 “...”确实是可以添加其他绑定属性的占位符。我并不是说这是一个有品位的解决方案,在理想情况下,数据源对象会在其属性发生变化时通知绑定的控件;但在某些情况下,这并不合理,可能需要更务实的(如果丑陋的)解决方案。现在,如果您有一个不那么难看并且不需要更改数据源或更改数据源的解决方案,我相信 OP 会感兴趣的。
      • 我说的是一个更高的完全通用方法的复杂性,因为每个控件都有一定数量的依赖属性,所以没有适当的 O 表示法,但你需要遍历所有控件和每次迭代都需要迭代控件的所有dps(甚至超出,因为还有附加的dps),O(n ^ 3)将是夸大其词,我猜它更像是O(n ^ 2)。
      • 我想我可以制作一个实现 INotifyPropertyChanged 的​​容器对象。但我也会研究这个 UpdateBindings 函数,也许我可以对所有元素执行此操作,而不仅仅是 TextBox。
      • @H.B.我当然同意您的观点,即最通用的解决方案可能性能不是很好。我已经相应地编辑了答案。但是对此有适当的 O 表示法;它是 O(n),其中 n 是控件的数量,因为 O 表示法是复杂性的指标,而不是性能指标。
      【解决方案3】:

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

      如果您实现了该接口,您可以引发PropertyChanged 事件并将null 作为参数,以通知UI 所有属性都已更改。

      【讨论】:

      • 谢谢,我没想到。我试过了,它适用于简单的属性,但它不适用于具有 的 List 属性。我添加了新的列表项,但 UI 没有显示它们。
      • Collections 需要实现 INotifyCollectionChanged 更改,以便在添加项目时绑定自行更新,ObservableCollection 将是您似乎已经知道的标准实现。
      • 是的,但我无法将 Artikel 中的收藏更改为 ObservableCollection。
      • +1 因为虽然我的回答涵盖了无法修改数据源的情况,但使用 INotifyPropertyChanged 和 INotifyCollectionChanged 肯定更可取。
      • @MTR 我很怀疑,不知道我会如何处理这种情况,但你的类似乎对绑定没有用,所以我可能会创建一个类BindableArtikel,它是它的副本,它实现了所有接口并传输所有数据,如果这在性能方面还不错的话。当然,如果您需要在某个时候再次分发Artikels,则需要转移。
      猜你喜欢
      • 1970-01-01
      • 2017-06-13
      • 2012-10-11
      • 1970-01-01
      • 2013-04-10
      • 2019-09-07
      • 2011-05-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多