Скажем, у меня есть такие классы:
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 — это модель представления текущего сфокусированного элемента управления.
«Любая более простая и чистая альтернатива?». Возможно, вы захотите изучить реактивную структуру, такую как Реактивный интерфейс: stackoverflow.com/a/22215041/7252182.
@EpicKip Я не могу этого сделать, потому что ButtonViewModel используется в качестве динамической модели представления для DataTemplate с ItemTemplateSelector в зависимости от его типа.
Зачем вообще существует модель просмотра для кнопки? Рассматривали ли вы возможность использования селектора шаблонов данных для выбора шаблона на основе свойства, а не типа? Если вы нажмете кнопку, вы хотите вызвать команду в зависимости от текущей модели? Просто привязка команды к currentmodel.Command (кстати, ужасное имя) кажется очевидным. Затем заставьте все, что вы делаете, работать с этим.





Он был вдохновлен 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).
Либо получите доступ к родительской виртуальной машине в xaml через относительный источник, либо просто напрямую привяжите
parentModelInstance.CurrentModel.Commandи уведомите об изменениях.