PropertyChanged always null with TreeView

532 Views Asked by At

I'm having a problem implementing a search functionality in my TreeView on a WPF Project.

I used this guide to create a TreeView with ViewModels. I have used the TextSeachDemo and edited the Controls in a way that they that they fit my application. (Added the right Classes, more layers etc.)

Everything works fine, I get a structure with correct children and parents and the search function also works, as it finds the correct entry.

Problem now is: When i try to set the "IsExpanded" Property from code nothing happens. Debubgging shows me that the PropertyChanged event in the RaiseProperty Changed Method is always null.

In the test Project provided by Josh Smith, everything seems to be working fine. The only significant difference that i could make out is that he set the datacontext in code while i did in the XAML:

Code from Josh Smith:

 public TextSearchDemoControl()
    {
        InitializeComponent();

        // Get raw family tree data from a database.
        Person rootPerson = Database.GetFamilyTree();

        // Create UI-friendly wrappers around the 
        // raw data objects (i.e. the view-model).
        _familyTree = new FamilyTreeViewModel(rootPerson);

        // Let the UI bind to the view-model.
        base.DataContext = _familyTree;
    }

From the constructor from the MainViewModel (The ViewModel that handles the entire window)

List<FactoryItem> rootItems = _machineService.GetFactoryItems();
  FactoryTree = new FactoryTreeViewModel(rootItems);

Where as FactoryTree is a public Observable Property which i bind the DataContext of the TreeView too (instead of in code as above):

   <TreeView DataContext="{Binding FactoryTree}" ItemsSource="{Binding FirstGeneration}">

The other way around by the way, when i open a item via the GUI, the breakpoint on my property does trigger and raise an event.

Any ideas?

1

There are 1 best solutions below

0
On

This solution addresses the problem in a more mvvm friendly way. A UserControl contains a TreeView. It uses the type YourViewModel as data context. The view model contains a collection of YourDomainType which itself has a child collection ChildElements of the same type.

In xaml, the data is bound to the ElementInViewModel collection of the view model. In addition there is a HierarchicalDataTemplate (which is appropriate for a tree view).

The type YourDomainType contains properties IsExpanded and IsSelected which are bound to the respective properties of the TreeViewItem in a Style. If you set these properties in your view model the tree view should react as expected (selecting or expanding the respective TreeViewItem).

I am aware that IsExpanded and IsSelected do not belong to a DTO object. YourDomainType is probably more a type for displaying data, but it could also wrap a DTO object stored in it.

<UserControl>

    <UserControl.DataContext>
        <YourViewModel/>
    </UserControl.DataContext>  

    <UserControl.Resources>        
        <CollectionViewSource Source="{Binding Path=ElementsInViewModel}" x:Key="Cvs">
        </CollectionViewSource>

        <HierarchicalDataTemplate DataType="{x:Type DomainModel:YourDomainType}"
                                  ItemsSource="{Binding Path=ChildElements}">
            <TextBlock Text="{Binding Path=Name}"/>            
        </HierarchicalDataTemplate>        

        <Style TargetType="{x:Type TreeViewItem}">
            <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
            </Setter>
            <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
            </Setter>            
        </Style>

    </UserControl.Resources>


    <DockPanel>
        <TreeView ItemsSource="{Binding Source={StaticResource Cvs}}"/>
    </DockPanel>

</UserControl>

public class YourViewModel
{

    public ObservableCollection<YourDomainType> ElementsInViewModel 
    {
        get
        {
            return _elementsInViewModel;
        }
        set
        {
            if (_elementsInViewModel != value)
            {
                _elementsInViewModel = value;
                RaisePropertyChanged("ElementsInViewModel");
            }
        }
    }
    ObservableCollection<YourDomainType> _elementsInViewModel;


    public YourViewModel()
    {

    }
}

public class YourDomainType
{

    public ObservableCollection<YourDomainType> ChildElements
    {
        get
        {
            return _childElements;
        }
        set
        {
            if (_childElements != value)
            {
                _childElements = value;
                RaisePropertyChanged("ChildElements");
            }
        }
    }
    ObservableCollection<YourDomainType> _childElements;


    public bool IsExpanded
    {
        get
        {
            return _isExpanded;
        }
        set
        {
            if (_isExpanded != value)
            {
                _isExpanded = value;
                RaisePropertyChanged("IsExpanded");
            }
        }
    }
    bool _isExpanded;


    public bool IsSelected
    {
        get
        {
            return _isSelected;
        }
        set
        {
            if (_isSelected != value)
            {
                _isSelected = value;
                RaisePropertyChanged("IsSelected");
            }
        }
    }
    bool _isSelected;


    public string Name
    {
        get
        {
            return _name;
        }
        set
        {
            if (_name != value)
            {
                _name = value;
                RaisePropertyChanged("Name");
            }
        }
    }
    string _name;
}