如何创建允许用户选择目录的用户控件(WPF)
在本文中,我们将构建一个 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 版