今天我来写一篇关于利用WPF来实现Windows的资源管理器功能,当然只是局部实现这个功能,因为在很多时候我们需要来实现对本机资源的管理,当然我们可以使用OpenFileDialog dialog = new OpenFileDialog()这个Microsoft.Win32命名空间下的这个类来实现一些资源查找和导入的功能,但是在很多时候我们可能需要更多的功能,并且希望能够集成到我们自己的项目中,但是我们这个时候就不得不自己来写一套来集成到我们的软件中去了,因为OpenFileDialog这个是无法作为一个UserControl加入到我们的项目中的,当然我们只是实现了其中的一部分功能,因为Windows的资源管理器也是一个重量级的应用,也是十分庞大和复杂的,这里只是通过这个Demo来加深对WPF的MVVM模式及软件基本功的巩固。

  在正式介绍整体框架之前,首先来看看整体的结构,从而对其有一个大概的了解。  

图一 整体界面描述

  整个界面从大的方面来说主要包括三个方面:1 文件及文件夹显示区域、2 导航区域、3 路径显示区域,其实在整个界面中,2和3都是围绕1来进行操作的,三个区域之间的耦合性其实是非常高的,所以常规的做法就是三个部分分为三个UserControl,并且同时绑定到一个ViewModel中,这样整个层次也就比较清晰了,缺点是一个ViewModel中代码太多,职责非常大,所以在这个DEMO中尝试将三个部分分开,三个ViewModel来操作三个View里面的内容,整个实现下来其实也有一些不足之处,那就是容易将问题复杂化,很多在一个类中就能够完成的工作,最终要通过各种类与类之间的耦合来完成,所以通过这个DEMO希望自己能够多一些思考,从而在软件的设计中能够再多一些经验,能够把握好软件粒度的问题,下面就软件的具体内容来深入分析一下。

第一部分:FileList

  这个部分是整个文件和文件夹的显示部分,再三权衡下,决定采用自定义DataGrid的方式来展现整个部分。

<UserControl x:Class="FileSelectorDemo.Views.FileList"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:converter="clr-namespace:FileSelectorDemo.Converters"xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:defines="clr-namespace:FileSelectorDemo.Defines"xmlns:local="clr-namespace:FileSelectorDemo.Views"mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"><UserControl.Resources><converter:CountToVisibilityConverter x:Key="CountToVisibilityConverter"></converter:CountToVisibilityConverter><converter:TypeToVisibleConverter x:Key="TypeToVisibleConverter"></converter:TypeToVisibleConverter><converter:TypeToCollapsedConverter x:Key="TypeToCollapsedConverter"></converter:TypeToCollapsedConverter><converter:CollectionSelectedCountConverter x:Key="CollectionSelectedCountConverter"></converter:CollectionSelectedCountConverter>       </UserControl.Resources><Grid>      <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"><Grid><StackPanel Orientation="Vertical"><DataGrid x:Name="fileList" Style="{StaticResource DefaultDataGrid}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" defines:MouseDoubleClick.Command="{Binding OpenCurrentDirectory}"defines:MouseDoubleClick.CommandParameter="{Binding SelectedItem,RelativeSource={RelativeSource Self}}"  IsReadOnly="True"defines:MouseLeftButtonUpClick.Command="{Binding SelectCurrentFileListItem}" ItemsSource="{Binding CurrentFileList}"  CanUserAddRows="False"  AutoGenerateColumns="False" GridLinesVisibility="None"><DataGrid.Columns><DataGridTemplateColumn  MinWidth="60"><DataGridTemplateColumn.Header><CheckBox Content="全选" Margin="2" FontSize="12" HorizontalAlignment="Center" VerticalAlignment="Center" IsChecked="{Binding DataContext.IsStateCheckAll,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGrid}}}"></CheckBox></DataGridTemplateColumn.Header><DataGridTemplateColumn.CellTemplate><DataTemplate><Grid HorizontalAlignment="Center" VerticalAlignment="Center"><TextBlock Text="不可选" FontSize="12" Foreground="Black" Visibility="{Binding CurrentType,Converter={StaticResource TypeToCollapsedConverter}}" HorizontalAlignment="Left" VerticalAlignment="Center" ></TextBlock><CheckBox IsChecked="{Binding IsSelected,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" IsThreeState="False" IsHitTestVisible="False" Visibility="{Binding CurrentType,Converter={StaticResource TypeToVisibleConverter}}"  HorizontalAlignment="Left" VerticalAlignment="Center" ToolTip="点击选中当前对象"></CheckBox></Grid></DataTemplate></DataGridTemplateColumn.CellTemplate></DataGridTemplateColumn><DataGridTemplateColumn Header="名称" ><DataGridTemplateColumn.CellTemplate><DataTemplate><StackPanel Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Center"><Image Source="{Binding Icon}" Margin="2"  HorizontalAlignment="Center" VerticalAlignment="Center"></Image><TextBlock Text="{Binding Name}" Margin="2" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock></StackPanel></DataTemplate></DataGridTemplateColumn.CellTemplate></DataGridTemplateColumn><DataGridTextColumn Header="修改日期"   Binding="{Binding CreateTime}"/><DataGridTextColumn Header="类型"   Binding="{Binding CurrentType}"/><DataGridTextColumn Header="大小"  Binding="{Binding Size}"/></DataGrid.Columns></DataGrid><TextBlock Margin="2 5" HorizontalAlignment="Left" VerticalAlignment="Center" ><Run>总共 </Run><Run Text="{Binding CurrentFileList.Count,Mode=OneWay}"></Run><Run> 个项目</Run><Run>(已选中 </Run><Run Text="{Binding CurrentFileList,Converter={StaticResource CollectionSelectedCountConverter},Mode=OneWay}"></Run><Run> 个项目)</Run></TextBlock></StackPanel><TextBlock Text="该文件为空"  Visibility="{Binding CurrentFileList.Count,Converter={StaticResource CountToVisibilityConverter}}" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock></Grid></ScrollViewer>          </Grid>
</UserControl>

  这里都是一些常规的定义,这里就不再赘述,DataGrid样式是引用Themes文件夹下面的CustomDataGrid的样式,这里面也包括自定义的ScrollViewer样式和ScrollBar的样式,读者也可以进行思考,另外需要注意设置下面的几个属性。

  1  IsReadOnly="True"这个属性能够保证在鼠标点击时候,不再显示内部的TextBox,从而使使用者不能够随意进行编辑。

  2  GridLinesVisibility="None" 这个能够使整个DataGrid不再显示分割线,从而使其样式更加接近Windows的原生样式。

  3  CanUserAddRows="False" 这个属性也非常重要,不然在整个显示区域的下面会多出一行,当用户点击它的时候会增加行。

  4  AutoGenerateColumns="False"这个就比较熟悉了,一般不让其自动增加列。

  5   SelectionUnit=“FullRow” 表示鼠标点击时选择的单位是整行,而不是其中的单元格或者其他,关于其它的几个枚举值,读者也可查阅相关了解。

  6   SelectionMode=“Extended”允许多选,当按下鼠标的Ctrl键进行点击的时候能够选中多个对象。

  7   最后一个就是关于设置DataGrid的虚拟化容器了,具体设置方法是:

  <Setter Property="VirtualizingStackPanel.IsVirtualizing" Value="True"></Setter><Setter Property="VirtualizingStackPanel.VirtualizationMode" Value="Recycling" />

  WPF中的VirtualizingStackPanel.VirtualizationMode 附加属性指定 ItemsControl 中的面板如何虚拟化其子项。默认情况下,VirtualizingStackPanel 将为每个可见项创建一个项容器,并在不再需要时(比如当项滚动到视图之外时)丢弃该容器。当 ItemsControl 包含多个项时,创建和废弃项容器的过程可能会对性能产生负面影响。如果 VirtualizingStackPanel.VirtualizationMode 设置为 Recycling,VirtualizingStackPanel 将重用项容器,而不是每次都创建新的项容器,这个是摘录自MSDN的相关资料,由于我们加载的DataGrid的项不是很多,如果足够多的情况下,效果可能会更加明显,关于更多的“虚拟化”技术可以参考更多的资料。

  这里我们需要重点关注的是当我们双击DataGridRow时会打开对应的子文件夹,同时单击时会选中当前的DataGridRow,这里关于事件的绑定我们使用的不是System.Windows.Interactivity这种方式来绑定事件的,这里我们通过自定义一个附加属性来实现的,这里以鼠标左键双击为例来进行说明。

  这里需要进行说明的就是在OnMouseDoubleClick中,我们通过当前鼠标的点击的Point来查找最终的DataGridRow 然后触发绑定的Command事件,在前台View中,我们只需要通过绑定到对应的ICommand即可。

    public class MouseDoubleClick{public static DependencyProperty CommandProperty =DependencyProperty.RegisterAttached("Command",typeof(ICommand),typeof(MouseDoubleClick),new UIPropertyMetadata(CommandChanged));public static DependencyProperty CommandParameterProperty =DependencyProperty.RegisterAttached("CommandParameter",typeof(object),typeof(MouseDoubleClick),new UIPropertyMetadata(null));public static void SetCommand(DependencyObject target, ICommand value){target.SetValue(CommandProperty, value);}public static void SetCommandParameter(DependencyObject target, object value){target.SetValue(CommandParameterProperty, value);}public static object GetCommandParameter(DependencyObject target){return target.GetValue(CommandParameterProperty);}private static void CommandChanged(DependencyObject target, DependencyPropertyChangedEventArgs e){Control control = target as Control;if (control != null){if ((e.NewValue != null) && (e.OldValue == null)){control.MouseDoubleClick += OnMouseDoubleClick;}else if ((e.NewValue == null) && (e.OldValue != null)){control.MouseDoubleClick -= OnMouseDoubleClick;}}}private static void OnMouseDoubleClick(object sender, MouseButtonEventArgs e){DataGrid datagrid = sender as DataGrid;Point point = e.GetPosition(datagrid);IInputElement obj = datagrid.InputHitTest(point);DependencyObject target = obj as DependencyObject;while (target != null){if (target is DataGridRow){ICommand command = (ICommand)datagrid.GetValue(CommandProperty);object commandParameter = datagrid.GetValue(CommandParameterProperty);if (null != commandParameter){command.Execute(commandParameter);}break;}target = VisualTreeHelper.GetParent(target);}}}  

defines:MouseDoubleClick.Command="{Binding OpenCurrentDirectory}"  defines:MouseDoubleClick.CommandParameter="{Binding SelectedItem,RelativeSource={RelativeSource Self}}"

  其中我们需要定义命名空间defines,这种方法为我们绑定View层中的各种事件提供能了一种新的方式,这个是我们需要不断去总结和分析的地方。

  第二部分:Navigation

这一部分是我们的导航栏的部分,通过向前、向后、向上等快捷操作,我们能够快速切换文件夹,从而使切换路径变得更加容易,这一部分就是需要着重说一下最近浏览项目这个功能,它能够保存用户最近浏览的10个目录(开发者自定义),从而方便用户迅速切换不同的路径,这个是通过ToggleButton和Popup这一对经典的组合来实现的,具体实现请参考源代码。这一部分重点来说一下当鼠标移动到不同的位置时,能够变换绑定的图标是向前还是向后抑或选中状态,这个其实是通过绑定每一个Model的一个CurrentDirection来实现的,这里需要重点掌握DataTrigger的使用方法。

<Popup Placement="Bottom" PlacementTarget="{Binding ElementName=NavigationPanel}" StaysOpen="False" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"IsOpen="{Binding IsChecked,ElementName=History,Mode=OneWay}" PopupAnimation="Slide"><ItemsControl Background="#f5f5f5" BorderBrush="Gray" BorderThickness="1" Padding="0" ItemsSource="{Binding AttachedDataContext.DirectoryHistory,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type UserControl}}}"><ItemsControl.Template><ControlTemplate TargetType="{x:Type ItemsControl}"><Border x:Name="outer" Padding="0 2" Background="#f5f5f5"><StackPanel Orientation="Vertical" IsItemsHost="True"></StackPanel></Border>                        </ControlTemplate></ItemsControl.Template><ItemsControl.ItemTemplate><DataTemplate><Button x:Name="radioButton" Command="{Binding AttachedDataContext.SwitchDirectory,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type UserControl}}}"CommandParameter="{Binding}"><Button.Template><ControlTemplate TargetType="{x:Type Button}"><Border x:Name="bg" Padding="0" Background="#f5f5f5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"><StackPanel Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Center"><Path x:Name="path" Margin="2" HorizontalAlignment="Center" VerticalAlignment="Center" Stroke="{DynamicResource {x:Static SystemColors.ActiveBorderBrushKey}}"  Width="24" Height="24"Opacity="0" StrokeThickness="2"  StrokeLineJoin="Round"  SnapsToDevicePixels="False">                                                </Path>                                         <Image Source="{Binding Icon}" Width="24" Height="24" Margin="2" HorizontalAlignment="Center" VerticalAlignment="Center"></Image><TextBlock Text="{Binding Name}" Margin="2" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock></StackPanel></Border><ControlTemplate.Triggers><DataTrigger Binding="{Binding CurrentDirection}" Value="选中"><Setter Property="Opacity" Value="1" TargetName="path"></Setter><Setter Property="Data" Value="M 2,10 L 8,14 18,6" TargetName="path"></Setter></DataTrigger><DataTrigger Binding="{Binding CurrentDirection}" Value="向前"><Setter Property="Opacity" Value="0" TargetName="path"></Setter><Setter Property="Data" Value="M8,6 L1,11 8,16 M0,11 L15,11" TargetName="path"></Setter></DataTrigger><DataTrigger Binding="{Binding CurrentDirection}" Value="向后"><Setter Property="Opacity" Value="0" TargetName="path"></Setter><Setter Property="Data" Value="M8,6 L15,11 8,16 M0,11 L15,11" TargetName="path"></Setter></DataTrigger><Trigger Property="IsMouseOver" Value="true"><Setter Property="Background" Value="#91c9f7" TargetName="bg"></Setter><Setter Property="Opacity" Value="1" TargetName="path"></Setter></Trigger></ControlTemplate.Triggers></ControlTemplate></Button.Template>  </Button>                                      </DataTemplate></ItemsControl.ItemTemplate></ItemsControl></Popup>

  第三部分:BreadCrumbView

   这个部分是用来显示当前文件夹路径,并进行快速切换准备的,这个部分是一个组合控件,主要是通过ItemsControl和ToggleButton和Popup来实现的,这个里面需要注意的是这里面绑定的命令和方法都是在FileList的ViewModel中定义的,这里为了最大程度的实现代码的重用。很多时候我们会发现通过这种方式我们需要能够随时访问到FileListViewModel中的内容,这个是整个DEMO中最重要的部分,所以如何才能够引用到FileListViewModel里面的内容呢?

