2
votes

How do you get the currently highlighted/selected node from a TreeView control? Based on the documentation here it should be possible to iterate through the control's SelectedNodes property but its always empty.


EDIT: This turns out to be an actual bug with XAML, tracked here. Until it's fixed, the accepted answer does fine as work-around.


Context: I'm working on a simple demo application to explore the TreeView control. I'm trying to add a keyboard shortcut to delete the currently active/selected node. I have the delete functionality in a RelayCommand class that implements ICommand. I have already gotten it working from the TreeViewItem DataTemplate as a button and as a flyout menu. I couldn't see a clean way to reuse the ICommand in a keyboard shortcut.

Image of the app with the last-clicked item highlighted; I need to access this TreeViewItem in the code-behind so I can delete it when the TreeView has focus and the "Delete" key is pressed. TreeViewExample

I have an event handler on the TreeView control:

<controls:TreeView x:Name="TreeDataBound"
    ItemsSource="{Binding Path=TreeSource, Mode=TwoWay}"
    ItemTemplateSelector="{StaticResource TreeItemTemplateSelector}"
    SelectionMode="Single"
    KeyDown="Tree_KeyDown">

The event handler should be looping through the selected nodes, although I think with SelectionMode="Single" the enumerable should only have one item.

private void Tree_KeyDown(object sender, KeyRoutedEventArgs e)
{
    if (e.Key == Windows.System.VirtualKey.Delete)
    {
        foreach (var element in TreeDataBound.SelectedNodes)
        {
            // Delete the item 
        }
    }
}

SelectedNodes appears to always be empty, even when one of the TreeView iems is highlighted. In the debugger SelectedNodes appears as type of System.__ComObject with no accessible properties, so I can't inspect it any further.

Any thoughts or suggestions for a better approach?

2
So with the TreeView SelectionMode="Multiple", this works as expected. There must be a way to get the selected item when the mode is "Single".daedalus12
Yes, I was able to adapt this answer. Thanks!daedalus12

2 Answers

1
votes

UWP Get selected nodes from TreeView control

For Single Selection mode. TreeViewItem contains IsSelected property, you could create model class with IsSelected and bind it. After item selected IsSelected value will be changed, so you could foreach the itemsource then delete the selected item. Please refer the following steps.

Code behind

