Страницы

Поиск по вопросам

вторник, 25 декабря 2018 г.

Програмно снять выделение с TreeView

Необходимо снять выделение с TreeView при удалении элемента. По умолчанию выделяется первый элемент, а задать SelectedItem = null не получается так как это свойство только для чтения. Как это реализовать?


Ответ

В обычном случае вы не создаёте элементы TreeView вручную, а генерируете их автоматически, используя ItemsSource + ItemTemplate
При этом treeView.SelectedItem будет не TreeViewItem, а элемент коллекции, привязанной к ItemsSource. Так что приведение к типу TreeViewItem не сработает.
Чтобы получить из этого собственно визуальный элемент, обычно применяется конструкция list.ItemContainerGenerator.ContainerFromItem(selectedItem);. Но в случае TreeView она, к сожалению, работает лишь на верхнем уровне: каждый TreeViewItem рекурсивно сам является TreeView, и заботится сам о своих контейнерах, так что list.ItemContainerGenerator ничего не знает о вложенных частях дерева.
Неплохое универсально работающее (но многословное) решение — следить за изменением выделенного элемента, и запоминать его в code behind.
Пример:


public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); DataContext = new[] { new DataHolder("Привет-привет", null), new DataHolder("Пока-пока", null), new DataHolder(null, new[] { new DataHolder("Я очень", null), new DataHolder("буду ждать", new[] { new DataHolder("звонка", null) }), }), };
TreeViewItem selectedItemTV = null; TV.AddHandler(TreeViewItem.SelectedEvent, (RoutedEventHandler)((sender, args) => selectedItemTV = (TreeViewItem)args.OriginalSource));
TreeViewItem selectedItemTV2 = null; TV2.AddHandler(TreeViewItem.SelectedEvent, (RoutedEventHandler)((sender, args) => selectedItemTV2 = (TreeViewItem)args.OriginalSource));
var dt = new DispatcherTimer() { Interval = TimeSpan.FromSeconds(5) }; dt.Tick += (sender, args) => { if (selectedItemTV != null) { selectedItemTV.IsSelected = false; selectedItemTV = null; } if (selectedItemTV2 != null) { selectedItemTV2.IsSelected = false; selectedItemTV2 = null; } }; dt.Start(); } }

public class DataHolder { public string Key { get; private set; } public IEnumerable Value { get; private set; }
public DataHolder(string key, IEnumerable value) { Key = key; Value = value; } }
Ну или вы можете подписаться обычным способом в XAML'е: TreeViewItem.Selected="TVItemSelected"

Ещё есть решение с полным рекурсивным обходном дерева, и поиском TreeViewItem'а, как описано здесь. Но оно мне кажется слишком уж неэффективным, и наверняка плохо дружит с виртуализацией.

Комментариев нет:

Отправить комментарий