可重用的 WPF 自动完成 TextBox(第二部分)






4.90/5 (6投票s)
本文介绍了一个控件,该控件可用于自动补全在文本框中输入的文件夹。它展示了 WPF 中绑定和主题的强大功能。
引言
本文基于 Aviad P 的优秀文章 “可重用的 WPF 自动完成文本框”。我写这篇文章是为了展示 WPF 中绑定和主题的灵活性。
因此,我
- 将 XML 绑定替换为运行时对象的绑定,
- 将 ViewModel 的数据源更改为
System.IO.Directory
... 以自动完成子目录, - 并添加了两种主题(ExpressionDark、WhistlerBlue)到其中。
生成的控件可用于自动补全在文本框中输入的文件夹。支持通用命名约定 (UNC)。ViewModel 将最近输入的 \\server\share 地址列表持久化到应用程序数据目录中的一个 XML 文件中(详情请参见 Window1.xaml.cs)。
背景
我一直在寻找一个可皮肤化的自动完成文件夹浏览器(文件夹选择器)控件,该控件允许像 Windows Explorer 中的地址栏那样选择一个文件夹。我找到的所有示例实现似乎都基于组合框。这些组合框有一个缺点,那就是实现了标准的组合框行为,当用户选择一个条目时弹出列表会消失(文本框中的文本会被选中),这相当奇怪。
我想要一个支持以下工作流程的控件:
- 用户在控件的文本框部分键入一个路径
- 控件建议一个子条目列表(如果有)
- 用户选择一个条目
- 控件将选定的条目添加到文本框中键入的路径,并添加一个目录分隔符 (\)
请注意,最后三个步骤会重复进行,直到最终路径出现在文本框中(用户可以按 ESC 键使弹出列表消失)。这样,用户就不再需要输入很多地址了。用户可以使用鼠标或键盘通过弹出列表浏览文件系统结构,并在文本框中显示路径。
使用代码
StyleCop
我在项目中使用 StyleCop 来统一代码的可读性。因此,如果您在编译项目时遇到错误,您可以下载并安装 StyleCop,或者编辑/删除每个 .csproj 文件中的相应条目。
<Import Project="$(ProgramFiles)\MSBuild\StyleCop\v4.5\StyleCop.Targets" />
项目结构
项目具有以下结构
|
ViewModel.cs 文件实现了后台查询子目录条目的地方(下方有更多详细信息)。我将 ViewModel.cs 和 MyDataTemplateSelector.cs 移到了 DLL 项目中,因为我希望该控件可用且易于使用,而无需考虑这些细节。 Shares.cs 文件中的
|
可以通过修改 App.xaml 文件中的条目来更改主题。在下面的高亮位置输入 WhistlerBlue.xaml 或 ExpressionDark.xaml(我知道这可以通过单选按钮(或其他)在运行时完成,但我选择保持原样,因为我想让示例尽可能简单)。
<Application x:Class="Test.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:actb="clr-namespace:Aviad.WPF.Controls;assembly=Aviad.WPF.Controls"
xmlns:local="clr-namespace:Test"
StartupUri="Window1.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Themes/ExpressionDark.xaml">
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
以下是我在表 1 的截图中使用设置:
- ExpressionDark
- 在 App.xaml 的
Application.Resources
标签中设置 Themes/ExpressionDark.xaml。 - WhistlerBlue
- 在 App.xaml 的
Application.Resources
标签中设置 Themes/WhistlerBlue.xaml。 - 无主题
- 从 App.xaml 的
Application.Resources
标签中移除 Themes/*.xaml 条目。 - 在 Window1.xaml 中移除或重新定义主题化的
StaticResource
条目。 - Window 标签中的
Background="{StaticResource MyWindowBackgroundBrush}"
TextBlock
标签中的Foreground="{StaticResource TextBrush}"
关注点
ViewModel 类中的 QueryList
方法是当控件尝试查询系统以获取其他条目时执行的主要函数。可以通过 System.IO.Directory.GetDirectories
函数从文件系统中查询其他条目,或者(对于 UNC)可以通过 Shares.cs 中定义的 ShareCollection
类进行查询。
文本框中的输入和子目录查询都与彼此断开,因为每个子目录查询都在后台线程中执行。这一切都是在不使用另一个线程类的情况下完成的!而是通过在 Window1.xaml 文件中实现 PriorityBinding
结构来实现的。实际上,这已经在控件的原始版本中实现了,但我认为我应该指出这一点,以便我们都能理解它为什么能这样工作……
我还意识到,我实在不想一遍又一遍地重复输入相同的 \\server\shared folder\ 地址…… 因此,我们在 ViewModel
类中处理自定义的 \\server\shared folder\ 条目列表。ViewModel
类是可序列化的。当 Window1.xaml.cs 中的窗口关闭或构造时,它会从磁盘持久化并初始化。请查看 ViewModel
类中的 List<string> SuggestEntries
属性以及 Window1.xaml.cs 中相应的类,以充分理解此添加。
我在控件的早期版本中注意到一个小问题。下拉列表的选择颜色有时是错误的(它是蓝色)。这是因为我在 AutocompleteTextBox.Popup
的 ListBox 样式中没有 ListBoxItem 样式。正确设置此样式的关键是 ExpressionDark.xaml 和 WhistlerBlue.xaml 文件中 <Style TargetType="{x:Type aviad:AutoCompleteTextBox}">
部分的 ItemContainerStyle
属性。
我本想让 WaitMessage
消失,同时保留 Window1.xaml 中的 PriorityBinding
架构。但我找不到简单的方法来做到这一点。如果您看到一种不重新实现整个控件即可移除 WaitMessage
的简单方法,请告诉我。
如果您发现任何我可能错过的“显而易见”的改进,请给我反馈。
历史
- 2011 年 10 月 25 日:初次发布。
- 2011 年 11 月 22 日
- 修复了 Window1.xaml 中异步查询和
CollectionViewSource
的正确使用。 - 在 Shares.cs 中添加了通过 UNC 表示法查询文件服务器文件夹共享的类。
- 在 AutoCompleteTextbox 样式中添加了
ItemContainerStyle
的样式,并改进了每个主题中的样式(事件,如鼠标悬停、焦点等,会导致某些 GUI 元素的淡入或淡出)。