Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

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!

like image 686
l33t Avatar asked Feb 28 '13 10:02

l33t


3 Answers

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.

like image 73
Grx70 Avatar answered Oct 07 '22 23:10

Grx70


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.

like image 31
Michael Schnerring Avatar answered Oct 07 '22 23:10

Michael Schnerring


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>
like image 41
somebody Avatar answered Oct 08 '22 00:10

somebody