2

Does anybody know how I can synchronize my properties, which are in a ViewModel, with my Dependency Properties, which are in the View?

I am trying to make a UserControl, which will then be hosted by a WPF-Window (MainWindow.xaml). The UserControl has an own ViewModel which contains ICommands and properties.

The problem is, that I also have to return certain properties to the MainWindow(.xaml) and also set them.

Currently my classes are looking like that:

MainWindow.xaml

<TextBox Name="tbInput" VerticalAlignment="Top" HorizontalAlignment="Stretch" Grid.Row="0"></TextBox>
    <local:View x:Name="appEntryView" Pfad="{Binding ElementName=tbInput, Path=Text}" Grid.Row="1" Margin="10"/>

View.xaml

<UserControl x:Class="DependencyProperties.Test"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:local="clr-namespace:DependencyProperties_Intro"
         x:Name="obj"
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">

<Grid>
    <TextBlock Text="{Binding ElementName=obj, Path=Pfad}"/>
</Grid>

View.xaml.cs

public partial class View: UserControl, INotifyPropertyChanged
{

    public String Pfad
    {
        get { return (String)GetValue(PfadProperty); }
        set { SetValue(PfadProperty, value); OnNotifyPropertyChanged("Pfad"); }
    }

    // Using a DependencyProperty as the backing store for Path.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty PfadProperty =
        DependencyProperty.Register("Pfad", typeof(String), typeof(GraphSharpTest), new PropertyMetadata(default(string)));

    public View()
    {
        InitializeComponent();
        this.DataContext = new ViewModel();
        var name = "Pfad";
        var binding = new Binding(name) { Mode = BindingMode.TwoWay };
        this.SetBinding(PfadProperty, binding);
    }
}

ViewModel.cs

public class ViewModel: INotifyPropertyChanged
{
    private String m_Pfad;

    public String Pfad
    {
        get { return m_Pfad; }
        set { m_Pfad = value; OnNotifyPropertyChanged("Pfad"); }
    }


    public void OnNotifyPropertyChanged(String info)
    {
        if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(info));
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

The dependency property works fine, but the setter method of "Pfad" in the ViewModel never gets called at all.

Thanks in advance!

kaliba
  • 230
  • 2
  • 12
  • 3
    The binding that you create in the View constructor is later replaced by the binding you define in MainWindow.xaml. It can't work that way. Besides that, you don't need to raise a PropertyChanged event in the setter of a dependency property. It already provides a change notification mechanism out of the box. Therefore you also don't need to implement INotifyPropertyChanged in class View. – Clemens Dec 19 '15 at 20:44
  • 4
    Otherwise it would be better to let MainWindow and View synchronize property values by a shared view model. – Clemens Dec 19 '15 at 20:47
  • Even though I deleted the TextBox binding, the setter-method won't be called. I never heard of a shared view. I'll check it out. I have this problem for about 3 hours now and everyone who has the same problem fixed it like that. I really have no idea what's missing. Thanks for letting me know, that the property changed wasn't needed. – kaliba Dec 19 '15 at 20:53
  • Not a shared view. What I mean is a common view model. I'm also not sure why you expect the view model Pfad property to be changed at all. It is bound to a TextBlock (not a TextBox), which wouldn't change the source property. – Clemens Dec 19 '15 at 21:00
  • It's just an example. Well, you can't change the TextBlock, but you can change the Textbox which is bound in the MainWindow. This example is maybe a bit strange, I agree. – kaliba Dec 19 '15 at 21:07

1 Answers1

2

Raising PropertyChanged in the CLR properties of dependency properties is a common mistake. You should not place any code there as it is not used by bindings at all. They exist merely for setting the property once in code or XAML, thus you also will not hit any breakpoints you set there.


var name = "Pfad";
var binding = new Binding(name) { Mode = BindingMode.TwoWay };
this.SetBinding(PfadProperty, binding);

I take it you want to forward the value to your view-model. This is not going to work as you can only bind the property once. Right now you also bind the property here:

<local:View x:Name="appEntryView" Pfad="{Binding ElementName=tbInput, Path=Text}" Grid.Row="1" Margin="10"/>

You could subscribe to the dependency property changes using the respective meta data when registering it, providing a callback, there you could set the value in the view-model.

The thing is: The view-model is private to the View, there really is no point in doing this synchronization if no-one has access to the data. You probably want the property to be either settable from the outside, treating the UserControl more like a control, discarding the view-model, or you want the view-model to be passed from outside as DataContext, and the view binds directly to it.


You need to be careful with explicitly setting the DataContext of UserControls in their definition, as it can obfuscate what is happening and lead to bindings unexpectedly breaking. If you want to set properties on the UserControl instance i would recommend avoiding it.

Community
  • 1
  • 1
H.B.
  • 166,899
  • 29
  • 327
  • 400
  • 1
    Thanks! I want to treat the UserControl like a control. I want to be able to configure the UserControl from the outside, but I also want to be able to add "custom workflows" to the UserControl. For example, I have a button in my View and the Command can be bound from outside to the button. I know that sounds strange, but the control has to be highly customizable. Maybe I shouldn't use MVVM here. There should also be a root-object which changes constantly and has to be known from the host-control (MainWindow.xaml), so I have to make a binding anyhow. – kaliba Dec 20 '15 at 09:58
  • 2
    Then i would get rid of the view-model for the `UserControl`; you can bind view-model properties on the instances "from the outside". – H.B. Dec 20 '15 at 10:28
  • 1
    Did you mean i can't bind the properties from the outside? If yes, is there any other architecture I can use or should i really just put everything in the Code-Behind? Maybe it would look even better, I don't know. – kaliba Dec 20 '15 at 10:41
  • 2
    I just meant that you would set properties on the instances like you would with any other control. I would put everything in the code behind. The only real alternative would be creating a custom control, i.e. a class that inherits from `Control`, which gets it's appearance from a default `Template` you have to register which also can be overridden. Then you have a more explicit separation between code and view. See also: [Control Authoring Overview](https://msdn.microsoft.com/library/ms745025%28v=vs.100%29.aspx) – H.B. Dec 22 '15 at 00:36
  • 2
    Hi! I deleted the whole viewmodel and put everything in the code-behind. Since then i got no problems and everything works just fine. Thanks ;) – kaliba Dec 22 '15 at 09:08