Twoway-bind view DependencyProperty для свойства viewmodel?
Несколько источников в сети сообщают нам, что в MVVM
связь/синхронизация между представлениями и режимами просмотра должна происходить через свойства зависимостей. Если я правильно понимаю это, свойство зависимостей представления должно быть привязано к свойству viewmodel с использованием двусторонней привязки. Теперь аналогичные вопросы задавали раньше, но без достаточного ответа.
Прежде чем приступить к анализу этой довольно сложной проблемы, вот мой вопрос:
Как синхронизировать пользовательский вид DependencyProperty
с свойством viewmodel?
В идеальном мире вы просто привязываете его как это:
<UserControl x:Class="MyModule.MyView" MyProperty="{Binding MyProperty}">
Это не работает, поскольку MyProperty
не является членом UserControl
. Doh! Я пробовал разные подходы, но никто не добился успеха.
Одним из решений является определение базового класса UserControlEx
с необходимыми свойствами зависимостей, чтобы заставить выше работать. Однако это скоро становится крайне беспорядочным. Не достаточно!
Ответы
Ответ 1
Я использую Caliburn.Micro для разделения ViewModel из представления. Тем не менее, это может работать в MVVM одинаково. Я полагаю, MVVM задает свойство view DataContext
экземпляру ViewModel.
VIEW
// in the class of the view: MyView
public string ViewModelString // the property which stays in sync with VM property
{
get { return (string)GetValue(ViewModelStringProperty); }
set
{
var oldValue = (string) GetValue(ViewModelStringProperty);
if (oldValue != value) SetValue(ViewModelStringProperty, value);
}
}
public static readonly DependencyProperty ViewModelStringProperty =
DependencyProperty.Register(
"ViewModelString",
typeof(string),
typeof(MyView),
new PropertyMetadata(OnStringValueChanged)
);
private static void OnStringValueChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
// do some custom stuff, if needed
// if not, just pass null instead of a delegate
}
public MyView()
{
InitializeComponent();
// This is the binding, which binds the property of the VM
// to your dep. property.
// My convention is give my property wrapper in the view the same
// name as the property in the VM has.
var nameOfPropertyInVm = "ViewModelString"
var binding = new Binding(nameOfPropertyInVm) { Mode = BindingMode.TwoWay };
this.SetBinding(SearchStringProperty, binding);
}
VM
// in the class of the ViewModel: MyViewModel
public string ViewModelStringProperty { get; set; }
Обратите внимание, что этот вид реализации полностью не реализует интерфейс INotifyPropertyChanged
. Вам нужно будет правильно обновить этот код.
Ответ 2
Если вы хотите сделать это в XAML, вы можете попробовать использовать стили для достижения этого.
Вот пример:
<UserControl x:Class="MyModule.MyView"
xmlns:local="clr-namespace:MyModule">
<UserControl.Resources>
<Style TargetType="local:MyView">
<Setter Property="MyViewProperty" Value="{Binding MyViewModelProperty, Mode=TwoWay}"/>
</Style>
</UserControl.Resources>
<!-- content -->
</UserControl>
В вашем случае оба MyViewProperty
и MyViewModelProperty
будут называться MyProperty
, но я использовал разные имена, чтобы быть ясно, что именно.
Ответ 3
Допустим, вы определили свой DependencyProperty "DepProp" в представлении и хотите использовать точно такое же значение в вашей ViewModel (которое реализует INotifyPropertyChanged, но не DependencyObject). Вы должны иметь возможность сделать следующее в своем XAML:
<UserControl x:Class="MyModule.MyView"
xmlns:local="clr-namespace:MyModule"
x:Name="Parent">
<Grid>
<Grid.DataContext>
<local:MyViewModel DepProp="{Binding ElementName=Parent, Path=DepProp}"/>
</Grid.DataContext>
...
</Grid>
</UserControl>