在 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));
}
<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>
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);
}
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;
}
<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,邏輯通用 |
掌握 `HierarchicalDataTemplate` + `ContextMenu.PlacementTarget` + `CommandParameter` 三大關鍵,即可在 WPF 中優雅實現 TreeView 動態右鍵新增子節點。