WPF:自定义Metro样式文件夹选择对话框FolderBrowserDialog
1.前言
WPF并没有文件选择对话框,要用也就只有使用Winform版的控件。至今我也没有寻找到一个WPF版本的文件选择对话框。
可能是我眼浊,如果各位知道有功能比较健全的WPF版文件选择对话框、文件打开对话框,还请留言告知。
这次做的是一个精简版的文件选择对话框。包含一个UserControl和一个承载UserControl的Window。
另外TreeView的样式引用自Mahspps中的样式。也就是如果需要使用这个文件选择对话框,就必须要引用Mahapps的相关dll。
当然,我会提供整个项目的源代码。如果大家不嫌弃,可以自己移除项目中的Mahspps相关引用。当然,这样做可能会使得控件界面显得比较难看,不过你还可以自己给TreeView提供你自己喜欢的样式。
这个文件选择对话框功能比较精简,仅仅提供文件夹选择的功能。其他诸如右键菜单、新建文件夹、文件夹拖放等功能目前并未实现。要完整使用WPF实现微软完整版的FolderBrowserDialog,我相信这工作量还是相当大的。
2.开始分析
先打开Winform版本的文件选择对话框看看是什么样子:

可以看到,从上到下依次是:桌面(不可展开)、库、计算机(展开就是各个磁盘驱动器)、网络、控制面板、回收站、以及桌面上的各个文件夹。
不管微软的这个习惯设计得怎么样,反正用户现在也习惯了这个设计。所以,本次实现的WPF版文件选择对话框也大致采用这个设计。其中有几点:
库用得太少了,去掉。
网络这个似乎有些是要输入密码的,我这没有远程的共享主机,所以不清楚微软这个是怎么让用户输入密码的。暂不实现。
至于控制版面和回收站,我不知道微软把这两个放在文件选择对话框里面算是什么意思?活跃气氛么~~~
其实说到这里,我有个想法。微软这个文件选择对话框里面的操作文件的方式(包括右键菜单、文件拖放等)和系统的资源管理器实在太像了。所以我怀疑这个文件选择对话框
里面的那棵树实际上是嵌入的一个另类的资源管理器,而并非微软单独开发的一个树。。。这样说来,也可以解释为什么微软不提供WPF版的文件选择对话框了,因为Explorer内部实现可能不是采用的WPF方式实现,所以改起来工作量很大...然后就不提供了。
当然,这些都是猜测,猜测。。
好了,话说回来,这个控件最关键的一点就是怎么把整个磁盘的文件通过一棵树加载起来,总不能初始化的时候先把整个磁盘的文件组织成一棵树?效率的原因决不允许你那样做。
有没有一种方法可以在用户展开某个节点的时候才初始化它的子节点?
微软为TreeView提供了一个模版:HierarchicalDataTemplate
使用这个模版,当用户展开某个节点时,TreeView会展开这个节点并且初始化这个节点的子节点的子节点,也就是HierarchicalDataTemplate第一次初始化的时候就被初始化第一层和第二层,当用户展开第一层的时候,它就开始初始化第三层...依次类推。因为使用这个模版初始化TreeView效率还是相当可观的。
到此,我们为TreeView准备一个模型(Model):
这个模型应该至少有这几个属性:
1.Name,节点的名称
2.FullName,节点所在位置的完整磁盘路径
3.Children,节点的所有子节点
另外你如果还需要其他类似图标等等也可以继续加。
在本例中,Model如下:

