1
votes

Im using a treeview control inside my usercontrol. Usercontrol is used in a MainWindow (just for test). DataContext of the MainWindow is my ViewModel. In usercontrol code-behind i set some DependencyProperty and everythings work fine, eccept a property that doesn't fire the callback. In ViewModel there is an ObservableCollection Tree. MyObject contains a property ObservableCollection Leafs. This is the tree structure i'm using to populate the TreeView.

If i pass Tree property to the usercontrol (usign a DependencyProperty Source), it works fine. I mean that if i change elements inside Tree in ViewModel, Source fires the notification and the TreeView is updated. If i pass Tree.Leafs.elementAt(0) CurrentTree to the usercontrol (using another DependencyProperty Root) the notification fires once only when the usercontrol is loaded. If i change CurrentTree, the notification in ViewModel works, but RootChanged() in UserControl is not raised. I don't understand why.

This is the code: MainWindow

<DockPanel>
    <controls:TreeView Root="{Binding CurrentTree, Mode=TwoWay, diag:PresentationTraceSources.TraceLevel=High}">            
    </controls:TreeView>
</DockPanel>

ViewModel

    private MyObject currentTree;
    public MyObject CurrentTree
    {
        get
        {
            return currentTree;
        }
        set
        {
            if (currentTree == value) return;
            currentTree = value; 
            NotifyPropertyChanged();
        }
    }

UserControl code behind

 public static readonly DependencyProperty RootProperty = DependencyProperty.Register("Root", typeof(MyObject), typeof(TreeView), new FrameworkPropertyMetadata(new PropertyChangedCallback(RootChanged)));
private static void RootChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    ((TreeView)d).Root= (MyObject)e.NewValue;
}

public MyObject Root
{
    get
    {
        return (MyObject)GetValue(RootProperty);
    }
    set
    {
        SetValue(RootProperty, value);
    }
}

TreeView (UserControl) xaml

<UserControl x:Class="MyControls.TreeView"
         x:Name="TreeViewUserControl"
         .....
    <TextBlock Text="{Binding Root.Key, ElementName=TreeViewUserControl }" Height="16"/>
         .....
</UserControl>

The MainView DataContext is ViewModel, i suppose that the Datacontext of UserControl is inherited from MainView. The TextBlock in UserControl is used for test the DependencyProperty.

This is the XAML Binding debug:

System.Windows.Data Warning: 67 : BindingExpression (hash=14200498): Resolving source 
System.Windows.Data Warning: 70 : BindingExpression (hash=14200498): Found data context element: TreeView (hash=49044892) (OK)
System.Windows.Data Warning: 78 : BindingExpression (hash=14200498): Activate with root item ViewModel (hash=49218346)
System.Windows.Data Warning: 108 : BindingExpression (hash=14200498):   At level 0 - for ViewModel.CurrentTree found accessor ReflectPropertyDescriptor(CurrentTree)
System.Windows.Data Warning: 104 : BindingExpression (hash=14200498): Replace item at level 0 with ViewModel (hash=49218346), using accessor ReflectPropertyDescriptor(CurrentTree)
System.Windows.Data Warning: 101 : BindingExpression (hash=14200498): GetValue at level 0 from ViewModel (hash=49218346) using ReflectPropertyDescriptor(CurrentTree): MyObject (hash=52380055)
System.Windows.Data Warning: 80 : BindingExpression (hash=14200498): TransferValue - got raw value MyObject (hash=52380055)
System.Windows.Data Warning: 89 : BindingExpression (hash=14200498): TransferValue - using final value MyObject (hash=52380055)

So, i don't understand why the DependencyPropertyCallback is fired once.

1
You don't need your callback actually. You're trying to set the property value second time - it's already set for you.dymanoid
It seems also pointless to have a TwoWay Binding on the Root property. Does the control ever set that property itself?Clemens
As another note, it also looks odd to have a property as the backing "field" of another property. Change private BsonTree currentTree { get; set; } to private BsonTree currentTree;. And although perhaps obvious, but NotifyPropertyChanged(); uses CallerMemberNameAttribute?Clemens
So TreeView is only meant to confuse us? Really, change it, especially when it's just used for this post. Please also show us some code where you actually change the CurrentTree property.Clemens
No, the setter isn't called when the property is set by a Binding (and some other sources). WPF calls SetValue(RootProperty, ...) directly.Clemens

1 Answers

0
votes

Problem solved.

Code is a little bit complex then the code posted. This is the structure:

1 Class Library Project with a class MyViewModel

2 UserControls Library Project with "MyTreeView"

3 Wpf Project used to test 1 and 2. MainWindow datacontext is MyViewModel from 1

The main problem wasn't with DP.

In Class Library Project i used a BaseNotify class inherited from all other classes. MyViewModel too. Removing BaseNotify from MyViewModel and inheriting INotifyPropertyChanged directly, now everthing works fine! I don't know why, but it work.

BaseNotify class:

public class BaseNotify : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    public void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}