public partial class BreadCrumbView : UserControl{public BreadCrumbView(){InitializeComponent();Loaded +=new RoutedEventHandler(BreadCrumbView_Loaded);}private void BreadCrumbView_Loaded(object sender, RoutedEventArgs e){this.DataContext = new ViewModels.BreadCrumbViewModel(AttachedDataContext);}/// <summary>/// 当前FileList的DataContext对象/// </summary>public object AttachedDataContext{get { return (object)GetValue(AttachedDataContextProperty); }set { SetValue(AttachedDataContextProperty, value); }}// Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...public static readonly DependencyProperty AttachedDataContextProperty =DependencyProperty.Register("AttachedDataContext", typeof(object), typeof(BreadCrumbView), new PropertyMetadata(null));}

    通过定义一个AttachedDataContext对象,我们能够将FileListViewModel中定义的属性分散到各个ViewModel中,这样在一定程度上能够保证避免FileListViewModel中代码过多同时职责过重的问题,但是同时我们也发现了,如果彼此之间的耦合过大,采用这种方式会加重代码之间的复杂度,因为有时不得不通过Action或者事件等方式来进行ViewModel之间的交互和通讯,所以降到这里我们不得不说一些较大较复杂的项目中使用框架的重要性了,比如Prism亦或是Caliburn.Micro等框架能够使整个软甲架构看起来更加清楚和明白,这也是为了更好地增加软件的模块化和灵活性。

  通过这个DEMO的分析,我们需要在不断的实践中去总结这类型的经验,从而使整个软件显得更加合理,最终使自己能够真正地对软件的架构的思想有一个比较深入的了解。

最后需要整个Demo的请点击此处进行下载!

转载于:https://www.cnblogs.com/seekdream/p/8323959.html

WPF实现Windows资源管理器(附源码)相关推荐

  1. ESC快速关闭windows资源管理器窗口源码

    介绍: 源码的操作原理很简单,主要是比较喜欢键盘操作,大部分窗口按esc都可以关闭. 比如微信,QQ之类的,但是windows自带的windows资源管理器无法用esc关闭. 所以写了一个小工具,需要 ...

