自从了解了MVVM,就迷恋上了它,基本上抛弃了传统的开发方式~
前些天无意中看到一篇关于用MVVM打造TreeView的文章(http://www.codeproject.com/KB/WPF/TreeViewWithViewModel.aspx),感觉比较美妙,遂拿来简单改造一下,使它支持无限级。
首先,定义一个实体类:
public class TreeItem { readonly List<TreeItem> _children = new List<TreeItem>(); public List<TreeItem> Children { get { return _children; } } public string Name { get; set; } public int ID { get; set; } public int ParentID { get; set; } }
接下来,把TreeViewItemViewModel、以及PropertyChangedBase直接搬过来。
然后定义一个ViewModel:
public class TreeItemViewModel : TreeViewItemViewModel { private TreeItem item; public TreeItemViewModel(TreeItem item) : this(item, null) { } public TreeItemViewModel(TreeItem item, TreeItemViewModel parent) : base(parent, true) { this.item = item; } public string Name { get { return item.Name; } set { item.Name = value; this.NotifyPropertyChanged(p => p.Name); } } protected override void LoadChildren() { var loadedData = TreeItemDemoData.LoadChildrens(item.ID); if (loadedData.Count > 0) { Children.Clear(); foreach (var t in loadedData) { Children.Add(new TreeItemViewModel(t, this)); } } } }
以及相关Demo数据:
public class TreeItemDemoData { private static readonly List<TreeItem> data; static TreeItemDemoData() { data = new List<TreeItem>(); for (int i = 0; i < 10; i++) { data.Add(new TreeItem { Name = string.Format("Name {0}", i.ToString()), ID = i, ParentID = i - 1 }); } } public static List<TreeItem> LoadChildrens(int parentId) { return data.Where(t => t.ParentID == parentId).ToList(); } }
定义界面:
<Window x:Class="TreeViewDemo.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:vm="clr-namespace:TreeViewDemo.ViewModel" Title="TreeViewDemo" Height="300" Width="300" > <StackPanel> <TreeView ItemsSource="{Binding}"> <TreeView.ItemContainerStyle> <Style TargetType="{x:Type TreeViewItem}"> <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" /> <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" /> <Setter Property="FontWeight" Value="Normal" /> <Style.Triggers> <Trigger Property="IsSelected" Value="True"> <Setter Property="FontWeight" Value="Bold" /> </Trigger> </Style.Triggers> </Style> </TreeView.ItemContainerStyle> <TreeView.Resources> <HierarchicalDataTemplate DataType="{x:Type vm:TreeItemViewModel}" ItemsSource="{Binding Children}"> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding Name}" /> </StackPanel> </HierarchicalDataTemplate> </TreeView.Resources> </TreeView> </StackPanel> </Window>
最后,绑定数据:
public partial class Window1 : Window { public Window1() { InitializeComponent(); DataContext = new ObservableCollection<TreeItemViewModel>( TreeItemDemoData.LoadChildrens(0).Select(t => new TreeItemViewModel(t)) ); } }
OK,完成,发现HierarchicalDataTemplate挺神奇的~_~。
相信TreeView应用范围还是相当广的,比如某些应用的数据N级分类。
当然,如果是数据库应用,可能会有一定延迟,造成界面的假死,在此对本文例子稍加改造,使用多线程提高UI的响应速度。
对TreeItemViewModel进行改造:
protected override void LoadChildren() { PreLoad(); } private string originalName; private Dispatcher uiThreadDispatcher; private void PreLoad() { originalName = Name; Name = Name + " (expanding...)"; uiThreadDispatcher = Dispatcher.CurrentDispatcher; ThreadPool.QueueUserWorkItem(Loading); } private void Loading(object state) { Thread.Sleep(500);//模拟延迟 var loadedData = TreeItemDemoData.LoadChildrens(item.ID); if (loadedData.Count > 0) { uiThreadDispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() => Children.Clear())); foreach (var t in loadedData) { uiThreadDispatcher.BeginInvoke(DispatcherPriority.Background, new Action<TreeItem>(vm => Children.Add(new TreeItemViewModel(vm, this))), t); } } Name = originalName; }
只是简单的小技巧~本文就到这里。