Как отобразить ContextMenu для определенного TreeViewItem и выделить выбранный TreeViewItem в WPF после MVVM ⇐ C#
-
Гость
Как отобразить ContextMenu для определенного TreeViewItem и выделить выбранный TreeViewItem в WPF после MVVM
Следуя MVVM, я разработал TreeView и теперь хочу, чтобы щелчок правой кнопкой мыши отображал ContextMenu для определенного TreeViewItem. TreeViewItem выделен желтым цветом Как вы можете видеть на скриншоте, я хочу, чтобы ContextMenu отображалось только при щелчке правой кнопкой мыши по элементу TreeViewItem первого уровня (отмечено желтым цветом). При нажатии на другие места контекстное меню не отображается.
Еще одна проблема, с которой я столкнулся: после того, как я щелкнул правой кнопкой мыши TreeViewItem, чтобы отобразить ContextMenu, я не могу выполнять какие-либо другие операции во всем окне. Не показывать ContextMenu на другом TreeViewItem или не сворачивать узел. Каким-то образом все окно потеряло фокус. Только когда я вывожу мышь из окна и возвращаюсь в окно, я могу выполнить операцию снова, но снова появляется только одна операция и та же проблема. Ниже показан пример. Контекстное меню возможно только один раз
Коды следующие:
XAML:
MainViewModel:
публичный класс MainViewModel { общественный ObservableCollection ListBoxSource {get; набор; } общественный ObservableCollection TreeViewSource {get; набор; } общественная MainViewModel() { ListBoxSource = новый ObservableCollection(); TreeViewSource = новый ObservableCollection(); for (int i = 1; i < 20; i++) ListBoxSource.Add(new ViewItem($"ListBoxItem {i}")); ViewItem defaultView = новый ViewItem("Root", 1) ; TreeViewSource.Add(defaultView); for (int i = 1; i < 10; i++) defaultView.ViewItems.Add(new ViewItem($"TreeViewItem {i}")); } } TreeViewDragDropBehavior:
с использованием System.Collections.ObjectModel; использование System.Windows; использование System.Windows.Controls; используя System.Windows.Input; использование System.Windows.Interactivity; использование System.Windows.Media; пространство имен TreeviewExample { общедоступный класс TreeViewDragDropBehavior: Behavior { // для сохранения TreeViewItem для перетаскивания частный TreeViewItem перетащилTVI = ноль; защищенное переопределение void OnAttached() { база.OnAttached(); AssociatedObject.PreviewMouseLeftButtonDown += PreviewMouseLeftButtonDown; AssociatedObject.MouseMove += tv_MouseMove; AssociatedObject.DragOver += tv_DragOver; AssociatedObject.Drop += Tv_Drop; AssociatedObject.DragLeave += tv_DragLeave; AssociatedObject.PreviewMouseRightButtonDown += AssociatedObject_PreviewMouseRightButtonDown; } Private void AssociatedObject_PreviewMouseRightButtonDown (отправитель объекта, MouseButtonEventArgs e) { перетащилTVI = FindAnchestor((DependencyObject)e.OriginalSource); перетащилTVI.Background = Brushes.MediumPurple; } // сохраняем TreeViewItem для перетаскивания Private void PreviewMouseLeftButtonDown (отправитель объекта, MouseButtonEventArgs e) { перетащилTVI = FindAnchestor((DependencyObject)e.OriginalSource); } // запускаем Drag&Drop, когда мышь перемещается и есть сохраненный TreeViewItem Private void tv_MouseMove (отправитель объекта, MouseEventArgs e) { если (draggedTVI != ноль) { // Находим данные за TreeViewItem ViewItem dragData = перетащилTVI.DataContext как ViewItem; // Инициализируем перетаскивание & операция сброса DragDrop.DoDragDrop(draggedTVI, dragData, DragDropEffects.Move); // сбрасываем сохраненный TreeViewItem перетащилTVI = ноль; } } // выделить цель Private void tv_DragOver (отправитель объекта, DragEventArgs e) { TreeViewItem tvi = FindAnchestor((DependencyObject)e.OriginalSource); если (tvi != null) tvi.Background = Brushes.MediumPurple; } Private void Tv_Drop (отправитель объекта, DragEventArgs e) { если (отправитель TreeView) { MainViewModel vm = (отправитель как TreeView).DataContext как MainViewModel; // проверка данных if (!e.Data.GetDataPresent(typeof(ViewItem))) return; // сохраняем цель перетаскивания ViewItem targetItem = (e.OriginalSource как TextBlock)?.DataContext как ViewItem; если (targetItem == null) return; Данные ViewItem = (ViewItem)e.Data.GetData(typeof(ViewItem)); //если перетащить из ListBox в TreeView если (draggedTVI == ноль) { ViewItem viewItemToInsert = новый ViewItem(data.ItemName); targetItem.ViewItems.Add(viewItemToInsert); } иначе, если (!targetItem.Equals(draggedTVI.DataContext as ViewItem)) { УдалитьViewNodeFromSource(vm.TreeViewSource, данные); targetItem.ViewItems.Add(данные); } } // сбрасываем фон целевого TreeViewItem TreeViewItem tvi = FindAnchestor((DependencyObject)e.OriginalSource); if (tvi != null) tvi.Background = Brushes.White; } // сбрасываем фон на левой возможной цели TreeViewItem Private void tv_DragLeave (отправитель объекта, DragEventArgs e) { TreeViewItem tvi = FindAnchestor((DependencyObject)e.OriginalSource); if (tvi != null) tvi.Background = Brushes.White; } // Помощник для поиска в VisualTree частный статический T FindAnchestor(текущий DependencyObject), где T: DependencyObject { делать { если (текущий равен T) вернуть (T)ток; текущий = VisualTreeHelper.GetParent(текущий); } Пока (текущий! = ноль); вернуть ноль; } частная пустота DeleteViewNodeFromSource (ObservableCollection viewItems, ViewItem viewItem) { foreach (ViewItem vi в viewItems) { если (vi.Equals(viewItem)) { viewItems.Remove(vi); перерыв; } еще УдалитьViewNodeFromSource(vi.ViewItems, viewItem); } } } } Класс ViewItem:
публичный класс ViewItem { общественная строка ItemName {get; набор; } общественный ObservableCollection ViewItems {get; } = новый ObservableCollection(); общественный ИНТ IsVisible {получить; набор; } публичный ViewItem() { } public ViewItem (имя строки, int IsVisible = 0) { this.ItemName = имя; это.IsVisible = IsVisible; } } Я действительно понятия не имею о первом выпуске. Что касается второй проблемы, я думаю, что проблема заключается в событии PreviewMouseRightButtonDown. В функции события я задаю фон выбранного TreeViewItem. Если я прокомментирую событие, такая проблема исчезнет. Но я понятия не имею, как это исправить. В конце концов мне все равно нужно сделать выбранный TreeViewItem выделенным, пока ContextMenu не закроется.
Может ли кто-нибудь помочь мне с этими двумя проблемами?
[*]Показать ContextMenu для определенного TreeViewItem [*]Включить выделение выбранного TreeViewItem и не влиять на другие контроллеры.
Следуя MVVM, я разработал TreeView и теперь хочу, чтобы щелчок правой кнопкой мыши отображал ContextMenu для определенного TreeViewItem. TreeViewItem выделен желтым цветом Как вы можете видеть на скриншоте, я хочу, чтобы ContextMenu отображалось только при щелчке правой кнопкой мыши по элементу TreeViewItem первого уровня (отмечено желтым цветом). При нажатии на другие места контекстное меню не отображается.
Еще одна проблема, с которой я столкнулся: после того, как я щелкнул правой кнопкой мыши TreeViewItem, чтобы отобразить ContextMenu, я не могу выполнять какие-либо другие операции во всем окне. Не показывать ContextMenu на другом TreeViewItem или не сворачивать узел. Каким-то образом все окно потеряло фокус. Только когда я вывожу мышь из окна и возвращаюсь в окно, я могу выполнить операцию снова, но снова появляется только одна операция и та же проблема. Ниже показан пример. Контекстное меню возможно только один раз
Коды следующие:
XAML:
MainViewModel:
публичный класс MainViewModel { общественный ObservableCollection ListBoxSource {get; набор; } общественный ObservableCollection TreeViewSource {get; набор; } общественная MainViewModel() { ListBoxSource = новый ObservableCollection(); TreeViewSource = новый ObservableCollection(); for (int i = 1; i < 20; i++) ListBoxSource.Add(new ViewItem($"ListBoxItem {i}")); ViewItem defaultView = новый ViewItem("Root", 1) ; TreeViewSource.Add(defaultView); for (int i = 1; i < 10; i++) defaultView.ViewItems.Add(new ViewItem($"TreeViewItem {i}")); } } TreeViewDragDropBehavior:
с использованием System.Collections.ObjectModel; использование System.Windows; использование System.Windows.Controls; используя System.Windows.Input; использование System.Windows.Interactivity; использование System.Windows.Media; пространство имен TreeviewExample { общедоступный класс TreeViewDragDropBehavior: Behavior { // для сохранения TreeViewItem для перетаскивания частный TreeViewItem перетащилTVI = ноль; защищенное переопределение void OnAttached() { база.OnAttached(); AssociatedObject.PreviewMouseLeftButtonDown += PreviewMouseLeftButtonDown; AssociatedObject.MouseMove += tv_MouseMove; AssociatedObject.DragOver += tv_DragOver; AssociatedObject.Drop += Tv_Drop; AssociatedObject.DragLeave += tv_DragLeave; AssociatedObject.PreviewMouseRightButtonDown += AssociatedObject_PreviewMouseRightButtonDown; } Private void AssociatedObject_PreviewMouseRightButtonDown (отправитель объекта, MouseButtonEventArgs e) { перетащилTVI = FindAnchestor((DependencyObject)e.OriginalSource); перетащилTVI.Background = Brushes.MediumPurple; } // сохраняем TreeViewItem для перетаскивания Private void PreviewMouseLeftButtonDown (отправитель объекта, MouseButtonEventArgs e) { перетащилTVI = FindAnchestor((DependencyObject)e.OriginalSource); } // запускаем Drag&Drop, когда мышь перемещается и есть сохраненный TreeViewItem Private void tv_MouseMove (отправитель объекта, MouseEventArgs e) { если (draggedTVI != ноль) { // Находим данные за TreeViewItem ViewItem dragData = перетащилTVI.DataContext как ViewItem; // Инициализируем перетаскивание & операция сброса DragDrop.DoDragDrop(draggedTVI, dragData, DragDropEffects.Move); // сбрасываем сохраненный TreeViewItem перетащилTVI = ноль; } } // выделить цель Private void tv_DragOver (отправитель объекта, DragEventArgs e) { TreeViewItem tvi = FindAnchestor((DependencyObject)e.OriginalSource); если (tvi != null) tvi.Background = Brushes.MediumPurple; } Private void Tv_Drop (отправитель объекта, DragEventArgs e) { если (отправитель TreeView) { MainViewModel vm = (отправитель как TreeView).DataContext как MainViewModel; // проверка данных if (!e.Data.GetDataPresent(typeof(ViewItem))) return; // сохраняем цель перетаскивания ViewItem targetItem = (e.OriginalSource как TextBlock)?.DataContext как ViewItem; если (targetItem == null) return; Данные ViewItem = (ViewItem)e.Data.GetData(typeof(ViewItem)); //если перетащить из ListBox в TreeView если (draggedTVI == ноль) { ViewItem viewItemToInsert = новый ViewItem(data.ItemName); targetItem.ViewItems.Add(viewItemToInsert); } иначе, если (!targetItem.Equals(draggedTVI.DataContext as ViewItem)) { УдалитьViewNodeFromSource(vm.TreeViewSource, данные); targetItem.ViewItems.Add(данные); } } // сбрасываем фон целевого TreeViewItem TreeViewItem tvi = FindAnchestor((DependencyObject)e.OriginalSource); if (tvi != null) tvi.Background = Brushes.White; } // сбрасываем фон на левой возможной цели TreeViewItem Private void tv_DragLeave (отправитель объекта, DragEventArgs e) { TreeViewItem tvi = FindAnchestor((DependencyObject)e.OriginalSource); if (tvi != null) tvi.Background = Brushes.White; } // Помощник для поиска в VisualTree частный статический T FindAnchestor(текущий DependencyObject), где T: DependencyObject { делать { если (текущий равен T) вернуть (T)ток; текущий = VisualTreeHelper.GetParent(текущий); } Пока (текущий! = ноль); вернуть ноль; } частная пустота DeleteViewNodeFromSource (ObservableCollection viewItems, ViewItem viewItem) { foreach (ViewItem vi в viewItems) { если (vi.Equals(viewItem)) { viewItems.Remove(vi); перерыв; } еще УдалитьViewNodeFromSource(vi.ViewItems, viewItem); } } } } Класс ViewItem:
публичный класс ViewItem { общественная строка ItemName {get; набор; } общественный ObservableCollection ViewItems {get; } = новый ObservableCollection(); общественный ИНТ IsVisible {получить; набор; } публичный ViewItem() { } public ViewItem (имя строки, int IsVisible = 0) { this.ItemName = имя; это.IsVisible = IsVisible; } } Я действительно понятия не имею о первом выпуске. Что касается второй проблемы, я думаю, что проблема заключается в событии PreviewMouseRightButtonDown. В функции события я задаю фон выбранного TreeViewItem. Если я прокомментирую событие, такая проблема исчезнет. Но я понятия не имею, как это исправить. В конце концов мне все равно нужно сделать выбранный TreeViewItem выделенным, пока ContextMenu не закроется.
Может ли кто-нибудь помочь мне с этими двумя проблемами?
[*]Показать ContextMenu для определенного TreeViewItem [*]Включить выделение выбранного TreeViewItem и не влиять на другие контроллеры.
Мобильная версия