  2. 【转】Visual Studio团队资源管理器 Git 源码管理工具简单入门

    1.1 环境 Visual Studio + GitLab (其他版本同理) 1.2 Git操作过程图解 1.3 常见名词解释 拉取(Pull):将远程版本库合并到本地版本库,相当于(Fetch+Me ...

  3. 【Python工具】Python实现一款支持各大平台的视频下载器 | 附源码

    相关文件 想学Python的小伙伴可以关注小编的公众号[Python日志] 有很多的资源可以白嫖的哈,不定时会更新一下Python的小知识的哈!! 需要源码的小伙伴可以在公众号回复视频下载器 简介 一 ...

  4. php网页播放器源码免费,基于Flowplayer打造一款免费的WEB视频播放器附源码

    Flowplayer 是一个开源(GPL 3的)WEB视频播放器.您可以将该播放器嵌入您的网页中,如果您是开发人员,您还可以自由订制跟配置播放器相关参数以达到您要的播放疗效.本文主要介绍Flowpla ...

  5. 【博主推荐】html好看的音乐播放器(附源码)

    html好看的音乐播放器 文章目录 1.主界面列表效果 2.主界面详情效果 3.背景风格1-背景色轮动 4.背景风格2-图片轮动 5.MP3免费下载 5.1 MyFreeMP3 5.2.铜钟音乐 源码 ...

  6. 360player全景图播放器-附源码

    开发了一个全景图播放器,自带全景展示.背景音乐.重力感应等功能,不用写代码,通过配置文件就可以展示全景图片. 360plyaer使用说明 1.360player是基于浏览器的全景图播放器,浏览器需要支 ...

  7. MFC Windows 程序设计[315]之磁盘文件列举器(附源码)

    MFC Windows 程序设计[315]之磁盘文件列举器 程序之美 前言 主体 运行效果 核心代码 逻辑分析 结束语 程序之美 前言 MFC是微软公司提供的一个类库(class libraries) ...

  8. MFC Windows 程序设计[108]之取色器(附源码)

    MFC Windows 程序设计[108]之取色器 程序之美 前言 主体 运行效果 核心代码 逻辑分析 结束语 程序之美 前言 MFC是微软公司提供的一个类库(class libraries),以C+ ...

  9. SSM+图书馆电子文件资源管理 毕业设计-附源码191614

    SSM图书馆电子文件资源管理系统 摘 要 信息化社会内需要与之针对性的信息获取途径,但是途径的扩展基本上为人们所努力的方向,由于站在的角度存在偏差,人们经常能够获得不同类型信息,这也是技术最为难以攻克 ...

最新文章

  1. 如何做产品路线图规划?
  2. Selenium_等待页面加载完毕
  3. JavaMail发送和接收邮件
  4. spring boot之从零开始开发自己的网站
  5. java控制系统音量_Java 控制 Windows 系统音量-Go语言中文社区
  6. CF1479D Odd Mineral Resource
  7. require.jsAMD模块化编程
  8. 收藏 | 人脸检测之Retinaface
  9. 推荐一个非常好用的进程管理器
  10. 如何在服务器运行脚本精灵,脚本精灵循环方式教程
  11. 浅谈人工智能(AI)
  12. Scala下载安装和环境变量配置
  13. 人人视频android资源比ios多,人人视频
  14. 浅谈企业知识资产管理及建设思路
  15. 【论文笔记】ego_planner
  16. 万字长文人脸识别深度研究:发展与市场、市场研究、流程及主要技术、行业应用、产品落地和个人看法
  17. AcrelCloud-9500电瓶车充电桩收费平台在公共场所中的应用
  18. VS Code 修改字体 + 取消注释斜体 + 修改注释颜色
  19. conda 环境导入导出 yaml
  20. 合肥工业大学2022大数据技术实验一

热门文章

  1. fragment error
  2. Eclipse Removing obsolete files from server 问题
  3. C#中char[]与string之间的转换
  4. 扩展js string 方法
  5. ASP.NET DEMO 12 : CheckBoxList 实现单选
  6. BAC--Downtime 凌晨时段设定
  7. 在 Linux 下确认 NTP 是否同步的方法
  8. HTTPS加密那点事--轻松秒懂HTTPS非对称加密
  9. css中那些容易被我们程序猿所忽略的选择器
  10. 望手指半月痕可知内脏疾病