易于使用的 WPF 自动完成文本框
一个基于文本框的易于使用的自定义控件,允许自动完成,兼容 WCF

引言
本文的目的是与社区分享我过去一个月一直在研究的一些代码,以允许一个简单的文本框拥有自定义的自动完成过滤器。这个想法的灵感来自于 GMail 的搜索功能。这个自定义控件需要具备我的项目所需的所有以下功能
- 它必须非常易于使用,并且需要尽可能少的代码与我的项目集成。
- 它需要与 WCF 兼容。我的想法是,像 GMail 一样,创建一个分层应用程序,因此过滤功能需要在服务器端执行,然后结果通过 WCF 通道传输。
- 它需要过滤自定义数据(可以来自数据库或自定义列表)并在多个字段上搜索,就像 GMail 所做的那样,并建议类似的结果。
- 所有过滤都需要以异步方式完成,因此我将使用响应式库。
- 它必须与键盘和鼠标交互。
- 它需要强制用户从可用列表中选择一个项目。
- 它必须与我在项目中使用的 MVVM 模板完全兼容。
- 它需要通知用户正在应用过滤器,以及过滤完成的时间。
- 它需要让用户浏览结果列表,并选择一个项目。
- 除了过滤代码之外,所有代码都需要封装在自定义控件内部。
- 水印文本支持
- 此外,我需要在 viewmodel 中选择一个默认选项,并在控件上反映出来。
我在网上找到了一些示例源代码,但没有一个能满足以上所有要求。我没有编写整个代码。我将这个项目建立在这个出色的文章的基础之上 http://blog.petegoo.com/2011/11/22/building-an-auto-complete-control-with-reactive-extensions-rx/。我欢迎关于如何改进此代码的各种建议。
背景
几个月来,我一直在努力将一个 VB6 应用程序迁移到新技术。经过几周的上网冲浪、搜索网络、学习新技术,我设法开始了我的项目。我面临的一个问题是如何让用户从几个表中选择一个项目。例如,我需要让用户从客户表中为客户订单选择一个客户。这需要通过 WCF 通道完成。通常,我会在客户端填充一个包含所有客户的组合框,并让用户选择一个,但这效率很低。这个想法是输入用户的一些数据(至少三个字符)以帮助过滤数据,并向最终用户返回可能的匹配项。
使用代码
从 XAML 的角度来看,使用该控件非常简单
<Window x:Class="TextBoxAutoCompleteTest.Window1"
xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml
xmlns:ac="clr-namespace:WpfAutoComplete.Controls;assembly=WpfAutoComplete"
Title="Autocomplete Text Box Project"
Height="300" Width="300">
<Grid>
<StackPanel>
<Label Content="This is an Autocomplete Textbox" />
<ac:TextBoxAutoComplete Name="autoTxtBoxEng"
SearchDataProvider="{Binding Path=MySearchProviderEng}"
SelectedListBoxValue="{Binding Path=PhraseNumber, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
WatermarkText="Type in filtering text here..."/> </StackPanel>
</Grid>
</Window>
关于代码的一些说明
- SearchDataProvider 属性是用于数据过滤的类。该类需要实现 ISearchDataProvider 接口。
- SelectedListBoxValue 是可选的,并指向 VM 上要由控件更新的属性。
- WatermarkText 是在未输入任何内容时要显示的文本。
这是一个使用字典作为示例的简单 SearchDataProvider 类的示例
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TextBoxAutoCompleteTest
{
class MyDataProviderEng : WpfAutoComplete.ISearchDataProvider
{
public WpfAutoComplete.SearchResult DoSearch(string searchTerm)
{
return new WpfAutoComplete.SearchResult
{
SearchTerm = searchTerm,
Results = dict.Where(item => item.Value.ToUpperInvariant().Contains(searchTerm.ToUpperInvariant())).ToDictionary(v => v.Key, v => v.Value)
};
}
public WpfAutoComplete.SearchResult SearchByKey(object Key)
{
return new WpfAutoComplete.SearchResult
{
SearchTerm = null,
Results = dict.Where(item => item.Key.ToString()==Key.ToString()).ToDictionary(v => v.Key, v => v.Value)
};
}
private readonly Dictionary<object, string> dict = new Dictionary<object, string> {
{ 1, "The badger knows something"},
{ 2, "Your head looks something like a pineapple"},
{ 3, "Crazy like a box of green frogs"},
{ 4, "The billiard table has green cloth"},
{ 5, "The sky is blue"},
{ 6, "We're going to need some golf shoes"},
{ 7, "This is going straight to the pool room"},
{ 8, "We're going to Bonnie Doon"},
{ 9, "Spring forward - Fall back"},
{ 10, "Gerry had a plan which involved telling all"},
{ 11, "When is the summer coming"},
{ 12, "Take you time and tell me what you saw"},
{ 13, "All hands on deck"}
};
}
}
很容易调整此代码以使用 WCF 服务来过滤和返回可用选项,而不是预定义的字典。看看这个新的 ISearchDataProvider 实现
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Ohmio.Client.DataProviders
{
class DataProviderClientes : WpfAutoComplete.ISearchDataProvider
{
private OhmioService.OhmioServiceClient serviceClient =
new OhmioService.OhmioServiceClient();
public WpfAutoComplete.SearchResult DoSearch(string searchTerm)
{
return new WpfAutoComplete.SearchResult
{
SearchTerm = searchTerm,
Results = (serviceClient.EnumClientes(searchTerm.ToUpperInvariant())).ToDictionary(x=>(object)x.Key , y=>y.Descripcion)
};
}
public WpfAutoComplete.SearchResult SearchByKey(object Key)
{
return new WpfAutoComplete.SearchResult
{
SearchTerm = null,
Results = (serviceClient.EnumClientes(Key.ToString())).ToDictionary(v => (object)v.Key, v => v.Descripcion)
};
}
}
}
在这种情况下,使用了一个 WCF 服务,该服务具有一个名为 EnumClientes 的方法。此方法获取一个过滤参数,在数据库上执行过滤,然后返回一个 List<object> ,该列表被转换为字典对象。
关注点
在编写此代码的过程中,我学到了很多东西。仍然存在一些错误/缺失的功能,这些超出了我对该代码的实际理解,尤其是关于响应式库
- 该代码在按键之间使用延迟来启动过滤过程。这样做的想法是使过滤过程更有效,而不是对每个键入的字符执行过滤。有时控件停止响应(在输入文本后不会执行过滤)。当它失去焦点并点击 Delete 或 Backspace 时,它会再次工作。
- 验证:当未选择任何选项时,控件周围绘制一个红色边框。我没有找到自定义错误消息的方法。
- 水印文本的颜色固定为浅灰色。当背景较暗时,这是一个问题。
- 缺少多选选项。
历史
2014 年 7 月 10 日:第一个版本