c# - ¿DependencyProperty de Twoway-bind view a la propiedad viewmodel?
wpf mvvm (3)
Múltiples fuentes en la red nos dicen que, en MVVM
, la comunicación / sincronización entre vistas y modelos de vista debería ocurrir a través de propiedades de dependencia. Si entiendo esto correctamente, una propiedad de dependencia de la vista debe vincularse a una propiedad del modelo de vista mediante enlace bidireccional. Ahora, preguntas similares se han hecho antes, pero sin una respuesta suficiente.
Antes de comenzar a analizar este problema bastante complejo, esta es mi pregunta:
¿Cómo sincronizo DependencyProperty
de una vista personalizada con una propiedad del viewmodel?
En un mundo ideal, simplemente lo vinculas así:
<UserControl x:Class="MyModule.MyView" MyProperty="{Binding MyProperty}">
Eso no funciona porque MyProperty
no es miembro de UserControl
. Doh! He intentado diferentes enfoques, pero ninguno resultó exitoso.
Una solución es definir una clase base, UserControlEx
, con las propiedades de dependencia necesarias para que funcione lo anterior. Sin embargo, esto pronto se vuelve extremadamente desordenado. ¡No es suficiente!
Si quieres hacerlo en XAML, podrías intentar usar estilos para lograrlo.
Aquí hay un ejemplo:
<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>
En su caso, tanto MyViewProperty
como MyViewModelProperty
se llamarían MyProperty
pero utilicé diferentes nombres solo para aclarar qué es qué.
Supongamos que ha definido su propiedad DependencyProperty "DepProp" en la Vista y desea utilizar exactamente el mismo valor en su ViewModel (que implementa INotifyPropertyChanged pero no DependencyObject). Debería poder hacer lo siguiente en su 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>
Yo uso Caliburn.Micro para separar el ViewModel de la Vista. Aún así, podría funcionar de la misma manera en MVVM. Supongo que MVVM también establece la propiedad DataContext
la vista en la instancia de ViewModel.
VER
// in the class of the view: MyView
public string ViewModelString // the property which stays in sync with VM''s 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; }
Tenga en cuenta que este tipo de implementación carece por completo de implementación de la interfaz INotifyPropertyChanged
. Necesitarías actualizar este código correctamente.