簡體   English   中英

如何從 TReeViewItem 上下文菜單訪問 TreeView COntext?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM