首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計疑難解答

WPF C# TreeView 動態右鍵選單新增子節點:完整 MVVM 與 Code-Behind 實作

     編輯:程式設計疑難解答

在 WPF 中為 `TreeView` 節點綁定右鍵選單,並實現「根節點/子節點」動態新增子節點。本文提供 **純 Code-Behind** 與 **MVVM (Command + HierarchicalDataTemplate)** 兩種完整實作,涵蓋資料模型、樣式、右鍵選單、命令綁定。

核心資料模型

public class TreeNode : INotifyPropertyChanged
{
    public string Header { get; set; }
    public ObservableCollection Children { get; } = new ObservableCollection();
    public TreeNode Parent { get; set; }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string n = null) =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(n));
}

方案一:Code-Behind 快速實作(適合原型/小專案)

XAML

<TreeView x:Name="treeView">
    <TreeView.Resources>
        <HierarchicalDataTemplate DataType="{x:Type local:TreeNode}" ItemsSource="{Binding Children}">
            <TextBlock Text="{Binding Header}" />
        </HierarchicalDataTemplate>
    </TreeView.Resources>
    <TreeView.ItemContainerStyle>
        <Style TargetType="TreeViewItem">
            <Setter Property="ContextMenu">
                <Setter.Value>
                    <ContextMenu>
                        <MenuItem Header="新增子節點" Click="AddChild_Click" />
                        <MenuItem Header="重新命名" Click="Rename_Click" />
                        <MenuItem Header="刪除" Click="Delete_Click" />
                    </ContextMenu>
                </Setter.Value>
            </Setter>
        </Style>
    </TreeView.ItemContainerStyle>
</TreeView>

Code-Behind

private void AddChild_Click(object sender, RoutedEventArgs e)
{
    var menuItem = sender as MenuItem;
    var treeViewItem = menuItem?.Parent as ContextMenu?.PlacementTarget as TreeViewItem;
    var parentNode = treeViewItem?.DataContext as TreeNode;
    if (parentNode == null) return;

    var newNode = new TreeNode { Header = $"子節點 {parentNode.Children.Count + 1}", Parent = parentNode };
    parentNode.Children.Add(newNode);
    // 展開父節點以顯示新子節點
    treeViewItem.IsExpanded = true;
}

private void Delete_Click(object sender, RoutedEventArgs e)
{
    var menuItem = sender as MenuItem;
    var treeViewItem = menuItem?.Parent as ContextMenu?.PlacementTarget as TreeViewItem;
    var node = treeViewItem?.DataContext as TreeNode;
    node?.Parent?.Children.Remove(node);
}

方案二:MVVM 標準實作(適合正式專案)

ViewModel

public class TreeViewModel : INotifyPropertyChanged
{
    public ObservableCollection Roots { get; } = new ObservableCollection();

    public ICommand AddChildCommand { get; }
    public ICommand DeleteCommand { get; }

    public TreeViewModel()
    {
        AddChildCommand = new RelayCommand(node => {
            var child = new TreeNode { Header = $"子節點 {node.Children.Count + 1}", Parent = node };
            node.Children.Add(child);
        });
        DeleteCommand = new RelayCommand(node => node.Parent?.Children.Remove(node));
    }
    // INotifyPropertyChanged 實作省略
}

// 簡易 RelayCommand
public class RelayCommand : ICommand
{
    private readonly Action _execute;
    public RelayCommand(Action execute) => _execute = execute;
    public bool CanExecute(object p) => true;
    public void Execute(object p) => _execute((T)p);
    public event EventHandler CanExecuteChanged;
}

XAML (MVVM)

<TreeView ItemsSource="{Binding Roots}">
    <TreeView.Resources>
        <HierarchicalDataTemplate DataType="{x:Type local:TreeNode}" ItemsSource="{Binding Children}">
            <TextBlock Text="{Binding Header}" />
        </HierarchicalDataTemplate>
    </TreeView.Resources>
    <TreeView.ItemContainerStyle>
        <Style TargetType="TreeViewItem">
            <Setter Property="ContextMenu">
                <Setter.Value>
                    <ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
                        <MenuItem Header="新增子節點" Command="{Binding DataContext.AddChildCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}" CommandParameter="{Binding}" />
                        <MenuItem Header="刪除" Command="{Binding DataContext.DeleteCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}" CommandParameter="{Binding}" />
                    </ContextMenu>
                </Setter.Value>
            </Setter>
        </Style>
    </TreeView.ItemContainerStyle>
</TreeView>

關鍵技巧總結

需求技巧
右鍵選單知道被點擊的節點`ContextMenu.PlacementTarget` → `TreeViewItem` → `DataContext` 即為 `TreeNode`
MVVM 中 Command 取得參數`CommandParameter="{Binding}"` 傳遞當前 `DataContext` (TreeNode)
動態新增後自動展開Code-Behind 設 `treeViewItem.IsExpanded = true`;MVVM 用 `IsExpanded` 綁定屬性
根節點也能右鍵新增根節點同樣有 `ContextMenu`,`Parent` 為 null,邏輯通用

常見坑

  • ❌ `ContextMenu` 的 `DataContext` 不屬於視覺樹 → 用 `PlacementTarget.DataContext` 取得節點
  • ❌ `ObservableCollection` 未引用 `System.Collections.ObjectModel`
  • ❌ `HierarchicalDataTemplate` 的 `DataType` 未對應到具體類別 → 節點不顯示
  • ✅ 節點類別實作 `INotifyPropertyChanged`,UI 才能自動更新

掌握 `HierarchicalDataTemplate` + `ContextMenu.PlacementTarget` + `CommandParameter` 三大關鍵,即可在 WPF 中優雅實現 TreeView 動態右鍵新增子節點。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved