TreeView数据绑定

http://www.cnblogs.com/mgen/archive/2011/07/21/2113152.html

[更新至V2]WPF:MVVM简单文件浏览器

image 

 

源代码下载  
2.0 下载页面 
1.0 下载页面 

注意:链接是微软SkyDrive页面,下载时请用浏览器直接下载,用某些下载工具可能无法下载 
源代码环境:Microsoft Visual C# 2008 Express

 

程序ViewModel名称是FileSystemObjectViewModel,它把文件和文件夹统一视为一类,并用FileSystemObjectType枚举来区分类型。ViewModel的逻辑很简单,这里就不再多说了,下面主要介绍一些其他需要注意的地方。

建议下载源代码作参考。

 

在2.0后:

程序使用.NET 4.0中Directory类型所支持的EnumerateDirectories和EnumerateFiles方法。

 

即时数据加载

文件浏览器最基本的性能考虑就应该是数据的即时加载,也就是说展开的文件夹视图都是在用户进行TreeViewItem的展开操作之后才被加载的,当然文件夹视图收缩后已经加载的数据是不会被扔掉的,下次展开会直接打开已经加载的数据。

那么每当一个ViewModel被建立后,初始化函数会进行这样一种操作,如果它是文件夹的话,快速检查当前文件夹是否有子成员(子文件夹或子文件),如果有的话,把一个特殊的没有任何值的特殊ViewModel加在当前ViewModel的Children属性中。这样WPF数据绑定会在当前生成的TreeViewItem的Items加入这个由特殊ViewModel所生成的另一个TreeViewItem,当前TreeViewItem左侧会显示“加号”,代表着当前文件夹有子成员,而事实上,子成员的具体内容只有当该文件夹被用户展开后才会被加载。

MVVM中View中是没有事件代码的,只有XAML,因此我们首先要利用TreeViewItem的IsExpanded依赖属性,同时ViewModel中也有IsExpanded属性,用双向的数据绑定使这两个属性可以互相设置(在本例中其实只存在View设置ViewModel的情况)。

 blog

 

参考FileSystemObjectViewModel中的OnExpanded方法:  

/// <summary>

/// 节点被展开后的操作

/// </summary>

protected virtual void OnExpanded()

{

    //是否有特殊节点

    if (HasSpecialChild)

    {

        //将要展开的节点拥有没有列举的子成员(第一次打开)

 

        //我们需要移除特殊节点,并将子文件夹加入到Children中

        RemoveSpecialChild();

 

        foreach (var dir in GetSubDirs())

            _Children.Add(new FileSystemObjectViewModel(dir, FileSystemObjectType.Folder));

        foreach (var file in GetSubFiles())

            _Children.Add(new FileSystemObjectViewModel(file, FileSystemObjectType.File));

    }

}

 

打开TreeView的虚拟化

  在.NET 3.5后,微软增加了VirtualizingStackPanel对TreeView的支持,虚拟化可以使TreeView得到更客观的性能,但是需要手动声明VirtualizingStackPanel的附加属性来使虚拟化起作用。

<TreeView VirtualizingStackPanel.IsVirtualizing="True"

          VirtualizingStackPanel.VirtualizationMode="Recycling"></TreeView>

VirtualizingStackPanel.VirtualizationMode默认是Standard:创建并丢弃项容器,应该尽可能的使用Recycling:重用项容器。

 

 

从文件或文件夹中提取图标

Windows Forms中有Icon.ExtractAssociatedIcon(MSDN:http://msdn.microsoft.com/en-us/library/system.drawing.icon.extractassociatedicon.aspx),但其缺点是无法从提取文件夹(包括磁盘根目录)的图标,并且在WPF程序中使用需要额外引用System.Drawing命名空间DLL。

因此使用SHGetFileInfo本地Windows API(MSDN:http://msdn.microsoft.com/en-us/library/bb762179.aspx),并将非托管图标资源转换成WPF的BitmapSource。

感谢互联网,就直接用这个链接所提供的代码来做SHGetFileInfo的执行:(http://stackoverflow.com/questions/6008600/get-program-icons),我把它包在一个叫IconExtractor的类中。

 

 

View

      主View:FileSystemView使用ObjectDataProvider来调用ViewModel(FileSystemObjectViewModel)的静态方法来索取当前系统的所有磁盘根目录并将结果绑定在TreeView的DataContext上。

  资源中还包括一个自定义的数据转换器(IconConverter)用来将FileSystemObjectViewModel转换成相应的文件或文件夹图标,并通过Image显示在TreeView的HierarchicalDataTemplate中。

 

FileSystemView:

<UserControl.Resources>

    <ObjectDataProvider x:Key="dataProvider"

                       ObjectType="loc:FileSystemObjectViewModel"

                       MethodName="GetSystemDrives"/>

 

    <loc:IconConverter x:Key="iconConverter" />

 

</UserControl.Resources>

<TreeView VirtualizingStackPanel.IsVirtualizing="True"

         VirtualizingStackPanel.VirtualizationMode="Recycling"

         DataContext="{Binding Source={StaticResource dataProvider}}"

         ItemsSource="{Binding Children}">

 

    <TreeView.ItemTemplate>

        <HierarchicalDataTemplate DataType="loc:FileSystemObjectViewModel"

                                 ItemsSource="{Binding Children}">

 

            <StackPanel Orientation="Horizontal">

                <Image Source="{Binding Converter={StaticResource iconConverter}}" />

                <TextBlock Text="{Binding DisplayName}" />

            </StackPanel>

 

        </HierarchicalDataTemplate>

    </TreeView.ItemTemplate>

 

    <TreeView.ItemContainerStyle>

        <Style TargetType="TreeViewItem">

            <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />

        </Style>

    </TreeView.ItemContainerStyle>

 

</TreeView>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值