Multiple sources on the net tells us that, in MVVM
, communication/synchronization between views and viewmodels should happen through dependency properties. If I understand this correctly, a dependency property of the view should be bound to a property of the viewmodel using two-way binding. Now, similar questions have been asked before, but with no sufficient answer.
Before I start analyzing this rather complex problem, here's my question:
How do I synchronize a custom view's DependencyProperty
with a property of the viewmodel?
In an ideal world, you would simply bind it as this:
<UserControl x:Class="MyModule.MyView" MyProperty="{Binding MyProperty}">
That does not work since MyProperty
is not a member of UserControl
. Doh! I have tried different approaches, but none proved successful.
One solution is to define a base-class, UserControlEx
, with necessary dependency properties to get the above to work. However, this soon becomes extremely messy. Not good enough!
If you want to do it in XAML, you could try using styles to achieve that.
Here's an example:
<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>
In your case both MyViewProperty
and MyViewModelProperty
would be named MyProperty
but I used different names just to be clear about what is what.
I use Caliburn.Micro for separating the ViewModel from the View. Still, it might work the same way in MVVM. I guess MVVM sets the view's DataContext
property to the instance of the ViewModel, either.
// 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);
}
// in the class of the ViewModel: MyViewModel
public string ViewModelStringProperty { get; set; }
Note, that this kind of implementation lacks completely of implementation of the INotifyPropertyChanged
interface. You'd need to update this code properly.
Lets say you have defined your DependencyProperty "DepProp" in the View and want to use the exactly same value in your ViewModel (which implements INotifyPropertyChanged but not DependencyObject). You should be able to do the following in your 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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With