0

I have built a UserControl containing a TreeView where each node contains a bunch of controls.
Simplified version of the tree's code:

<UserControl x:Class="MyTestWPF.MyTree"
         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:MyTestWPF"
         mc:Ignorable="d" 
         x:Name="MyTreeUserControl">
<UserControl.Resources>
    <HierarchicalDataTemplate DataType="{x:Type local:MyTreeNode}"
                          ItemsSource="{Binding Nodes}">
        <StackPanel Orientation="Horizontal">
            <ComboBox>
                <ComboBoxItem>Item 1</ComboBoxItem>
                <ComboBoxItem>Item 2</ComboBoxItem>
            </ComboBox>
            <TextBlock Text="{Binding Path=Name}" />
        </StackPanel>
    </HierarchicalDataTemplate>
</UserControl.Resources>
<TreeView ItemsSource="{Binding MyTreeProp, ElementName=MyTreeUserControl}" />
</UserControl>

The control's codebehind only has a simple DependencyProperty for the MyTreeProp property created with the propdp snippet.

I then call the control like so:

<local:MyTree MyTreeModel="{Binding Path=MyData}" />

And here's the parent view's DataContext with MyData:

this.DataContext = new {
    MyData = new List<MyTreeNode>() {
        new MyTreeNode() {
            Name = "1",
            Nodes = new MyTreeNode[] {
                new MyTreeNode() { Name= "2" }
            }
        }
    }
};

The MyTreeNode is simply:

public class MyTreeNode
{
    public string Name { get; set; }
    public MyTreeNode[] Nodes { get; set; }
}

My question is about passing data into the MyTree control's MyTreeProp.
I initially tried to set the ItemsSource's binding without the ElementName, and setting MyTree's DataContext to this in the constructor, but that threw an exception:

System.Windows.Data Error: 40 : BindingExpression path error:
'MyData' property not found on 'object' ''MyTree' (Name='MyTreeUserControl')'. BindingExpression:Path=MyData;
DataItem='MyTree' (Name='MyTreeUserControl');
target element is 'MyTree' (Name='MyTreeUserControl');
target property is 'MyTreeProp' (type 'List`1')

Why?

  • What am I misunderstanding about how the view is attempting to read the DataContext?
  • Why does it seem from the error that it is searching for MyData on the UserControl? Or am I misreading that?
  • Is there really no way to pass the value to the UserControl's property, and then use it without the need to reference the UserControl.Name in each and every element that needs to read it?

--Edit--
Based on the comments, I understand that setting DataContext directly is wrong... but that still leaves the question - what is the correct way to access the UserControl's properties? Does it really require setting a name to the UserControl tag, and then explicitly referencing it by name whenever I need to access the property?

Guy Passy
  • 694
  • 1
  • 9
  • 32
  • 2
    `'MyData' property not found on 'object' ''MyTree' (Name='MyTreeUserControl')` means that you have explicitly set the UserControl's DataContext. You must not do that. – Clemens Jul 16 '19 at 09:26
  • If that is the case, why are half of the examples I've seen on stack overflow explicitly setting `this.DataContext = this;` in the constructor? – Guy Passy Jul 16 '19 at 09:27
  • 2
    These examples are all wrong, sorry. People who recommend it do not know what they are talking about. Setting `DataContext = this;` (or setting it to some private view model) effectively prevents that `MyTreeModel="{Binding Path=MyData}"` works as intended. – Clemens Jul 16 '19 at 09:27
  • Instead of `ItemsSource="{Binding MyTreeProp, ElementName=MyTreeUserControl}"` you may write `ItemsSource="{Binding MyTreeProp, RelativeSource={RelativeSource AncestorType=UserControl}}"`. Thus you avoid to have a generated `MyTreeUserControl` field in your UserControl class. – Clemens Jul 16 '19 at 09:33
  • I think I understand. If I want to do what I'm looking to do, I need to actually use a `Model` object and set that. It will then be passed around my `UserControl` as `DataContext` automatically. I was expecting some more simple way to access the `View`'s properties from the `View`... but I understand that this was not a well-founded expectation. – Guy Passy Jul 16 '19 at 09:39

0 Answers0