TreeView In WPF

涉及WPF相关知识点

DependencyProperty、Command、MarkupExtension、DataBind、Style、Template、 Resource、Trigger

详细细节参考相关技术文章

Example 介绍

功能

文本框中输入字符模糊查找树中的节点,如果匹配则自动展开树并高亮匹配节点。

文件结构说明

Model文件夹下是持久化数据对象(PO),例子中使用MockDatabase来模拟数据库持久化。

ViewModel文件夹下是现实数据对象(VO),用来和WPF控件进行数据绑定。

关键点说明

TreeView
base.DataContext = GroupTreeViewModel.Instance;

MainWindow.xaml.cs文件中为整个MainWindow绑定一个数据源

<TreeView ItemsSource="{Binding Path=Groups}">

TreeView和GroupTreeViewModel中的Groups属性进行绑定,是一次性单项绑定(即在程序启动时把数据往界面上绑定一次,不带界面数据同步)。

<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设置样式,其中Property="IsExpanded"、Property="IsSelected"、 Property="FontWeight"都是TreeViewItem的控件属性。

{Binding IsExpanded, Mode=TwoWay},这里的IsExpanded是RoomVO或者GroupVO的属性(不是GroupTreeViewModel的属性,因为通过TreeView自身的绑定之后数据源发生了变化,TreeViewItem是TreeView的子节点,在WPF中子节点是继承父节点的数据源的,就像TreeView继承Window的数据源一样)

Mode=TwoWay,表示界面和数据双向绑定(即TreeViewItem.IsExpanded和 GroupVO.IsExpanded、RoomVO.IsExpanded做同步变更)

标记扩展{Binding ...}用于为目标对象进行数据绑定,目标对象必须是一个依赖属性;源对象则可以是一个普通的属性。如果源对象发生数据变化需要通知目标对象进行同步,则绑定的数据源类型必须实现INotifyPropertyChanged接口。也就是说只填写Mode=TwoWay,不实现 INotifyPropertyChanged接口是无法完成目标数据和源数据的双向同步的。

this.FirePropertyChanged("IsSelected");
private void FirePropertyChanged(string propertyName) {
    if(this.PropertyChanged != null) {
        this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

例子中的GroupVO.cs和RoomVO.cs里都有如下代码,用来完成TreeViewItem和GroupVO、RoomVO中的 IsSelected属性值的双向同步绑定

<TreeView.Resources>
    <HierarchicalDataTemplate 
        DataType="{x:Type m:GroupVO}" 
        ItemsSource="{Binding Rooms}">
        <StackPanel Orientation="Horizontal">
            <Image Width="16" Height="16" Margin="3,0" Source="Images\GroupIcon.png" />
            <TextBlock Text="{Binding FriendlyName}" />
        </StackPanel>
    </HierarchicalDataTemplate>
    <DataTemplate DataType="{x:Type m:RoomVO}">
        <StackPanel Orientation="Horizontal">
            <Image Width="16" Height="16" Margin="3,0" Source="Images\RoomIcon.png" />
            <TextBlock Text="{Binding FriendlyName}" />
        </StackPanel>
    </DataTemplate>
</TreeView.Resources>

为TreeView定义模板,用来表述树中各各节点和数据的绑定关系。HierarchicalDataTemplate是有层级结构的模板,DataTemplate是普通数据模板。

DataType="{x:Type m:GroupVO}"指定HierarchicalDataTemplate的数据类型,DataType="{x:Type m:RoomVO}指定DataTemplate的数据类型,由于TreeView同时定义了两个数据模板,而数据源只有一个(GroupTreeViewModel),WPF迷茫了,不知道怎么绑定数据,所以需要为每个模板写明数据类型。当然如果只有一个模板的情况下,可以忽略DataType,WPF会自动识别。

x和m,都是命名空间的缩写,在MainWindow.xaml的头上都做了定义

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:m="clr-namespace:TestTreeView.ViewModel"

x:Type,Type是WPF的关键字

m:GroupVO,就是TestTreeView.ViewModel.GroupVO的类型

Search
<TextBox x:Name="TextBoxSearch"
    KeyDown="TextBoxSearch_KeyDown"
    Margin="0,0,4,0"
    Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}"
    Width="150" Height="21" />
<Button Command="{Binding m:SearchCommand}"
    Content="搜索"
    Padding="8,0" Height="22" Width="50" Click="SearchButton_Click" />
Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}"

当TextBox.Text属性发生变化,更新数据源SearchText

x:Name="TextBoxSearch"

定义了一个TextBox TextBoxSearch变量,在后台代码可以访问

Command="{Binding m:SearchCommand}"

将Button的点击事件和一个ICommand绑定,Command在GroupTreeViewModel.cs中定义如下:

private class SearchTreeCommand : ICommand {
    readonly GroupTreeViewModel model;

    public SearchTreeCommand(GroupTreeViewModel model) {
        this.model = model;
    }

    public bool CanExecute(object parameter) {
        return true;
    }

    event EventHandler ICommand.CanExecuteChanged {
        add { }
        remove { }
    }

    public void Execute(object parameter) {
        model.Search();
    }
}

代码:Code

posted @ 2012-10-09 23:08  CanMusic  阅读(926)  评论(2编辑  收藏  举报