public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();
        DataSource = GetData();
    }
    private ObservableCollection<ExplorerItem> DataSource;
    private ObservableCollection<ExplorerItem> GetData()
    {
        var list = new ObservableCollection<ExplorerItem>();
        ExplorerItem folder1 = new ExplorerItem()
        {
            Name = "Work Documents",
            Type = ExplorerItem.ExplorerItemType.Folder,
            Children =
            {
                new ExplorerItem()
                {
                    Name = "Functional Specifications",
                    Type = ExplorerItem.ExplorerItemType.Folder,
                    Children =
                    {
                        new ExplorerItem()
                        {
                            Name = "TreeView spec",
                            Type = ExplorerItem.ExplorerItemType.File,
                          }
                    }
                },
                new ExplorerItem()
                {
                    Name = "Feature Schedule",
                    Type = ExplorerItem.ExplorerItemType.File,
                },
                new ExplorerItem()
                {
                    Name = "Overall Project Plan",
                    Type = ExplorerItem.ExplorerItemType.File,
                },
                new ExplorerItem()
                {
                    Name = "Feature Resources Allocation",
                    Type = ExplorerItem.ExplorerItemType.File,
                }
            }
        };
        ExplorerItem folder2 = new ExplorerItem()
        {
            Name = "Personal Folder",
            Type = ExplorerItem.ExplorerItemType.Folder,
            Children =
                    {
                        new ExplorerItem()
                        {
                            Name = "Home Remodel Folder",
                            Type = ExplorerItem.ExplorerItemType.Folder,
                            Children =
                            {
                                new ExplorerItem()
                                {
                                    Name = "Contractor Contact Info",
                                    Type = ExplorerItem.ExplorerItemType.File,
                                },
                                new ExplorerItem()
                                {
                                    Name = "Paint Color Scheme",
                                    Type = ExplorerItem.ExplorerItemType.File,
                                },
                                new ExplorerItem()
                                {
                                    Name = "Flooring Woodgrain type",
                                    Type = ExplorerItem.ExplorerItemType.File,
                                },
                                new ExplorerItem()
                                {
                                    Name = "Kitchen Cabinet Style",
                                    Type = ExplorerItem.ExplorerItemType.File,
                                }
                            }
                        }
                    }
        };

        list.Add(folder1);
        list.Add(folder2);
        return list;
    }

    private void Tree_KeyDown(object sender, KeyRoutedEventArgs e)
    {
        if (e.Key == Windows.System.VirtualKey.Delete)
        {
             DeleteSelectItem(DataSource);
        }

    }
    private ExplorerItem DeleteSelectItem(ObservableCollection<ExplorerItem> DataSource)
    {
        foreach (var item in DataSource)
        {
            if (item.IsSelected == true)
            {
                DataSource.Remove(item);
                return item;
            }

            var FindResult = DeleteSelectItem(item.Children);
            if (FindResult != null)
                return FindResult;
        }

        return null;
    }
}
public class ExplorerItem : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    public enum ExplorerItemType { Folder, File };
    public String Name { get; set; }
    public ExplorerItemType Type { get; set; }
    private ObservableCollection<ExplorerItem> m_children;
    public ObservableCollection<ExplorerItem> Children
    {
        get
        {
            if (m_children == null)
            {
                m_children = new ObservableCollection<ExplorerItem>();
            }
            return m_children;
        }
        set
        {
            m_children = value;
        }
    }

    private bool m_isExpanded;
    public bool IsExpanded
    {
        get { return m_isExpanded; }
        set
        {
            if (m_isExpanded != value)
            {
                m_isExpanded = value;
                NotifyPropertyChanged("IsExpanded");
            }
        }
    }

    private bool m_isSelected;
    public bool IsSelected
    {
        get { return m_isSelected; }

        set
        {
            if (m_isSelected != value)
            {
                m_isSelected = value;
                NotifyPropertyChanged("IsSelected");
            }
        }

    }

    private void NotifyPropertyChanged(String propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

class ExplorerItemTemplateSelector : DataTemplateSelector
{
    public DataTemplate FolderTemplate { get; set; }
    public DataTemplate FileTemplate { get; set; }

    protected override DataTemplate SelectTemplateCore(object item)
    {
        var explorerItem = (ExplorerItem)item;
        return explorerItem.Type == ExplorerItem.ExplorerItemType.Folder ? FolderTemplate : FileTemplate;
    }
}

Xaml

<Page.Resources>
    <DataTemplate x:Key="FolderTemplate" x:DataType="local:ExplorerItem">
        <TreeViewItem
            AutomationProperties.Name="{x:Bind Name}"
            IsExpanded="{x:Bind IsExpanded,Mode=TwoWay}"
            IsSelected="{x:Bind IsSelected,Mode=TwoWay}"
            ItemsSource="{x:Bind Children}"
            >

            <StackPanel Orientation="Horizontal">
                <Image Width="20" Source="../Assets/folder.png" />
                <TextBlock Margin="0,0,10,0" />
                <TextBlock Text="{x:Bind Name}" />
            </StackPanel>
        </TreeViewItem>
    </DataTemplate>

    <DataTemplate x:Key="FileTemplate" x:DataType="local:ExplorerItem">
        <TreeViewItem AutomationProperties.Name="{x:Bind Name}" IsSelected="{x:Bind IsSelected,Mode=TwoWay}">

            <StackPanel Orientation="Horizontal">
                <Image Width="20" Source="../Assets/file.png" />
                <TextBlock Margin="0,0,10,0" />
                <TextBlock Text="{x:Bind Name}" />
            </StackPanel>
        </TreeViewItem>
    </DataTemplate>

    <local:ExplorerItemTemplateSelector
        x:Key="ExpolrerItemTemplateSelector"
        FileTemplate="{StaticResource FileTemplate}"
        FolderTemplate="{StaticResource FolderTemplate}"
        />
</Page.Resources>
<Grid>
    <TreeView
        x:Name="TreeDataBound"
        HorizontalAlignment="Center"
        VerticalAlignment="Top"
        ItemTemplateSelector="{StaticResource ExpolrerItemTemplateSelector}"
        ItemsSource="{x:Bind DataSource,Mode=OneWay}"
        KeyDown="Tree_KeyDown"
        SelectionMode="Single"
        />
</Grid>

For the complete code sample please refer this link.

0
votes

This problem has been addressed, but it is not "enabled" when you create a uwp app as usual.

You need to modify some lines of you code to enable the newer version of xaml.

First, follow instructions on this page: https://docs.microsoft.com/en-us/windows/apps/winui/winui2/getting-started . Get the latest package installed in your project.

Then add xmlns:muxc="using:Microsoft.UI.Xaml.Controls" or using muxc = Microsoft.UI.Xaml.Controls; to which ever Xaml/CS file you want to use the new control from latest package. Examples can be fond at :https://docs.microsoft.com/en-us/windows/uwp/design/controls-and-patterns/tree-view . In one word, add muxc: to every <TreeView> so that they become <muxc:TreeView> (so to every child item except for data templates). And then in the back cs code, use muxc.TreeViewxxx instead of default namespace.

I am no expert on C# or VS, this answer could be improved or have unnecessary instructions, but it worked for me now.