假设我有类似的课程:
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
是当前焦点控件的视图模型。
答案 0 :(得分:0)
它受ReactiveUI和DependencyProperty上的手动绑定的启发:
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);
问题
参考ReactiveUI.WPF
和ReactiveUI.Fody
,并像这样修改视图模型
public class ButtonViewModel : ReactiveObject
{
[Reactive]
public ICommand Command { get; set; }
}
然后我们可以像这样绑定两个属性:
container.WhenAnyValue(x => x.CurrentControlModel.Commands.Paste)
.BindTo(pasteButtonViewModel, x => x.Command);
可能存在的问题
DependencyProperty.UnsetValue
)。