在WPF应用程序中,经常需要让用户选择文件或目录。本文将介绍如何创建一个简单的目录选择器控件,它可以被嵌入到对话框窗口中。为了理解本教程,需要具备基本的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); }
}
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>,它定义了当前目录的内容。然后定义了一个名为CPath的依赖属性,它代表控件当前的路径。可以将CPath设置为一个初始目录,但它也是控件当前所在的目录(即用户到目前为止已选择的目录)。
接下来,UserControl_Loaded方法被调用。如果CPath属性已被设置并且目录存在,则将其用作控件的初始目录,否则列表将被填充为LogicalDrives的列表。
然后,调用PopulateList方法。这个方法清除ObservableCollection(以及ListBox),然后开始将当前目录的内容(仅目录名)作为字符串添加到其中。如果发生错误,或者目录不再存在(用户可能在控件已经更新后删除了它),或者没有访问该目录的权限,那么将字符串"Access Denied"添加到列表中。
ListBox_MouseDoubleClick事件在代码中定义。当用户双击列表框或其子项时,将触发此事件。这是因为事件冒泡。事件冒泡表示事件可以沿着控件树向上传播并到达主容器。例如,如果双击列表框中的一个项,列表框和UserControl都会被双击。在这种情况下,获取选定的索引,并使用静态类Path.Combine的帮助将其添加到CPath字符串中。
为了"向上导航",可以使用静态类Path,该类有一个方法GetDirectoryName,它返回另一个元素所在的目录的路径。在这种情况下,这个元素是一个目录,所以可以使用这段代码来向上导航。例如,从C:\Dir1\Dir2\Dir3,Path.GetDirectoryName(string)将返回C:\Dir1\Dir2。
最后一点:如果在文本框中输入一个路径,并且用户按下Enter键,控件将自动导航到指定的目录(如果它存在)。