如何结合WPF中不同模型的两个属性



让我说我有这样的课程:

public class ParentModel : INotifyPropertyChanged
{
    // INotifyPropertyChanged pattern implemented ...
    public IChildViewModel CurrentControlModel {
        get { ... } set { /* Notify on changes */ } 
    } 
}
public class ChildModelA : INotifyPropertyChanged, IChildViewModel
{
    // INotifyPropertyChanged pattern implemented ...
    public ICommand Command {
        get { ... } set { /* Notify on changes */ } 
    } 
}
public class ChildModelB : INotifyPropertyChanged, IChildViewModel
{
    // INotifyPropertyChanged pattern implemented ...
    public ICommand Command {
        get { ... } set { /* Notify on changes */ } 
    }  
}
public class ButtonViewModel : INotifyPropertyChanged
{
    ICommand Command get { ... } set { /* Notify on changes */ }    
}

我想拥有Command属性以反映parentModelInstance.CurrentControlModel.Command事件的值 CurrentControlModel更改。

我不能修改 ButtonViewModel.Command属性作为属性的代理因为这是所有按钮的视图模型,我不想为每个可能的按钮专业化它。

如果我做

ButtonViewModel viewModel; 
viewModel.Command = parentModelInstance.CurrentControlModel.Command;

它不起作用,因为CurrentControlModel可以更改(例如,在启动时是null)。我可以收听PropertyChanged事件,但是对于模型的所有属性,都会很麻烦。

有任何更轻松,更干净的替代方案吗?

上下文

给出一些上下文,它是动态工具栏代码的一部分,您的按钮可以更改图标,被禁用或更改命令,命令目标等...取决于当前的集中控制(可能是不同类型)的是什么。 CurrentControlModel是当前集中控制的视图模型。

进入约束土地的旅程

第一个解决方案:一个授权它们的助手,并使用视图模型绑定

它的启发是由反应性和手动结合对依赖性的启发:

public static BindableProperty<TProperty> Watch<TInstance, TProperty>(
    this TInstance instance,
    Expression<Func<TInstance, TProperty>> expression, 
    BindingMode mode = BindingMode.TwoWay)
{
    return new BindableProperty<TProperty>(instance, 
        GetPath((MemberExpression)expression.Body), mode);
}
public static void BindTo<TInstance, TProperty>(
    this BindableProperty<TProperty> bindable,
    TInstance instance,
    Expression<Func<TInstance, TProperty>> expression) where TInstance 
    : DependencyObject
{
    var getterBody = expression.Body;
    var propertyInfo = (PropertyInfo)((MemberExpression)getterBody).Member;
   var name = propertyInfo.Name;
    var dependencyPropertyName = name + "Property";
    var fieldInfo = typeof(TInstance).GetField(dependencyPropertyName, 
        BindingFlags.Public | BindingFlags.Static);
    var dependencyProperty = (DependencyProperty)fieldInfo.GetValue(null);
    Binding binding = new Binding();
    binding.Source = bindable.Source;
    binding.Path = new PropertyPath(bindable.Path);
    binding.Mode = bindable.Mode;
    binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
    BindingOperations.SetBinding(instance, dependencyProperty, binding);
}
public class BindableProperty<T>
{
    public object Source { get; }
    public string Path { get; }
    public BindingMode Mode { get; }
    public BindableProperty(object source, string path, BindingMode mode)
    {
        Source = source;
        Path = path;
        Mode = mode;
    }
}

ButtonViewModel必须源自DependencyObject并实现模式对于Command属性

public class ButtonViewModel : DependencyObject
{
    public static readonly DependencyProperty CommandProperty = 
        DependencyProperty.Register("Command", typeof(ICommand), 
        typeof(ButtonViewModel), new PropertyMetadata(default(ICommand)));
    public ICommand Command
    {
        get { return (ICommand) GetValue(CommandProperty); }
        set { SetValue(CommandProperty, value); }
    }
}

然后可以像这样使用它(用于将糊命令绑定到粘贴按钮):

container.Watch(x => x.CurrentControlModel.Commands.Paste)
    .BindTo(pasteButtonViewModel, x => x.Command);

问题

  • 必须为视图模型的所有属性设置依赖关系模式。
  • 反射和表达分析可以提高运行时异常。
  • 如果需要转换,我们必须编写一个代理进行转换和值修改传播的代理。

第二个解决方案:反应性。UI和FODY

引用ReactiveUI.WPFReactiveUI.Fody,并像这样修改视图模型

public class ButtonViewModel : ReactiveObject
{
    [Reactive]
    public ICommand Command { get; set; }
}

然后我们可以绑定这样的两个属性:

container.WhenAnyValue(x => x.CurrentControlModel.Commands.Paste)
    .BindTo(pasteButtonViewModel, x => x.Command);

潜在问题剩下的问题

  • 通过不依赖依赖性Property(显然),存在一个潜在的问题,因为我们无法告诉听众未设置属性(使用DependencyProperty.UnsetValue)。
  • 这是一种绑定的一种方式。

最新更新