WPF之Treeview實現MVVM雙向繫結
阿新 • • 發佈:2019-12-24
Treeview
分別有兩個資料模板HierarchicalDataTemplate
(層級資料模板)和DataTemplate
(資料模板),分別應用於生成子資料項和普通資料項。
在使用過程中,如果對兩個模板的DataType
設定為同一型別,執行時會直接報錯。
大概原因是添加了倆個相同Key的資源(因為HierarchicalDataTemplate
和DataTemplate
都是定義在<TreeView.Resources>
標籤中)。
程式碼:
public class TypeTreeModel :TypeModel { public ObservableCollection<TypeTreeModel> ChildList { get; set; } = new ObservableCollection<TypeTreeModel>(); } public class TypeModel { public int Id { get; set; } public string Name { get; set; } } <TreeView x:Name="treeView" ItemsSource="{Binding TypeList}" MinWidth="200" MaxHeight="200" > <TreeView.Resources> <HierarchicalDataTemplate DataType="{x:Type vm:TypeTreeModel}" ItemsSource="{Binding ChildList}"> <TextBlock Text="{Binding Name}" Margin="3 2"/> </HierarchicalDataTemplate> <DataTemplate DataType="{x:Type vm:TypeModel}"> <TextBlock Text="{Binding Name}" ToolTip="{Binding Id}" Margin="3 2"></TextBlock> </DataTemplate> </TreeView.Resources> </TreeView>
效果圖:
TypeList
一般都是資料庫取出來的資料處理巢狀後的結果。有時候會遇到需求,對於不同的分類有不同的型別。根據雙向繫結的規則,我們只需要修改ViewModel
中TypeList
的內容就可以了。
注意: ObservableCollection
只有在列表項發生變化時才會觸發頁面重新整理,即新增或刪除時才會反應到頁面上。若採用直接賦值的寫法,不會觸發頁面重新整理。
推薦更新資料時候的寫法:
//清空原先的列表
TypeList.Clear();
list.ForEach(d =>
{
TypeList.Add(d);
});
TreeView.SelectedItem雙向繫結
TreeView.SelectedItem
是ReadOnly
的,所以不能通過簡單的繫結方法去獲取選中項
最終還是要通過繫結Treeview
的SelectedItemChanged
事件,來修改ViewModel
中的資料
1.直接繫結事件
<TreeView ItemsSource="{Binding TypeList}" SelectedItemChanged="TreeView_OnSelectedItemChanged" /> private void TreeView_OnSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e) { vm.SelectItem = (Cluster)e.NewValue; }
2.通過Command
繫結事件
- 在專案中引用
System.Windows.Interactivity.WPF
(簡單來說該外掛可以將頁面控制元件的Event
轉為ViewModel中的Command
) - 在窗體中新增引用
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
- 繫結
Command
到SelectedItemChanged
事件
<TreeView x:Name="treeView" ItemsSource="{Binding TypeList}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectedItemChanged">
<i:InvokeCommandAction Command="{Binding SelectItemChangeCommand}"
CommandParameter="{Binding ElementName=treeView,Path=SelectedItem}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TreeView>
ViewModel:
public class TreeViewModel : ViewModelBase
{
public ObservableCollection<TypeTreeModel> TypeList;
private TypeModel selectItem;
public TypeModel SelectItem
{
get { return selectItem; }
set { this.MutateVerbose(ref selectItem, value, RaisePropertyChanged()); }
}
public TreeViewModel()
{
TypeList = GetData();
}
public ICommand SelectItemChangeCommand
{
get
{
return new CommandBase((param) =>
{
if(param != null)
SelectItem = (TypeModel)param;
});
}
}
}
效果圖:
ViewModelBase
和CommandBase
是自己封裝的基類,就是為了寫WPF的雙向繫結簡單點,有興趣的可以評論向我要