寫在前面:在(一)中,介紹了TreeView控件MVVM模式下數據綁定的方法。在這篇文章中,將總結給節點添加事件的方法,這樣說有些不對,總之實現的效果就是點擊某個節點,將出現對應於該節點的頁面或者數據。(我這里用的方法肯定不是最好的,但是是我能想到的最佳方法了,WPF初學者,希望大家多多指教。)
Example#1: 實現下圖功能,點擊左側treeview姓名節點,在右側會出現響應的detailed information. 可以將ID的textbox中的text屬性綁定到treeview中SelectedItem
先構造兩個類,一個是User,一個是TreeNode。User是TreeNode的一個屬性。

public class User { public string Key { get; set; } public string Name { get; set; } public int? Age { get; set; } public User() { Key = null; Name = null; Age = null; } }

public class TreeNode { public int NodeID { get; set; } public int ParentID { get; set; } public string NodeName { get; set; } public List<TreeNode> ChildNodes { get; set; } public User user { get; set; } public TreeNode() { ChildNodes = new List<TreeNode>(); user = new User(); } }
綁定:
<TreeView Grid.Column="0" FontSize="15" ItemsSource="{Binding Path=Nodes}" x:Name="treeview"> <TreeView.ItemTemplate> <HierarchicalDataTemplate DataType="{x:Type local:TreeNode}" ItemsSource="{Binding Path=ChildNodes}"> <Label Content="{Binding Path=NodeName}"/> </HierarchicalDataTemplate> </TreeView.ItemTemplate> </TreeView> <TextBox Text="{Binding ElementName=treeview, Path=SelectedItem.user.Key}" Canvas.Left="70" Width="200" Canvas.Top="8" FontSize="15"/> <TextBox Text="{Binding ElementName=treeview, Path=SelectedItem.user.Age}" Canvas.Left="70" Width="200" Canvas.Top="8" FontSize="15"/>
上面兩句TextBox控件,就是將項目中名為“treeview"的控件的SelectedItem.user.Key和SeletedItem.user.Age的值綁定到Text屬性中。這樣點”Lily"節點,右側就會出現相應的信息。
Example#2:
上面的例子比較簡單,第二個例子將button控件作為treeviewitem,並給button控件綁定一個Command。
場景描述:左側是treeview,其中每個treeviewitem的元素都是button控件,點擊每個節點,中間的listview中會出現符合條件的學生的姓名,比如,是Grade1的學生有Lucy, Tom和Lily三人。是Grade2Class1的學生有Sam和Jack兩人。點擊listview中的學生姓名,右側會顯示學生的ID和Age信息。
TreeView部分的XAML代碼:
<TreeView Grid.Column="0" FontSize="15" ItemsSource="{Binding Path=Nodes}"> <TreeView.ItemTemplate> <HierarchicalDataTemplate DataType="{x:Type local:TreeNode}" ItemsSource="{Binding Path=ChildNodes}"> <Button Content="{Binding NodeName}" Command="{Binding DataContext.TreeViewCommand, RelativeSource={RelativeSource AncestorType=local:MainWindow}}" CommandParameter="{Binding Path=NodeID}" Background="White" BorderThickness="0"/> </HierarchicalDataTemplate> </TreeView.ItemTemplate> </TreeView>
可以看見,TreeViewItem元素從Label變成了Button。其中Button控件綁定了一個TreeViewCommand。這里需要指明綁定的是DataContext下的TreeViewCommand,否則默認的是TreeNode類型中的TreeViewCommand屬性。因此下面這句是非常關鍵的。
Command="{Binding DataContext.TreeViewCommand,
RelativeSource={RelativeSource AncestorType=local:MainWindow}}" CommandParameter="{Binding Path=NodeID}"
ListView部分的XAML代碼:
<ListView Name="listview" Grid.Column="1" ItemsSource="{Binding Users}" IsSynchronizedWithCurrentItem="True" BorderBrush="DarkGray" BorderThickness="5"> <ListView.View> <GridView> <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Path=Name}" Width="auto"/> </GridView> </ListView.View> </ListView>
ViewModel代碼:

