[英]How do I access the TreeView COntext from the TReeViewItem Context Menu?
我有一個樹視圖,其中列出了 PC 中的驅動器和文件夾。 我在 MVVM 中與 Micrososft MVVM 工具包、Fody 和 Microsoft Behaviors 一起工作。
我有一個用於 PC 的文件夾樹視圖 model,它創建了一個由 FolderItemViewModel 表示的文件夾項的可觀察集合。 這里顯示的簡化測試示例是從 App.xaml.cs 開始的。
我想要一個類似於文件資源管理器的上下文菜單。 我可以在樹視圖項目級別放置一個上下文菜單,但我需要更改 FolderViewModel 中的屬性,而我無法從 FolderItemViewModel 中更改這些屬性。
我希望 Iteractive Triggers 能像對 Selected Item 一樣有所幫助。
任何幫助將不勝感激。
Xaml:
"""
<TreeView x:Name="tvFolders" Grid.Row="1" Grid.Column="1" ItemsSource="{Binding FolderCollection}"
MinWidth="250" MinHeight="250">
<ie1:Interaction.Triggers >
<ie1:EventTrigger EventName ="SelectedItemChanged" >
<ie1:InvokeCommandAction CommandParameter="{Binding ElementName=tvFolders, Path=SelectedItem}"
Command="{Binding TreeSelectionChangedClick}" />
</ie1:EventTrigger>
</ie1:Interaction.Triggers >
<TreeView.Resources>
<ContextMenu x:Key="TreeViewMenu">
<MenuItem Command="{Binding NewFolderClick}" Header=" New Folder " />
<MenuItem Command="{Binding RenameClick}" Header=" Rename " />
</ContextMenu>
</TreeView.Resources>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}" >
<Setter Property="ContextMenu" Value="{StaticResource TreeViewMenu}" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True" >
<Setter Property="FontWeight" Value="Bold" />
</Trigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate >
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
<StackPanel Orientation="Horizontal" Grid.Row="2" Grid.Column="1" >
<TextBlock Text="Selected Folder: " />
<TextBox Text="{Binding SelectedFolderPath}" />
</StackPanel>
"""
文件夾視圖模型:
“”“內部 class FolderTreeViewModel:ObservableObject { public ObservableCollection FolderCollection { get; set; }
public string SelectedFolderPath { get; set; }
public ICommand TreeSelectionChangedClick { get; set; }
public FolderTreeViewModel()
{
List<FolderItemViewModel> FolderItems = new List<FolderItemViewModel>();
// Get the logical drives as a list of DirectoryItemModel
List<FolderItemModel> subFolders = GetLogicalDrives();
foreach (FolderItemModel item in subFolders)
{
FolderItemViewModel folderItemViewModel = new FolderItemViewModel(item.FullPath);
FolderItems.Add(folderItemViewModel);
}
FolderCollection = new ObservableCollection<FolderItemViewModel>(FolderItems);
TreeSelectionChangedClick = new RelayCommand<FolderItemViewModel>(TreeSelectionChangedCommand);
}
public static List<FolderItemModel> GetLogicalDrives()
{
// Get every logical drive on the machine
List<FolderItemModel> DirectoryItems = new List<FolderItemModel>();
string[] drives = Directory.GetLogicalDrives();
foreach (string drive in drives)
{
DirectoryItems.Add(new FolderItemModel { FullPath = drive});
}
return DirectoryItems; // Directory.GetLogicalDrives().Select(drive => new DirectoryItemModel { FullPath = drive, Type = DirectoryItemType.Drive }).ToList();
}
private void TreeSelectionChangedCommand(FolderItemViewModel tVI)
{
if (TreeSelectionChangedClick != null)
{
SelectedFolderPath = tVI.FullPath;
}
}
} """
FolerItemViewModel:
""" 命名空間 TestTreeViewContextMenu.ViewModels { 內部 class FolderItemViewModel { 公共字符串名稱 { get; set; } public string FullPath { get; set; } public ObservableCollection SubFolders { get; set; }
public ICommand NewFolderClick { get; set; }
public ICommand RenameClick { get; set; }
public FolderItemViewModel(string fullName)
{
FullPath = fullName;
if (fullName == Path.GetPathRoot(fullName))
{
Name = fullName;
}
else
Name = Path.GetFileName(fullName);
SubFolders = new ObservableCollection<FolderItemViewModel>();
NewFolderClick = new RelayCommand(NewFolderCommand);
RenameClick = new RelayCommand(RenameCommand);
}
private void NewFolderCommand()
{
MessageBox.Show("New Folder");
// Cannot update the FolderTreeViewModel ColderCollection from FolderItemViewModel
}
private void RenameCommand()
{
MessageBox.Show($"Rename");
// Cannot update the SelectedFolderPath value from here
}
}
} """
我通過在 Treeview.ItemTemplate 的 TextBlock 中使用交互事件“PreviewMouseRightButtonDown”破解了它。 我向 FolderItemViewModel 添加了一個新屬性“RightSelected”,該屬性在構造函數中設置為“false”。 當鼠標右鍵被按下時,它被設置為“真”。 此事件發生在上下文菜單顯示之前。 從 FolderTreeViewModel 中的上下文菜單觸發的命令在 FolderCollection 中搜索具有“RightSelected == true”的 FolderItem,並將此 FolderItem 設置為命令的主題。 如果在顯示項目的 TextBlock 外部單擊鼠標,則沒有 FolderItems 將設置“RightSelected”屬性。 搜索例程必須檢查此條件。 選定的 FolderItem 保存在“_lastRightSelected”中,並在每次搜索開始時清除。
看法:
<Window x:Class="TestTreeViewContextMenu.Views.FolderTreeView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ie="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ie1="http://schemas.microsoft.com/xaml/behaviors"
xmlns:local="clr-namespace:TestTreeViewContextMenu"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="10" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="10" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10" />
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="10" />
</Grid.ColumnDefinitions>
<TreeView x:Name="tvFolders" Grid.Row="1" Grid.Column="1" ItemsSource="{Binding FolderCollection}"
MinWidth="250" MinHeight="250">
<TreeView.ContextMenu >
<ContextMenu>
<MenuItem Header=" New Folder " Command="{Binding NewFolderClick}" />
<MenuItem Header="RENAME" Command="{Binding RenameClick}" />
</ContextMenu>
</TreeView.ContextMenu>
<ie1:Interaction.Triggers >
<ie1:EventTrigger EventName ="SelectedItemChanged" >
<ie1:InvokeCommandAction
CommandParameter="{Binding ElementName=tvFolders, Path=SelectedItem}"
Command="{Binding TreeSelectionChangedClick}" />
</ie1:EventTrigger>
</ie1:Interaction.Triggers >
<TreeView.ItemTemplate>
<HierarchicalDataTemplate >
<TextBlock Text="{Binding Name}" >
<ie1:Interaction.Triggers>
<ie1:EventTrigger EventName="PreviewMouseRightButtonDown">
<ie1:ChangePropertyAction PropertyName="RightSelected" Value="true" TargetObject="{Binding}" />
</ie1:EventTrigger>
</ie1:Interaction.Triggers>
</TextBlock>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
<StackPanel Orientation="Horizontal" Grid.Row="2" Grid.Column="1" >
<TextBlock Text="Selected Folder: " />
<TextBox Text="{Binding SelectedFolderPath}" />
</StackPanel>
</Grid>
文件夾樹視圖模型:
using Microsoft.Toolkit.Mvvm.ComponentModel;
using Microsoft.Toolkit.Mvvm.Input;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using TestTreeViewContextMenu.Model;
namespace TestTreeViewContextMenu.ViewModels
{
internal class FolderTreeViewModel : ObservableObject
{
public ObservableCollection<FolderItemViewModel> FolderCollection { get; set; }
public string SelectedFolderPath { get; set; }
public ICommand TreeSelectionChangedClick { get; set; }
public ICommand NewFolderClick { get; set; }
public ICommand RenameClick { get; set; }
public ICommand SelectedFolderClick { get; set; }
public FolderItemViewModel SelectedFolderItem { get; set; }
public ICommand RightMouseButtonDownClick { get; set; }
public bool IsSelected { get; set; }
FolderItemViewModel _lastRightSelected = null;
public FolderTreeViewModel()
{
List<FolderItemViewModel> FolderItems = new List<FolderItemViewModel>();
// Get the logical drives as a list of DirectoryItemModel
List<FolderItemModel> subFolders = GetLogicalDrives();
foreach (FolderItemModel item in subFolders)
{
FolderItemViewModel folderItemViewModel = new FolderItemViewModel(item.FullPath);
FolderItems.Add(folderItemViewModel);
}
FolderCollection = new ObservableCollection<FolderItemViewModel>(FolderItems);
TreeSelectionChangedClick = new RelayCommand<FolderItemViewModel>(TreeSelectionChangedCommand);
NewFolderClick = new RelayCommand(NewFolderCommand);
RenameClick = new RelayCommand(RenameCommand);
RightMouseButtonDownClick = new RelayCommand<FolderItemViewModel>(RightMouseButtonDownCommand);
}
public List<FolderItemModel> GetLogicalDrives()
{
// Get every logical drive on the machine
List<FolderItemModel> DirectoryItems = new List<FolderItemModel>();
string[] drives = Directory.GetLogicalDrives();
foreach (string drive in drives)
{
DirectoryItems.Add(new FolderItemModel { FullPath = drive});
}
return DirectoryItems;
}
private void RightMouseButtonDownCommand(FolderItemViewModel tVI)
{
SelectedFolderItem = tVI;
}
private void TreeSelectionChangedCommand(FolderItemViewModel tVI)
{
if (TreeSelectionChangedClick != null)
{
SelectedFolderPath = tVI.FullPath;
SelectedFolderItem = tVI;
}
}
private void NewFolderCommand()
{
FindRightSelected();
string folderItemFullName = "";
if (_lastRightSelected == null)
folderItemFullName = "";
else
folderItemFullName = _lastRightSelected.FullPath;
MessageBox.Show($"New Folder in FolderTreeVM {folderItemFullName}");
// Cannot update the FolderTreeViewModel ColderCollection from FolderItemViewModel
}
private void RenameCommand()
{
FindRightSelected();
string folderItemFullName = "";
if (_lastRightSelected == null)
folderItemFullName = "";
else
folderItemFullName = _lastRightSelected.FullPath;
MessageBox.Show($"Rename in FolderTreeVM {folderItemFullName} ");
// Cannot update the SelectedFolderPath value from here
}
private void FindRightSelected()
{
FolderItemViewModel folderItemViewModel = null;
if (_lastRightSelected != null)
{
_lastRightSelected.RightSelected = false;
}
foreach (FolderItemViewModel tVI in FolderCollection)
{
if (tVI.RightSelected)
{
folderItemViewModel = tVI;
break;
}
}
_lastRightSelected = folderItemViewModel ?? null;
}
private void SelectedItemCommand(FolderItemViewModel tVi)
{
SelectedFolderItem = tVi;
}
}
}
文件夾項目視圖模型:
using Microsoft.Toolkit.Mvvm.Input;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using TestTreeViewContextMenu.Model;
namespace TestTreeViewContextMenu.ViewModels
{
internal class FolderItemViewModel
{
public string Name { get; set; }
public string FullPath { get; set; }
public bool RightSelected { get; set; }
public ObservableCollection<FolderItemViewModel> SubFolders { get; set; }
public FolderItemViewModel(string fullName)
{
FullPath = fullName;
if (fullName == Path.GetPathRoot(fullName))
Name = fullName;
else
Name = Path.GetFileName(fullName);
RightSelected = false;
SubFolders = new ObservableCollection<FolderItemViewModel>();
}
}
}
感謝 Mark Liversage 在這個答案中為我指出了正確的方向: Select TreeView 在顯示 ContextMenu 之前右鍵單擊節點
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.