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

易于使用的 WPF 自动完成文本框

starIconstarIconstarIconstarIconstarIcon

5.00/5 (6投票s)

2014 年 7 月 10 日

CPOL

4分钟阅读

viewsIcon

48490

downloadIcon

57

一个基于文本框的易于使用的自定义控件,允许自动完成,兼容 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 日:第一个版本

© . All rights reserved.