可以看到,多出了一个Type,这个Type主要指节点的类型,比如上面提到的网络、库、控制面板等等,这个说它们是文件夹但又不太是,所以没办法和文件夹一样统一处理。
所以给每个节点赋予一个类型,既方便处理,也方便以后的扩展。
Type的实现如下:
/// <summary>
/// 文件项类型
/// </summary>
internal enum MetroFolderBrowserControlModelType
{
/// <summary>
/// 表明这是一个文件夹
/// </summary>
Directory,
/// <summary>
/// 桌面
/// </summary>
Desktop,
/// <summary>
/// 计算机
/// </summary>
Computer,
/// <summary>
/// 磁盘驱动器
/// </summary>
Disk,
}
考虑到不同类型的节点给它们不同的节点图标显得更好看,所以有这样做:
/// <summary>
/// 文件项类型
/// </summary>
public MetroFolderBrowserControlModelType Type
{
get
{
return type;
}
set
{
switch(value)
{
case MetroFolderBrowserControlModelType.Directory:
{
ItemImagePath = ImagePathHelper.FolderIconPath;
break;
}
case MetroFolderBrowserControlModelType.Computer:
{
ItemImagePath = ImagePathHelper.ComputerIconPath;
break;
}
case MetroFolderBrowserControlModelType.Desktop:
{
ItemImagePath = ImagePathHelper.DesktopIconPath;
break;
}
case MetroFolderBrowserControlModelType.Disk:
{
ItemImagePath = ImagePathHelper.DiskIconPath;
break;
}
default:
{
ItemImagePath = ImagePathHelper.FolderIconPath;
break;
}
}
type = value;
}
}
现在最主要的就是Children的实现还没做好。Children的如何实现也关系到整个控件的效率。
针对不同的类型,采用不同的办法获取其Children:
/// <summary>
/// 子目录(文件 + 文件夹)
/// </summary>
public ObservableCollection<MetroFolderBrowserControlModel> Children
{
get
{
try
{
if (children != null)
{
return children;
}
children = new ObservableCollection<MetroFolderBrowserControlModel>();
switch(Type)
{
case MetroFolderBrowserControlModelType.Desktop:
{
break;
}
case MetroFolderBrowserControlModelType.Computer:
{
foreach (var device in Environment.GetLogicalDrives())
{
if (Directory.Exists(device))
{
MetroFolderBrowserControlModel model = new MetroFolderBrowserControlModel();
model.FileName = device;
model.FullName = device;
model.Type = MetroFolderBrowserControlModelType.Disk;
children.InvokeAdd<MetroFolderBrowserControlModel>(model);
}
}
break;
}
case MetroFolderBrowserControlModelType.Disk:
case MetroFolderBrowserControlModelType.Directory:
{
foreach (var item in ExplorerHelper.GetDirectoryChildrenItems(FullName, true, false, false))
{
MetroFolderBrowserControlModel model = new MetroFolderBrowserControlModel();
model.FileName = item.Name;
model.FullName = item.FullName;
children.Add(model);
}
break;
}
default:
{
break;
}
}
return children;
}
catch (Exception)
{
//出现异常,返回空的集合
return null;
}
}
}
接下来使用 绑好,这个树差不多就展示出来了:
<TreeView ItemsSource="{Binding MetroFolderBrowserControlModels}" x:Name="treeview">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<Image Source="{Binding ItemImagePath, Mode=TwoWay}" Width="16" Height="16"/>
<TextBlock Text="{Binding FileName}" Margin="5,0,0,0"/>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
至于其他获取用户选择的路径、对话框的返回值等等就不继续说了,有需要的可以下载源代码查看:
下载源代码:
http://download.csdn.net/detail/lyclovezmy/7655655
大致效果如下图:

WPF:自定义Metro样式文件夹选择对话框FolderBrowserDialog的更多相关文章
- c# 打开、保存文件对话框 和 文件夹选择对话框
1. OpenFileDialog openImageDialog = new OpenFileDialog(); openImageDialog.Filter = "Image Files ...
- [VB.NET]调用系统的文件夹选择对话框
以下示例代码展示如何调用系统的文件夹选择对话框: Private Function SelectFolder(ByVal Describe As String, Optional ByVal Show ...
- 在WPF中使用文件夹选择对话框
开发中有时会想实现"选择某个文件夹"的效果: 在WPF中,使用Microsoft.Win32.OpenFileDialog只能选择文件,FolderBrowserDialog只能用 ...
- C++文件(夹)选择对话框
由于各种应用,我们需要调用系统的打开文件对话框或者打开文件夹对话框,或两者兼有.今遇到这个情况已经解决,特写下这篇博文. 1.打开文件对话框常用的方法是使用系统的CFileDialog.这里介绍另外一 ...
- 【转】python qt(pyqt)的文件打开、文件保存、文件夹选择对话框
import PyQt4.QtCore,PyQt4.QtGui # 获取文件路径对话框 file_name = QFileDialog.getOpenFileName(self,"open ...
- 文件夹选择之FolderBrowserDialog控件
应用程序可能只允许用户选择文佳夹而非文件,例如在播放MP3时,用户可能把所有的MP3放在一个文佳夹内,在添加时,只要选择添加这个文佳夹,将会把在这个文件内的所有MP3添加的播放器里.在这里对播放器来说 ...
- (WPF) 文件和文件夹选择对话框。
点击button,选择一个excel文件,并将文件名显示在textbox上. private void btnSelectErrorTableFile_Click(object sender, Rou ...
- MFC 文件夹选择对话框
CString setSavePath() { CString strPath = _T(""); HRESULT hr; LPITEMIDLIST pItemList; BROW ...
- Asp.net点击按钮弹出文件夹选择框的实现(网页)
本文地址:http://www.cnblogs.com/PiaoMiaoGongZi/p/4092112.html 在Asp.net网站实际的开发中,比如:需要实现点击一个类似于FileUpload的 ...
随机推荐
- RESTful规范1
RESTful规范 一 什么是RESTful REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为"表征状 ...
- 【MySQL经典案例分析】关于数据行溢出由浅至深的探讨
本文由云+社区发表 一.从常见的报错说起 故事的开头我们先来看一个常见的sql报错信息: 相信对于这类报错大家一定遇到过很多次了,特别对于OMG这种已内容生产为主要工作核心的BG,在内容线的存 ...
- Mysql数据库的二进制安装和基础入门操作
前言:Mysql数据库,知识非常的多,要想学精学通这块知识,估计也要花费和学linux一样的精力和时间.小编也是只会些毛皮,给大家分享一下~ 一.MySQL安装 (1)安装方式: 1 .程序包yum安 ...
- OpenCV添加中文(五)
OpenCV添加文字的方法putText(...),添加英文是没有问题的,但如果你要添加中文就会出现"???"的乱码,需要特殊处理一下. 下文提供封装好的(代码)方法,供OpenC ...
- python json模块出现Invalid control character这个异常的原因
今天在查看日志的时候,发现一个问题,觉得很奇怪,跑了一个多月的代码突然报错了,还是个没见过的错误. 所以今晚抽空记录下. 一.重现错误 说好的传过来的数据是json格式的呢?怎么会突然报错了呢? 二. ...
- SpringBoot之RabbitMQ的使用
一 .RabbitMQ的介绍 RabbitMQ是消息中间件的一种,消息中间件即分布式系统中完成消息的发送和接收的基础软件,消息中间件的工作过程可以用生产者消费者模型来表示.即,生产者不断的向消息队列发 ...
- Docker介绍及常用操作演示(一)--技术流ken
Docker简介 Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化.容器是完全使用沙箱机制,相互 ...
- SpringBoot学习(一)-->Spring的发展
一.Spring的发展 1.Spring1.x 时代 在Spring1.x时代,都是通过xml文件配置bean,随着项目的不断扩大,需要将xml配置分放到不同的配置文件中,需要频繁的在java类和xm ...
- [转]PHP时区/MySql时区/Linux时区
本文转自:https://blog.csdn.net/watermelonmk/article/details/82669062 问题背景:手头上有个国外的项目,为了所谓的国际化,得将时区修改至[美国 ...
- Spring Bean的生命周期相关博客
最近得面试题一直 问 Spring 得生命周期,鉴于自己还未阅读过源码 所以只能是自己 背一波了.属实不懂硬背得作用,但是无奈被各位面试官打败了.等以后有时间了 一定要阅读几遍spring的 源码 有 ...