Twoway-bind view's DependencyProperty to viewmodel's property?

20,293

Solution 1

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.

VIEW

// 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; }

Note, that this kind of implementation lacks completely of implementation of the INotifyPropertyChanged interface. You'd need to update this code properly.

Solution 2

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.

Solution 3

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>
Share:
20,293
l33t
Author by

l33t

Updated on May 12, 2020

Comments

  • l33t
    l33t about 4 years

    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!