Search code examples
c#wpfcheckboxtreeview.net-4.7.2

C# MVVM WPF TreeView with checkbox don't populate


My issue is that when i tried to create dynamically a TreeView with checkbox, but the TreeView is empty.

After some research i tried this :

Here is my treeview in XAML

<TreeView x:Name="TrwDonneesXml"
                                  HorizontalAlignment="Left" VerticalAlignment="Top"
                                  Grid.Row="8" Grid.Column="0"
                                  Height="420" 
                                  Width="500" 
                                  Margin="10,10,10,10"
                                  Background="LightGray"
                                  ScrollViewer.CanContentScroll="True" 
                                  ScrollViewer.VerticalScrollBarVisibility="Auto"
                                  ScrollViewer.HorizontalScrollBarVisibility="Auto"
                                  ItemsSource="{Binding Root, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                                  IsEnabled="True">
                            <TreeView.Resources>
                                <HierarchicalDataTemplate  DataType="{x:Type models:CheckableItem}" ItemsSource="{Binding Childrens, Mode=TwoWay}">
                                    <StackPanel Orientation="Horizontal" >
                                        <!--These elements are bound to a CheckableItem object.-->
                                        <CheckBox Focusable="False"
                                                  Content="{Binding Name}"
                                              IsChecked="{Binding IsChecked, Mode=TwoWay}"
                                              VerticalAlignment="Center"/>
                                        <!--<TextBlock Text="{Binding Name}"
                                                   Margin="2,0"/>-->
                                    </StackPanel>
                                </HierarchicalDataTemplate>
                            </TreeView.Resources>
                        </TreeView>

Here is my ViewModel


namespace AsurEnvironnement.SynchronisationModule.ViewModels
{
    public class Synchronisation2UserControlViewModel : ViewModelBase
    {
[...]
public ObservableCollection<CheckableItem> Root = new ObservableCollection<CheckableItem>();
                ObservableCollection<CheckableItem> rootAChildrens = new ObservableCollection<CheckableItem>();
[...]
               public override async Task InitializeStepAsync()
        {

            try
            {
                ObservableCollection<CheckableItem> rootAChildrens = new ObservableCollection<CheckableItem>();
                ObservableCollection<CheckableItem> rootBChildrens = new ObservableCollection<CheckableItem>();

                CheckableItem rootA = new CheckableItem() { Name = "RootA" };
                CheckableItem childrenA1 = new CheckableItem() { Name = "ChildrenA1" };
                CheckableItem childrenA2 = new CheckableItem() { Name = "ChildrenA2" };
                rootAChildrens.Add(childrenA1);
                rootAChildrens.Add(childrenA2);
                rootA.Childrens = rootAChildrens;

                CheckableItem rootB = new CheckableItem() { Name = "RootB" };
                CheckableItem childrenB1 = new CheckableItem() { Name = "ChildrenB1" };
                rootBChildrens.Add(childrenB1);
                rootB.Childrens = rootBChildrens;

                CheckableItem rootC = new CheckableItem() { Name = "RootC" };

                Root.Add(rootA);
                Root.Add(rootB);
                Root.Add(rootC);

                [...]
            }
            catch (Exception ex)
            {
                ProcessSnackBar(ex.Message);
                _logger.LogError(ex.Message, ex);
            }
            finally
            {
                [...]
            }
        }
[...]
}

Here is my Model

namespace AsurEnvironnement.SynchronisationModule.Models
{
    public class CheckableItem : DataModelBase, INotifyPropertyChanged
    {
        private bool? _isChecked;
        public ObservableCollection<CheckableItem> Childrens;
        public bool? IsChecked
        {
            get { return _isChecked; }

            set
            {
                _isChecked = value;
                foreach(CheckableItem children in Childrens)
                {
                    children.IsChecked = true;
                }
            }
        }

        public string Name { get; set; }
    }
}

I tried to check with Console.WriteLine(TrwDonneesXml.HasItems); to see if the data exist but can't be seen but the result is false so i checked in debug mode TrwDonneesXml.ItemsSource is null.

But i realy don't understand why.


Solution

  • The TreeView is empty due to your semantic mistake which causes the TreeView.ItemsSource binding to fail.

    This is a field:

    public ObservableCollection<CheckableItem> Root;
    

    This is a property:

    public ObservableCollection<CheckableItem> Root { get; set; }
    

    You are basically initializing a public field using a field initializer:

    public ObservableCollection<CheckableItem> Root 
      = new ObservableCollection<CheckableItem>();
    

    However, in WPF you can only bind to public properties and not to fields.
    The correct definition must be:

    public ObservableCollection<CheckableItem> Root { get; }
      = new ObservableCollection<CheckableItem>();