samedi 4 avril 2020

C# WPF - Checkbox filled TreeView appearing empty when getting its ItemsSource through Binding

I've been trying to build a TreeView with a Hierarchical setup filled with Checkboxes. And such, in order to retrieve which checkboxes are checked later in the code. And in between others, two problems are still on my plate: 1. However I try to give the TreeView's ItemsSource as a Binding, the TreeView appears empty when the program is running. This does not happen if I set ItemsSource manually in the code-behind. 2. I also have a problem with getting the IsChecked information of the Checkboxes, since I can't rely on the binding. Here's the interesting part of the code I've come up with so far: --- XAML ---

<TreeView x:Name="TreeView_Add_TagSelector" ItemsSource="{Binding TagCheckboxesTreeSource}">
    <TreeView.ItemTemplate>
        <HierarchicalDataTemplate DataType="{x:Type local:TagCheckboxNode}" ItemsSource="{Binding Children}">
            <StackPanel Orientation="Horizontal">
                <CheckBox Content="{Binding Name}" IsChecked="{Binding IsChecked}" />
            </StackPanel>
        </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>
</TreeView>

--- C# ---

public partial class MainWindow : Window
{
    ObservableCollection<TagCheckboxNode> tagCheckboxesTreeSource;
    List<Tag> selectedTags;
    // Other non-related attributes...

    public MainWindow()
    {
        // Other non-related stuff...

        // I've been trying my loading function before and after InitializeComponent just in case...
        LoadTagTree();
        InitializeComponent();
        LoadTagTree();
    }

    private ObservableCollection<TagCheckboxNode> TagCheckboxesTreeSource { get => tagCheckboxesTreeSource; set => tagCheckboxesTreeSource = value; }
    // Other non-related properties...

    private void LoadTagTree()
    {
        ObservableCollection<TagCheckboxNode> categories = new ObservableCollection<TagCheckboxNode>();
        foreach (XmlElement category in database.XmlCategories)
        {
            TagCheckboxNode categoryItem = new TagCheckboxNode(null, category.GetAttribute("name"));
            foreach(XmlElement tag in category)
            {
                TagCheckboxNode tagItem = new TagCheckboxNode(categoryItem, tag.GetAttribute("name"));
                categoryItem.Children.Add(tagItem);
            }
            categories.Add(categoryItem);
        }

        TagCheckboxesTreeSource = categories;
        //TreeView_Add_TagSelector.ItemsSource = categories;  // This works but the above doesn't
    }

    // I'm also going to need an event handler which fires up when a checkbox's state changes.
    private void TreeView_Add_TagSelector_SelectionChanged(object sender, RoutedEventArgs e)
    {
        // To fill up...
    }
}

class TagCheckboxNode : INotifyPropertyChanged
{
    TagCheckboxNode parent;
    ObservableCollection<TagCheckboxNode> children;
    bool? isChecked;
    bool shouldParentAlsoCheck;
    string name;

    public TagCheckboxNode(TagCheckboxNode parent, string name, bool shouldParentAlsoCheck = true)
    {
        this.parent = parent;
        this.name = name;
        this.isChecked = false;
        this.children = new ObservableCollection<TagCheckboxNode>();
        this.shouldParentAlsoCheck = shouldParentAlsoCheck;
    }

    public ObservableCollection<TagCheckboxNode> Children => children;
    public bool ShouldParentAlsoCheck => shouldParentAlsoCheck;
    public string Name => name;

    public bool? IsChecked
    {
        get { return isChecked; }
        set { SetIsChecked(value, true, shouldParentAlsoCheck); }
    }

    public bool HasChildNodes {
        // Non-relevant code
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged(string name = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }

    void SetIsChecked(bool? value, bool updateChildren, bool updateParent) {
        // More non-relevant code
        // Just be aware that OnPropertyChanged() is called inside here.
    }

    void VerifyCheckState() {
        // Non-relevant code again
    }
}

If you need more information feel free to ask. Thank you for your time in advance.

biguest




Aucun commentaire:

Enregistrer un commentaire