在代码中实现XAML绑定效果

在开发应用程序时,经常需要在视图模型(ViewModel)和视图(View)之间建立数据绑定,以实现数据的实时更新和交互。XAML 绑定为提供了一种简洁的方式来实现这一点。但是,当需要在代码中实现类似的绑定效果时,就需要一些额外的工作。本文将介绍如何在代码中实现类似于 XAML 绑定的效果,以便在数据模型发生变化时自动更新视图模型。

为什么需要在代码中实现绑定效果

在遵循MVVM设计模式开发应用程序时,通常会创建视图模型来处理业务逻辑和数据,然后将这些视图模型与 XAML 视图进行绑定。然而,在某些情况下,视图模型本身也需要与其他数据源建立绑定关系。例如,当视图由子视图组成时,子视图模型的变更需要通知父视图模型;或者,数据模型在更新时,需要通知视图模型进行相应的更新。在这些情况下,需要在代码中实现类似于 XAML 绑定的效果。

如何在代码中实现绑定效果

要在代码中实现绑定效果,关键在于使用 Binding 对象。Binding 对象允许以编程方式创建绑定,其行为与 XAML 绑定相同。首先,需要一个工具来从 lambda 表达式生成属性路径。以下是一个名为 RootedPropertyPath 的工具类,它可以从 lambda 表达式生成属性路径,并创建对应的 Binding 对象。

public class RootedPropertyPath<T> { public Object Target { get; set; } public String Path { get; set; } public Binding ToBinding(BindingMode mode = BindingMode.TwoWay) { return new Binding(Path) { Source = Target, Mode = mode }; } public static implicit operator System.Windows.PropertyPath(RootedPropertyPath<T> self) { return new System.Windows.PropertyPath(self.Path); } public static implicit operator String(RootedPropertyPath<T> self) { return self.Path; } } public static class RootedPropertyPath { public static RootedPropertyPath<T> Create<T>(Expression<Func<T>> expr) { Expression currentExpression = expr.Body; List<String> lst = new List<String>(); ConstantExpression ce; while (true) { ce = currentExpression as ConstantExpression; var me = currentExpression as MemberExpression; if (ce != null) break; if (me == null) throw new Exception(String.Format("Unexpected expression type {0} in lambda.", expr.GetType())); lst.Add(me.Member.Name); currentExpression = me.Expression; } lst.Reverse(); return new RootedPropertyPath<T>() { Path = String.Join(".", lst), Target = ce.Value }; } }

RootedPropertyPath<T> 类不仅可以生成属性路径,还可以收集绑定的“根”对象,即表达式的起始对象。接下来,需要一个工具来将生成的 Binding 对象绑定到 DependencyProperty 上。以下是一个名为 ObservableDependencyValue 的工具类,它可以创建一个可以绑定任意 Binding 并报告变更的 DependencyProperty。

public static class ObservableDependencyValue { public static ObservableDependencyValue<T> Create<T>(Expression<Func<T>> expr, BindingMode mode = BindingMode.OneWay) { return new ObservableDependencyValue<T>().Bind(expr, mode); } } public class ObservableDependencyValue<T> : DependencyObject { #region T ObservableDependencyValue<T>.Value = default(T) #region Boilerplate public T Value { get { return (T)GetValue(ValueProperty); } set { SetValue(ValueProperty, value); } } public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(T), typeof(ObservableDependencyValue<T>), new PropertyMetadata(default(T), StaticHandleValueChanged)); static void StaticHandleValueChanged(DependencyObject self, DependencyPropertyChangedEventArgs args) { ((ObservableDependencyValue<T>)self).HandleValueChanged((T)args.OldValue, (T)args.NewValue); } #endregion void HandleValueChanged(T oldValue, T value) { Notify(); } #endregion public ObservableDependencyValue() { } public ObservableDependencyValue(BindingBase bindingBase) { Bind(bindingBase); } public ObservableDependencyValue<T> Bind(BindingBase bindingBase) { BindingOperations.SetBinding(this, ValueProperty, bindingBase); return this; } public ObservableDependencyValue<T> Bind(Expression<Func<T>> expr, BindingMode mode = BindingMode.OneWay) { var path = RootedPropertyPath.Create(expr); return Bind(new Binding(path.Path) { Source = path.Target, Mode = mode }); } public void Notify() { if (ValueChanged != null) ValueChanged(Value); } public event Action<T> ValueChanged; }

现在已经拥有了创建绑定和处理变更通知的工具。接下来,可以在视图模型中使用这些工具来实现数据绑定。以下是一个示例:

public class PersonViewModel : OurViewModelBase { public PersonViewModel() { var observableDependencyProperty = ObservableDependencyValue.Create(() => this.Person.Employer.Address); observableDependencyProperty.ValueChanged += address => UpdateAddress(address); observableDependencyPropertyReference = observableDependencyProperty; } Object observableDependencyPropertyReference; }
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485