0

Why does my wpf treeview not display the content when i've set the itemsource in the xaml? It only appears to show the data when i set this property in the CS file using this

trvFamilies.ItemsSource = families;

Is it maybe not updating because im not triggering and updating somehow in the xaml? I'm not sure what to change to get this to work correctly.

ViewModel.cs

using System;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Input;

namespace WpfApplication1
{
    public class ViewModel : ObservableObject
    {
        private ObservableCollection<Family> families; 
        public ObservableCollection<Family> Families
        {
            get { return families; }
            set
            {
                families = value;
                NotifyPropertyChanged("Families");
            }
        }

        public ViewModel()
        {
            // FAMILIES
            Family family1 = new Family() { Name = "The Doe's" };
            family1.Members.Add(new FamilyMember() { Name = "John Doe", Age = 42 });
            family1.Members.Add(new FamilyMember() { Name = "Jane Doe", Age = 39 });
            family1.Members.Add(new FamilyMember() { Name = "Sammy Doe", Age = 13 });
            Families.Add(family1);

            Family family2 = new Family() { Name = "The Moe's" };
            family2.Members.Add(new FamilyMember() { Name = "Mark Moe", Age = 31 });
            family2.Members.Add(new FamilyMember() { Name = "Norma Moe", Age = 28 });
            Families.Add(family2);
        }
    }

    public class Family
    {
        public Family()
        {
            this.Members = new ObservableCollection<FamilyMember>();
        }

        public string Name { get; set; }

        public ObservableCollection<FamilyMember> Members { get; set; }
    }

    public class FamilyMember
    {
        public string Name { get; set; }

        public int Age { get; set; }
    }
}

ObservableObject.cs

using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace WpfApplication1
{
    public class ObservableObject : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

MainWindow.xaml

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:self="clr-namespace:WpfApplication1"
        Title="MainWindow" Height="350" Width="300"
        WindowStartupLocation="CenterScreen">
    <Grid Margin="5">

        <TreeView Name="trvFamilies" ItemsSource="{Binding self:families}" Grid.Row="1" Grid.ColumnSpan="2">
            <TreeView.Resources>
                <HierarchicalDataTemplate DataType="{x:Type self:Family}" ItemsSource="{Binding Members}">
                    <StackPanel Orientation="Horizontal">
                        <Label VerticalAlignment="Center" FontFamily="WingDings" Content="1"/>
                        <TextBlock Text="{Binding Name}" />
                    </StackPanel>
                </HierarchicalDataTemplate>
                <DataTemplate DataType="{x:Type self:FamilyMember}">
                    <StackPanel Orientation="Horizontal">
                        <Label VerticalAlignment="Center" FontFamily="WingDings" Content="2"/>
                        <TextBlock Text="{Binding Name}" />
                    </StackPanel>
                </DataTemplate>
            </TreeView.Resources>
        </TreeView>

    </Grid>
</Window>
JokerMartini
  • 5,674
  • 9
  • 83
  • 193
  • You now have to bind to your ViewModel instead of your window ofcourse, check the debug output that might say that Families do not exist (anymore) on self – Icepickle Oct 16 '15 at 22:27

1 Answers1

0

Currently your are binding to a public field:

  public List<Family> families

But a binding source (the data) needs to be a public property. For collection types, in WPF you usually use a ObservableCollection

So change your property to:

public ObservableCollection<Family> Families { get; set; }


Additional info:

Usually the property you bind to does not reside in the view class (here: not in your window), but in a separate class called "view model" which should implement the INotifyPropertyChanged interface.

Take a look here for more info: Model-View-ViewModel-MVVM-Explained

In the end, your view model should look like this:

public ObservableCollection<Family> Families
{
    get { return _families; }
    set
    {
        _families= value;
        RaisePropertyChanged("Families");
    }
}
public ObservableCollection<Family> _families; 

protected void RaisePropertyChanged(string propertyName)
{
    PropertyChangedEventHandler handler = PropertyChanged;
    if (handler != null)
    {
        handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

public event PropertyChangedEventHandler PropertyChanged;      

With the new view model class in place, the binding can be changed to

ItemsSource="{Binding Families}"

Finally you need to set you new view model class as DataContext in your window, see here.

Community
  • 1
  • 1
Martin
  • 5,165
  • 1
  • 37
  • 50
  • would you be able to modify my code to demonstrate what that would look like? – JokerMartini Oct 16 '15 at 21:32
  • I would always suggest that an `ObservableCollection` is privately declared and instantiated as readonly and doesn't have a public setter, so that people can only clear the list or add items, it doesn't really need the RaisePropertyChanged call in this case – Icepickle Oct 16 '15 at 21:39
  • I changed the mistake with the string, copy paste error :-) Well yes, you could make the setter of `Families` private. I tend to always implement `INotifyPropertyChanged` in the properties are bind to, just to avoid potential errors. – Martin Oct 16 '15 at 21:42
  • it's more to protect myself from the fact that people might bind to the `CollectionChanged` event, and not observing if the Property Families get changed in the ViewModel (+they also have to keep a reference to it to unregister from the old `ObservableCollection`. But the `RaisePropertyChanged` call is also unnecessary at that time, so it really simplifies things. Btw, your private _families is declared as public here ;) – Icepickle Oct 16 '15 at 21:45
  • so ive updated the post above and it appears to not show still? Why would this be. check out my latest code – JokerMartini Oct 16 '15 at 22:02
  • Change your binding to ItemsSource="{Binding Families}" and then you need to set you new view model class as `DataContext` in your window, see here: http://stackoverflow.com/a/4590471/4424024 – Martin Oct 16 '15 at 22:27