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

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.90/5 (6投票s)

2011 年 10 月 26 日

CPOL

6分钟阅读

viewsIcon

53585

downloadIcon

3290

本文介绍了一个控件,该控件可用于自动补全在文本框中输入的文件夹。它展示了 WPF 中绑定和主题的强大功能。

使用不同的 WhistlerBlue、ExpressionDark 或无主题风格的自动完成文本框的概述
ExpressionDark

Sample Image

WhistlerBlue

Sample Image

无主题

Sample Image

引言

本文基于 Aviad P 的优秀文章 “可重用的 WPF 自动完成文本框”。我写这篇文章是为了展示 WPF 中绑定和主题的灵活性。

因此,我

  1. 将 XML 绑定替换为运行时对象的绑定,
  2. 将 ViewModel 的数据源更改为 System.IO.Directory... 以自动完成子目录,
  3. 并添加了两种主题(ExpressionDark、WhistlerBlue)到其中。

生成的控件可用于自动补全在文本框中输入的文件夹。支持通用命名约定 (UNC)。ViewModel 将最近输入的 \\server\share 地址列表持久化到应用程序数据目录中的一个 XML 文件中(详情请参见 Window1.xaml.cs)。

背景

我一直在寻找一个可皮肤化的自动完成文件夹浏览器(文件夹选择器)控件,该控件允许像 Windows Explorer 中的地址栏那样选择一个文件夹。我找到的所有示例实现似乎都基于组合框。这些组合框有一个缺点,那就是实现了标准的组合框行为,当用户选择一个条目时弹出列表会消失(文本框中的文本会被选中),这相当奇怪。

我想要一个支持以下工作流程的控件:

  1. 用户在控件的文本框部分键入一个路径
    1. 控件建议一个子条目列表(如果有)
    2. 用户选择一个条目
    3. 控件将选定的条目添加到文本框中键入的路径,并添加一个目录分隔符 (\)

请注意,最后三个步骤会重复进行,直到最终路径出现在文本框中(用户可以按 ESC 键使弹出列表消失)。这样,用户就不再需要输入很多地址了。用户可以使用鼠标或键盘通过弹出列表浏览文件系统结构,并在文本框中显示路径。

使用代码

StyleCop

我在项目中使用 StyleCop 来统一代码的可读性。因此,如果您在编译项目时遇到错误,您可以下载并安装 StyleCop,或者编辑/删除每个 .csproj 文件中的相应条目。

<Import Project="$(ProgramFiles)\MSBuild\StyleCop\v4.5\StyleCop.Targets" />

项目结构

项目具有以下结构

Test 项目包含一个测试应用程序(可替换为您自己的),Aviad.WPF.Controls 项目包含 DLL 项目。如果 Test 项目是您的启动项目,您应该能够按 F5 运行并进行测试。

ViewModel.cs 文件实现了后台查询子目录条目的地方(下方有更多详细信息)。我将 ViewModel.csMyDataTemplateSelector.cs 移到了 DLL 项目中,因为我希望该控件可用且易于使用,而无需考虑这些细节。

Shares.cs 文件中的 ShareCollection 类在 ViewModel 类中使用,当用户输入仅包含文件服务器名称(例如:\\MyFileServer\)的 UNC 路径时。请注意,我在 Test 项目中添加了 Themes 文件夹。这里是 ExpressionDarkWhistlerBlue 主题的存放位置。而 ACTB 项目中的 Themes 文件夹仅包含 Generic 主题(如果 WPF 找不到其他资源中的样式,则使用该主题)。

AutoCompleteTextBox 控件使用了 Generic.xaml 中定义的样式进行主题化。如果系统在任何其他主题(如果有)中找不到相应的样式,则会应用此样式。ExpressionDark.xamlWhistlerBlue.xaml 文件包含相应主题的 AutoCompleteTextBox 样式。因此,如果在 App.xaml 中激活了任一主题,Generic.xaml 中的样式将完全被忽略。

可以通过修改 App.xaml 文件中的条目来更改主题。在下面的高亮位置输入 WhistlerBlue.xamlExpressionDark.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.xamlApplication.Resources 标签中设置 Themes/ExpressionDark.xaml
  • WhistlerBlue
    • App.xamlApplication.Resources 标签中设置 Themes/WhistlerBlue.xaml
  • 无主题
    • App.xamlApplication.Resources 标签中移除 Themes/*.xaml 条目。
    • Window1.xaml 中移除或重新定义主题化的 StaticResource 条目。
      1. Window 标签中的 Background="{StaticResource MyWindowBackgroundBrush}"
      2. 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.xamlWhistlerBlue.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 元素的淡入或淡出)。
© . All rights reserved.