MVVM打造无限级TreeView
时间:2010-09-02 来源:囧月
自从了解了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;
}
只是简单的小技巧~本文就到这里。
DEMO地址
相关阅读 更多 +










