0
votes

I am new to WPF, and trying to create a tree using treeview.

What I want to do is to generate a tree dynamically. Each treeViewItem contains a comboBox and a textBlock. As user expand a node, the app will retrieve children nodes information from a data source. Finally user could select several nodes with the checkBoxes.

Following some online tutorials, I did the following tree: e as shown below:

<Window.Resources>
    <Style TargetType="{x:Type TreeViewItem}">
        <Setter Property="HeaderTemplate">
            <Setter.Value>
                <HierarchicalDataTemplate DataType="{x:Type sotc:TaxNode}" ItemsSource="{Binding Path=Children}">
                    <StackPanel Orientation="Horizontal">
                        <CheckBox Name="chk" Margin="2" Tag="{Binding}"/>
                        <TextBlock Text="{Binding Path=TaxID}" ToolTip="{Binding Path=Lineage}" />
                    </StackPanel>
                </HierarchicalDataTemplate>
            </Setter.Value>
        </Setter>

    </Style>
</Window.Resources>

<Grid>
    <TreeView Margin="25,186,22,46">
        <TreeViewItem Header="Taxonomy Tree" x:Name="_TaxTree" x:FieldModifier="private">
            <TreeViewItem Header="Loading..." TextBlock.FontStyle="Italic"></TreeViewItem>
        </TreeViewItem>
    </TreeView>
</Grid>

And I have a method to get the selected comboBox

private List<CheckBox> GetSelectedCheckBoxes(ItemCollection items)
    {
        var list = new List<CheckBox>();
        foreach (TreeViewItem item in items)
        {
            UIElement element = GetChildControl(item, "chk");
            if (element != null)
            {
                var chk = (CheckBox)element;
                if (chk.IsChecked.HasValue && chk.IsChecked.Value)
                {
                    list.Add(chk);
                }
            }

            List<CheckBox> l = GetSelectedCheckBoxes(item.Items);
            list = list.Concat(l).ToList();
        }

        return list;
    }

    private UIElement GetChildControl(DependencyObject parentObject, string childName)
    {
        UIElement element = null;
        if (parentObject != null)
        {
            int totalChild = VisualTreeHelper.GetChildrenCount(parentObject);
            for (int i = 0; i < totalChild; i++)
            {
                DependencyObject childObject = VisualTreeHelper.GetChild(parentObject, i);

                if (childObject is FrameworkElement &&
                    ((FrameworkElement)childObject).Name == childName)
                {
                    element = childObject as UIElement;
                    break;
                }

                // get its child
                element = GetChildControl(childObject, childName);
                if (element != null) break;
            }
        }

        return element;
    }

But due to lack of knowledge on WPF, I do not know what is the ItemCollection I should pass to the method.

Any advice or tutorials will be greatly appreciated.

Have a nice holiday

1

1 Answers

0
votes

In your XAML, you can add a Name property to your TreeView:

<Grid>
    <TreeView Name="MyAwesomeTreeView" Margin="25,186,22,46">
        <TreeViewItem Header="Taxonomy Tree" x:Name="_TaxTree" x:FieldModifier="private">
            <TreeViewItem Header="Loading..." TextBlock.FontStyle="Italic"></TreeViewItem>
        </TreeViewItem>
    </TreeView>
</Grid>

Now you can now access the items of this treeview in codebehind like this:

ItemCollection myDataItems = MyAwesomeTreeView.Items
GetSelectedCheckBoxes(myDataItems, MyAwesomeTreeView);

However the method you've shown will not work on TreeViews with data templates. The reason is that a TreeView will only generate the TreeViewItems once they are actually visible on screen. Until then, it only contains the underlying data. To get around this, you need to use the TreeView's ItemContainerGenerator. Modify your checkbox method to:

private List<CheckBox> GetSelectedCheckBoxes(ItemCollection items, ItemsControl source)
{
    var list = new List<CheckBox>();
    foreach (object dataitem in items)
    {
        UIElement treeviewitem = source.ItemContainerGenerator.ContainerFromItem(dataitem)
        UIElement element = GetChildControl(treeviewitem, "chk");
        if (element != null)
        {
            var chk = (CheckBox)element;
            if (chk.IsChecked.HasValue && chk.IsChecked.Value)
            {
                list.Add(chk);
            }
        }

        List<CheckBox> l = GetSelectedCheckBoxes(item.Items, treeviewitem);
        list = list.Concat(l).ToList();
    }

    return list;
}

But I would like to emphasize that this is not a good way of doing things. I can bet that what you are trying to achieve can be achieved in a much simpler, straightforward and maintainable way, so I suggest that you explain what it is first.