65.9K
CodeProject 正在变化。 阅读更多。
Home

如何创建允许用户选择目录的用户控件(WPF)

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (1投票)

2014 年 6 月 1 日

CPOL

3分钟阅读

viewsIcon

16506

downloadIcon

175

在本文中,我们将构建一个 WPF 用户控件,旨在让用户在其文件系统中选择一个目录。

引言

本文介绍如何创建一个允许用户选择目录的控件。该控件可以轻松地放入 DialogWindow 中。

背景

为了理解本教程,您需要具备 WPF 的基础知识(什么是 UserControl、DialogWindow 等)、C#(集合、IO 和事件)以及 WPF 中的数据绑定(动态列表绑定)知识。

使用代码

该控件设计为中间一个列表框,控件上部有一个文本框,旁边还有一个按钮用于向上遍历目录树。

页面的代码是

<UserControl x:Class="DirectoryPicker.DPick"
             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:d="http://schemas.microsoft.com/expression/blend/2008"
             mc:Ignorable="d"
             d:DesignHeight="300" d:DesignWidth="300" Loaded="UserControl_Loaded">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="30"/>
            <RowDefinition Height="1*"/>
        </Grid.RowDefinitions>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="1*" />
                <ColumnDefinition Width="25" />
            </Grid.ColumnDefinitions>
            <TextBox x:Name="txtPath" Grid.Row="0" Margin="5,2" KeyUp="txtPath_KeyUp"/>
            <Button Content="Up" Grid.Column="1" Margin="2" Click="Button_Click"/>
        </Grid>
        <ListBox x:Name="lstDirs" Margin="5" Grid.Row="1" MouseDoubleClick="lstDirs_MouseDoubleClick"/>
    </Grid>
</UserControl>

后台代码是

public partial class DPick : UserControl
    {
        ObservableCollection<string> Dirs = new ObservableCollection<string>();
        public string CPath
        {
            get { return (string)GetValue(CPathProperty); }
            set { SetValue(CPathProperty, value); }
        }

        // Using a DependencyProperty as the backing store for CPath.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty CPathProperty =
            DependencyProperty.Register("CPath", typeof(string), typeof(DPick), new PropertyMetadata(""));

        public DPick()
        {
            InitializeComponent();
        }

        private void UserControl_Loaded(object sender, EventArgs e)
        {
            this.lstDirs.ItemsSource = Dirs;
            if (CPath == String.Empty || Directory.Exists(CPath) == false)
            {
		CPath = "";
                foreach (string s in Directory.GetLogicalDrives())
                {
                    Dirs.Add(s);
                }
                this.txtPath.Text = CPath;
            }
            else
            {
                PopulateList();
            }
        }

        private void lstDirs_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            int SelIndex = this.lstDirs.SelectedIndex;
            CPath = System.IO.Path.Combine(CPath, Dirs[SelIndex]);
            PopulateList();
        }

        private void PopulateList()
        {
            try
            {
                Dirs.Clear();
                foreach (var dir in Directory.GetDirectories(CPath))
                {
                    Dirs.Add(GetDirName(dir));
                }
                this.txtPath.Text = CPath;
            }
            catch (Exception)
            {
                Dirs.Clear();
                Dirs.Add("Access Denied");
            }

        }
        private string GetDirName(string Path)
        {
            return Path.Substring(Path.LastIndexOf('\\') + 1);
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            CPath = System.IO.Path.GetDirectoryName(CPath);
            PopulateList();
        }

        private void txtPath_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                string old = CPath;
                try
                {
                    CPath = this.txtPath.Text;
                    PopulateList();
                }
                catch (Exception) { CPath = old; }
            }
        }

        private void btnOK_Click(object sender, RoutedEventArgs e)
        {

        }
    }

注释

第一行定义了一个 **ObservableCollection<string>**,它定义了当前目录的内容。它不是一个简单的列表,因为 ObservableCollection 实现了 INotifyPropertyChanged 接口,可以在修改时发出通知。然后我们定义了一个名为 CPath 的依赖属性,它表示控件的当前路径。您可以将 CPath 设置为初始目录,但它也是控件当前所在的目录(即用户目前选择的目录)。

然后调用 **UserControl_Loaded** 方法。如果已设置 CPath 属性且目录存在,则将其用作控件的初始目录;否则,列表将填充逻辑驱动器的列表。我想重点介绍该方法的第一行:

this.lstDirs.ItemsSource = Dirs;

此行指示列表框将 Dirs 作为其项的源。由于 ObservableCollection 类的特殊能力,集合的内容和 ListBox 的内容将始终保持一致。

此时,该方法调用另一个名为 **PopulateList** 的方法。此方法清除 ObservableCollection(以及 ListBox),并开始将当前目录的内容(仅目录名称)作为字符串添加到其中。如果发生错误,或者目录不再存在(用户可能在控件已更新时删除了它),或者我们无权访问该目录,则向列表中添加字符串“访问被拒绝”。

然后在代码中定义 **ListBox_MouseDoubleClick** 事件。每当用户双击列表框或其子项时,都会发生此事件。这是因为事件冒泡。事件冒泡是指事件可以向上遍历控件树并到达主容器。例如,如果双击列表框的一个项,则列表框和 UserControl 都会被双击。在此事件中,我们获取选定的索引,并使用静态类 Path.Combine 将其添加到 CPath 字符串中。

为了“向上”,我们可以使用带有 GetDirectoryName 方法的静态类 Path,该方法返回另一个元素所在的目录的路径。在这种情况下,此元素是一个目录,因此我们可以使用此代码向上移动。例如,从 C:\Dir1\Dir2\Dir3,Path.GetDirectoryName(string) 将返回 C:\Dir1\Dir2。

最后一点:如果我们在文本框中插入路径并且用户按下 **Enter** 按钮,则如果该目录存在,控件将自动导航到指定的目录。

关注点

您可以使用此控件编写一个 DialogWindow。请记住,这是一个 UserControl。此窗口必须具有名为 Path(或您想要的任何名称)的依赖属性,并且此属性必须传递到控件的 CPath 依赖属性。当用户单击“确定”(窗口返回 true)时,您可以访问窗口中的 Path 属性,并且(如果您编写了正确的代码以使您的 Path 等于控件的 CPath)将其用于任何您想要的目的!

历史

文章第 1 版

© . All rights reserved.