5
votes

I am trying to stick close to MVVM approach to building my WPF app and am running into a weird binding issue and feel like I'm missing something.

I have a user control (PluginsTreeView) which has a ViewModel (PluginsViewModel) driving it. PluginsTreeView exposes a public DependencyProperty of type string (DocumentPath). My MainWindow set's this property in XAML, but it doesn't seem to make it to my UserControl. I'm looking for some indication as to why this doesn't work.

PluginsTreeView.xaml.cs

public partial class PluginsTreeView: UserControl
{
    public PluginsTreeView()
    {
        InitializeComponent();
        ViewModel = new ViewModels.PluginsViewModel();
        this.DataContext = ViewModel;
    }

    public static readonly DependencyProperty DocumentPathProperty =
        DependencyProperty.Register("DocumentPath", typeof(string), typeof(PluginsTreeView), new FrameworkPropertyMetadata(""));


    public string DocumentPath
    {
        get { return (string)GetValue(DocumentPathProperty); }
        set 
        {
            //*** This doesn't hit when value set from xaml, works fine when set from code behind
            MessageBox.Show("DocumentPath"); 
            SetValue(DocumentPathProperty, value); 
            ViewModel.SetDocumentPath(value);
        }
    }
    ....
 }

MainWindow.xaml

My PluginsTreeView never gets the value 'test path' and I'm not sure why. I feel like I'm missing something fundamental here.

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:Views="clr-namespace:Mediafour.Machine.EditorWPF.Views" x:Class="Mediafour.Machine.EditorWPF.MainWindow"
    xmlns:uc="clr-namespace:Mediafour.Machine.EditorWPF.Views"
    Title="MainWindow" Height="350" Width="600">
  <Grid>
    <uc:PluginsTreeView x:Name="atv" DocumentPath="from xaml" />
  </Grid>
</Window>

But, when I set the DependencyProperty from the code-behind of the MainWindow, it does seem to set the value correctly. I'm trying to figure out the difference here and why the code-behind approach works and setting the property in xaml doesn't.

MainWindow.xaml.cs

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        MainWindowViewModel ViewModel = new MainWindowViewModel();
        this.DataContext = ViewModel;

        atv.DocumentPath = "from code behind";  //THIS WORKS!
     }
     ....
 }

Messing around with Snoop, I see that the value from XAML "from xaml" does make it into the property but my Set method in PluginsTreeView still never gets hit. The message box I have in there as a debug tool doesn't pop unless value is set from MainWindow code-behind.

1
I think I've found the answer to my own question in case someone else encounters this issue. Apparently you should not add any logic to these properties, because they are only called when you set the property from code. If you set the property from XAML the SetValue() method is called directly. I've ended up registering a callback method and works great now: public static readonly DependencyProperty DocumentPathProperty = DependencyProperty.Register("DocumentPath", typeof(string), typeof(PluginsTreeView), new FrameworkPropertyMetadata("initial value", OnValueChanged));genus
That's correct, you should probably post that as an answer to your question. Or, perhaps delete your question because I'm sure it has been asked and answered several times already.Alan

1 Answers

2
votes

Apparently you should not add any logic to these properties setters, because they are only called when you set the property from code. If you set the property from XAML the SetValue() method is called directly. I've ended up registering a callback method and all works great now:

public static readonly DependencyProperty DocumentPathProperty = DependencyProperty.Register("DocumentPath", typeof(string), typeof(PluginsTreeView), new FrameworkPropertyMetadata("initial value", OnValueChanged));