public class ViewModel :INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private List<User> userlist = new List<User>(); private List<TreeNode> nodes; public List<TreeNode> Nodes { get { return nodes; } set { nodes = value; if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Nodes")); } } private List<User> users = new List<User>(); public List<User> Users { get { return users; } set { users = value; if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Users")); } } public DelegateCommand TreeViewCommand { get; } public ViewModel() { // 初始化Nodes和Users,一般Users是要訪問數據庫得到的,這里進行了簡化。 InitiateNodes(); InitiateUsers(); TreeViewCommand = new DelegateCommand(TreeViewCommandHandler); } private void TreeViewCommandHandler(object sender, DelegateCommandEventArgs e) { int id = Convert.ToInt32(e.Parameter); switch (id) { case 1: Users = userlist.Where(x => x.GradeNum == 1).ToList(); break; case 2: Users = userlist.Where(x => x.GradeNum == 2).ToList(); break; case 3: Users = userlist.Where(x => x.GradeNum == 3).ToList(); break; case 4: Users = userlist.Where(x => x.GradeNum == 1 && x.ClassNum == 1).ToList(); break; case 5: Users = userlist.Where(x => x.GradeNum == 1 && x.ClassNum == 2).ToList(); break; case 6: Users = userlist.Where(x => x.GradeNum == 2 && x.ClassNum == 1).ToList(); break; case 7: Users = userlist.Where(x => x.GradeNum == 2 && x.ClassNum == 2).ToList(); break; case 8: Users = userlist.Where(x => x.GradeNum == 3 && x.ClassNum == 1).ToList(); break; case 9: Users = userlist.Where(x => x.GradeNum == 3 && x.ClassNum == 2).ToList(); break; } } #region Initial methods private void InitiateNodes() { List<TreeNode> _nodes = new List<TreeNode>() { new TreeNode() { ParentID=0,NodeID=1,NodeName="Grade1" }, new TreeNode() { ParentID=0,NodeID=2,NodeName="Grade2" }, new TreeNode() { ParentID=0, NodeID=3, NodeName="Grade3" }, new TreeNode(){ParentID=1,NodeID=4,NodeName="Class1"}, new TreeNode(){ParentID=1,NodeID=5,NodeName="Class2"}, new TreeNode(){ParentID=2,NodeID=6,NodeName="Class1"}, new TreeNode(){ParentID=2, NodeID=7, NodeName="Class2"}, new TreeNode(){ParentID=3, NodeID=8, NodeName="Class1"}, new TreeNode(){ParentID=3, NodeID=9, NodeName="Class2"} }; Nodes = getChildNodes(0, _nodes); } private List<TreeNode> getChildNodes(int parentID, List<TreeNode> nodes) { List<TreeNode> mainNodes = nodes.Where(x => x.ParentID == parentID).ToList(); List<TreeNode> otherNodes = nodes.Where(x => x.ParentID != parentID).ToList(); foreach (TreeNode node in mainNodes) node.ChildNodes = getChildNodes(node.NodeID, otherNodes); return mainNodes; } private void InitiateUsers() { User Lily = new User() { Name = "Lily", Age = 12, GradeNum = 1, ClassNum = 1 }; User Tom = new User() { Name = "Tom", Age = 11, GradeNum = 1, ClassNum = 1 }; User Lucy = new User() { Name = "Lucy", Age = 12, GradeNum = 1, ClassNum = 2 }; User Sam = new User() { Name = "Sam", Age = 13, GradeNum = 2, ClassNum = 1 }; User Jack = new User() { Name = "Jack", Age = 13, GradeNum = 2, ClassNum = 1 }; User Ray = new User() { Name = "Ray", Age = 13, GradeNum = 2, ClassNum = 2 }; User Lisa = new User() { Name = "Lisa", Age = 14, GradeNum = 3, ClassNum = 1 }; User Liz = new User() { Name = "Liz", Age = 14, GradeNum = 3, ClassNum = 2 }; userlist.Add(Liz); userlist.Add(Lisa); userlist.Add(Sam); userlist.Add(Lucy); userlist.Add(Tom); userlist.Add(Lily); userlist.Add(Jack); userlist.Add(Ray); } #endregion }
其中有兩個類,DelegateCommand和DelegateCommandEventArgs,是繼承自ICommand,然后委托方法的。

public class DelegateCommand : ICommand { // 定義一個名為SimpleEventHandler的委托,兩個參數,一個object類,一個是自定義的DelegateCommandEventArgs類 public delegate void SimpleEventHandler(object sender, DelegateCommandEventArgs e); // handler是方法,別忘了,委托是用於定義方法的類 private SimpleEventHandler handler; private bool isEnabled = true; public DelegateCommand(SimpleEventHandler handler) { this.handler = handler; } public void Execute(object parameter) { this.handler(this, new DelegateCommandEventArgs(parameter)); } public bool CanExecute(object parameter) { return this.isEnabled; } public event EventHandler CanExecuteChanged; public bool IsEnabled { get { return this.isEnabled; } set { this.isEnabled = value; this.OnCanExecuteChanged(); } } private void OnCanExecuteChanged() { if (this.CanExecuteChanged != null) this.CanExecuteChanged(this, EventArgs.Empty); } }

public class DelegateCommandEventArgs : EventArgs { private object parameter; public DelegateCommandEventArgs(object parameter) { this.parameter = parameter; } public object Parameter { get { return this.parameter; } } }
2020.06.25更新內容
之前使用Button控件作為TreeViewItem,然后為Button的Command屬性綁定事件。更新為直接將事件綁定為TreeView的SelectedItemChanged屬性,同樣可以實現一樣的效果。詳情可見: