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

时间: 2024-10-11 03:46:48

WPF:自定义Metro样式文件夹选择对话框FolderBrowserDialog的相关文章

[VB.NET]调用系统的文件夹选择对话框

以下示例代码展示如何调用系统的文件夹选择对话框: 1 Private Function SelectFolder(ByVal Describe As String, Optional ByVal ShowNewFolder As Boolean = True) As String 2 Using nOpen As New System.Windows.Forms.FolderBrowserDialog() 3 nOpen.Description = Describe 4 nOpen.ShowNe

定制文件夹选择对话框的样式和大小实现方法

前面讲过了两种个性的文件夹挑选对话框的完成办法,见文章<Win7界面的和API完成的老界面文件夹挑选对话框代码完成详细解说>.而老界面的文件夹挑选对话框很小,也致使挑选很不便当,所以很多人就不喜爱这种对话框了.但是今日讲的定制这种文件夹挑选对话框的款式和巨细,或许能够大大提升用户体验吧.嘿嘿.         首要来看看完成截图吧,看图说话,有图有本相! 榜首张图是横向的,第二张图是纵向的,第三张图是横向纵向都拓展了.对话框中,还增加了编辑框,能够直接输入文件夹途径.挑选了文件夹后,会自动更新

2016.5.9 文件选择和文件夹选择对话框

1.文件夹选择 FolderBrowserDialog folderDlg = new FolderBrowserDialog(); folderDlg.ShowNewFolderButton = false; folderDlg.SelectedPath = Directory.GetCurrentDirectory(); folderDlg.Description = "选择脚本.sql文件存放路径"; if (folderDlg.ShowDialog() == DialogRes

在WPF中使用文件夹选择对话框

开发中有时会想实现"选择某个文件夹"的效果: 在WPF中,使用Microsoft.Win32.OpenFileDialog只能选择文件,FolderBrowserDialog只能用树型的方式选择文件夹,很不好用. 终于找到一个办法,使用Windows API Code Pack 在VS里打开Package Manager Console后输入Install-Package WindowsAPICodePack-Shell获取包后 就可以像这样打开选择文件夹Dialog了: var di

C++文件(夹)选择对话框

由于各种应用,我们需要调用系统的打开文件对话框或者打开文件夹对话框,或两者兼有.今遇到这个情况已经解决,特写下这篇博文. 1.打开文件对话框常用的方法是使用系统的CFileDialog.这里介绍另外一种方法就是使用OPENFILENAME这个结构体和GetOpenFileName()这个函数,可以实现单选文件或者多选文件,代码如下: 需引入头文件#include "CommDlg.h" [cpp] view plaincopy TCHAR szBuffer[MAX_PATH] = {0

【转】python qt(pyqt)的文件打开、文件保存、文件夹选择对话框

import PyQt4.QtCore,PyQt4.QtGui # 获取文件路径对话框 file_name = QFileDialog.getOpenFileName(self,"open file dialog","C:\Users\Administrator\Desktop","Txt files(*.txt)") ##"open file Dialog "为文件对话框的标题,第三个是打开的默认路径,第四个是文件类型过滤器

MFC 文件夹选择对话框

CString setSavePath() { CString strPath = _T(""); HRESULT hr; LPITEMIDLIST pItemList; BROWSEINFO browseinfo; TCHAR path[MAX_PATH]; hr = ::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); if (FAILED(hr)) { TRACE("CoInitEx failed: %x\n",

MFC技术实现选择文件夹的对话框

由于MFC自带的CFileDiag是只能选择文件的,无法定制只能选择文件夹的对话框.然后当你仅仅需要操作文件夹,批量操作文件时,你需要用WIN32API实现,以下贴出制做该对话框的函数 void CQQICKView::OnMmm() {  // TODO: Add your command handler code here  CString m_FileDir;  BROWSEINFO bi;  ZeroMemory(&bi, sizeof(BROWSEINFO));  bi.hwndOwn

WPF 自定义滚动条样式

先看一下效果: 先分析一下滚动条有哪儿几部分组成: 滚动条总共有五部分组成: 两端的箭头按钮,实际类型为RepeatButton Thumb 两端的空白,实际也是RepeatButton 最后就是Thumb(滑块) 所以如果要修改滚动条的样式,就要修改这五部分的样式.具体代码如下: <!--自定义滚动条样式-->            <SolidColorBrush x:Key="StandardBorderBrush"