ComboBox 智能过滤,模糊匹配,拼音首字母匹配

ComboBox 智能过滤,模糊匹配,拼音首字母匹配
socg
在一个项目总ComboBox中有很多项目,用户查找非常麻烦,系统自带的快速定位是匹配首字母,使用起来非常不方便。

网上找了很多,都是基于Items.Add()的方式,这种方式不支持数据源的键值对应,只有自己写一个了,看看效果先!

看起来不错,允许从任意位置匹配项目,而且不需要是连续的,最重要的是支持汉语拼音首字母匹配。

实现原理:将数据源备份到变量DataSource2,在TextChanged事件中循环DataSource2,找到匹配后添加到临时表,循环结束后将临时表绑定到DataSource。注意,在绑定中不要修改DataSource,这样速度无法忍受。

该方法使用DataSource2作为原数据源的引用,DataSource只是过滤后的副本,好了,废话少说,看代码

 

 

using System;
using System.Text;
 
namespace Socg
{
    public class ComboBox:System.Windows.Forms.ComboBox
    {
        #region public bool CleverFilter----智能过滤
        /// <summary>
        /// 智能过滤时使用
        /// </summary>
        public object DataSource2=null;
        /// <summary>
        /// 避免绑定数据源后导致的递归调用
        /// </summary>
        private bool lock_文本改变=false;
        //如果中下拉菜单中选择项目,事件顺序为SelectionChangeCommitted->TextChanged->SelectedValue
        private bool lock_点选列表项=false;
 
        private bool cleverFilter=false;
        /// <summary>
        /// 使ComboBox有智能过滤功能。在输入文本时会自动根据输入的内容进行匹配。这种匹配不需从首字母开始,且可以“间隔匹配”,还支持拼音首字母匹配。
        /// 例如:“以太网交换机”用“以交换”、“y交换”、“以jh”、“y交h”可以匹配(包括空字符串),用“已交换”,“以换交”不能匹配。
        /// 实现方法是增加变量“DataSource2”来存储原始数据源。输入字符后搜索“DataSource2”,找到匹配后添加到临时DataTable,最后将临时DataTable赋值到DataSource属性达到过滤效果。
        /// 需要注意的是从DataSource的到的数据源已经不是原始的数据源。回收时需要注意数据源副本的回收。为此,代码规定了启用“智能过滤”前必须手动设置DataSource2。停用“DataSource2”时必须先使DataSource2=null,否则会抛出异常。
        /// </summary>
        public bool CleverFilter
        {
            get
            {
                return cleverFilter;
            }
            set
            {
                if(CleverFilter!=value)
                {
                    cleverFilter=value;
                    if(value==true)
                    {
                        if(DataSource2==null)
                        {
                            throw new Exception("启用“智能过滤”时必须主动设置DataSource2=DataSource,设置成功之后DataSource将变为副本,真正原始的数据源在DataSource2");
                        }
                        this.TextChanged+=new EventHandler(ComboBox_TextChanged);
                        this.SelectedValueChanged+=new EventHandler(ComboBox_SelectedValueChanged);
                        this.SelectionChangeCommitted+=new EventHandler(ComboBox_SelectionChangeCommitted);
 
                    }
                    else
                    {
                        if(DataSource2!=null)
                        {
                            throw new Exception("取消“智能过滤”时必须主动设置DataSource2为null,它是真正的数据源,这是应将DataSource=DataSource2");
                        }
                        this.TextChanged-=new EventHandler(ComboBox_TextChanged);
                        this.SelectedValueChanged-=new EventHandler(ComboBox_SelectedValueChanged);
                        this.SelectionChangeCommitted-=new EventHandler(ComboBox_SelectionChangeCommitted);
                    }
                }
            }
        }
        #endregion
        void ComboBox_SelectionChangeCommitted(object sender,EventArgs e)
        {
            lock_点选列表项=true;
        }
 
        void ComboBox_SelectedValueChanged(object sender,EventArgs e)
        {
            lock_点选列表项=false;
        }
        #region void ComboBox_TextChanged(object sender,EventArgs e)
        void ComboBox_TextChanged(object sender,EventArgs e)
        {
            if(this.CleverFilter==true)
            {
                if(lock_文本改变==true)
                {
                    return;
                }
                if(lock_点选列表项==true)
                {
                    return;
                }
                if(this.Enabled==false||this.Visible==false||this.Focused==false)
                {
                    return;
                }
                try
                {
                    lock_文本改变=true;
                    System.Data.DataTable tempDataTable=null;
                    System.Windows.Forms.Application.DoEvents();//输入中文的一个词会先送进来一个汉字,导致读取的值是第一个汉字,加上这一句可以让汉字一次性全部接收
                    string text=this.Text;
 
                    #region 查找匹配项放入临时表
                    if(DataSource2 is System.Data.DataView)
                    {
                        System.Data.DataView v=(System.Data.DataView)DataSource2;
                        tempDataTable=v.Table.Clone();
                        for(int i=0;i<v.Count;i++)
                        {
                            if(Socg.Common.CleverMatch(v[i][this.DisplayMember].ToString(),text,false,true,true)==true)
                            {
                                tempDataTable.Rows.Add(v[i].Row.ItemArray);
                            }
                        }
                    }
                    else if(DataSource2 is System.Data.DataTable)
                    {
                        System.Data.DataTable v=(System.Data.DataTable)DataSource2;
                        tempDataTable=v.Clone();
                        for(int i=0;i<v.Rows.Count;i++)
                        {
                            if(Socg.Common.CleverMatch(v.Rows[i][this.DisplayMember].ToString(),text,false,true,true)==true)
                            {
                                tempDataTable.Rows.Add(v.Rows[i].ItemArray);
                            }
                        }
                    }
                    else
                    {
                        throw new Exception("目前只支持DataTable和DataView");
                    }
                    #endregion
 
                    this.DroppedDown=false;//关闭下拉列表
                    object tempTable=this.DataSource;
                    this.DataSource=tempDataTable;
 
                    #region 释放旧数据源
                    if(tempTable!=null)
                    {
                        if(tempTable!=DataSource2)//不要把原始数据源释放了
                        {
                            if(tempTable is System.Data.DataTable)
                            {
                                ((System.Data.DataTable)tempTable).Dispose();
                            }
                            else if(tempTable is System.Data.DataView)
                            {
                                ((System.Data.DataView)tempTable).Dispose();
                            }
                        }
                    }
                    #endregion
                    this.DroppedDown=true;//打开下拉列表
                    System.Windows.Forms.Application.DoEvents();//处理其它消息循环,在ComboBox外部实现是可省略此代码。继承方式实现需加这一句
                    this.Text=text;
                    this.SelectionStart=text.Length;
                    //打开下拉列表后系统会自动隐藏鼠标指针,下面两句为了显示指针
                    System.Windows.Forms.Cursor.Current=System.Windows.Forms.Cursors.Default;
                    System.Windows.Forms.Cursor.Show();
 
                }
                finally
                {
                    lock_文本改变=false;
                }
            }
        }
        #endregion
    }
}

//一下是相关函数

#region public static char GetChineseSpell()----提取汉字的拼音首字母
/// <summary>
/// 将Asc码转换为拼音首字母。
/// 如果 Asc 小于256,则原样返回
/// 如果 转换失败,则返回"*"。如:中文标点符号
/// 速度:25,000,000次/秒
/// </summary>
/// <param name="ChineseAsc">字符的Asc码</param>
/// <returns>拼音首字母</returns>
public static char GetChineseSpell(int ChineseAsc)
{
    if(ChineseAsc < 256)
        return (char)ChineseAsc;
    else if(ChineseAsc >= 45217 && ChineseAsc < 45253)
        return 'A';
    else if(ChineseAsc >= 45253 && ChineseAsc < 45761)
        return 'B';
    else if(ChineseAsc >= 45761 && ChineseAsc < 46318)
        return 'C';
    else if(ChineseAsc >= 46318 && ChineseAsc < 46826)
        return 'D';
    else if(ChineseAsc >= 46826 && ChineseAsc < 47010)
        return 'E';
    else if(ChineseAsc >= 47010 && ChineseAsc < 47297)
        return 'F';
    else if(ChineseAsc >= 47297 && ChineseAsc < 47614)
        return 'G';
    else if(ChineseAsc >= 47614 && ChineseAsc < 48119)
        return 'H';
    //                else if(ChineseAsc>=48119&&ChineseAsc<48119)return 'I';
    else if(ChineseAsc >= 48119 && ChineseAsc < 49062)
        return 'J';
    else if(ChineseAsc >= 49062 && ChineseAsc < 49324)
        return 'K';
    else if(ChineseAsc >= 49324 && ChineseAsc < 49896)
        return 'L';
    else if(ChineseAsc >= 49896 && ChineseAsc < 50371)
        return 'M';
    else if(ChineseAsc >= 50371 && ChineseAsc < 50614)
        return 'N';
    else if(ChineseAsc >= 50614 && ChineseAsc < 50622)
        return 'O';
    else if(ChineseAsc >= 50622 && ChineseAsc < 50906)
        return 'P';
    else if(ChineseAsc >= 50906 && ChineseAsc < 51387)
        return 'Q';
    else if(ChineseAsc >= 51387 && ChineseAsc < 51446)
        return 'R';
    else if(ChineseAsc >= 51446 && ChineseAsc < 52218)
        return 'S';
    else if(ChineseAsc >= 52218 && ChineseAsc < 52698)
        return 'T';
    //                else if(ChineseAsc>=52698&&ChineseAsc<52698)return 'U';
    //                else if(ChineseAsc>=52698&&ChineseAsc<52698)return 'V';
    else if(ChineseAsc >= 52698 && ChineseAsc < 52980)
        return 'W';
    else if(ChineseAsc >= 52980 && ChineseAsc < 53689)
        return 'X';
    else if(ChineseAsc >= 53689 && ChineseAsc < 54481)
        return 'Y';
    else if(ChineseAsc >= 54481)
        return 'Z';
    else
        return '*';
}

/// <summary>
/// 将汉字字符转换为拼音首字母。
/// 如果 Asc 小于256,则原样返回
/// 如果 转换失败,则返回"*"。如:中文标点符号
/// 速度:1,500,000次/秒
/// </summary>
/// <param name="ChineseChar">要转换的字符</param>
/// <returns>拼音首字母</returns>
public static char GetChineseSpell(char ChineseChar)
{
    int ChineseAsc;
    byte[] ChineseByte =System.Text.Encoding.Default.GetBytes(ChineseChar.ToString());
 
    if(ChineseByte.Length < 2)
    {
        ChineseAsc = ChineseByte[0];
    }
    else
    {
        ChineseAsc = (ChineseByte[0] << 8) + ChineseByte[1];
    }
    return GetChineseSpell(ChineseAsc);
}


/// <summary>
/// 将包含汉字的字符串转换为拼音首字母。
/// 如果 Asc 小于256,则原样返回
/// 如果 转换失败,则返回"*"。如:中文标点符号
/// 速度:650000字/秒
/// </summary>
/// <param name="ChineseString">要转换的字符串</param>
/// <returns>拼音首字母组成的字符串</returns>
public static string GetChineseSpell(string ChineseString)
{
    int count = ChineseString.Length;
    System.Text.StringBuilder returnString = new System.Text.StringBuilder(count);
    for(int i = 0;i < count;i++)
    {
        returnString.Append(GetChineseSpell(ChineseString[i]));
    }
    return returnString.ToString();
}
#endregion
 
#region public static bool CleverMatch(string source,string part,bool ignoreCase,bool chineseSpell,bool continuous)----智能匹配
/// <summary>
/// 智能匹配
/// 比较一个字符串是否是另一个字符串的“子串”,支持智能模式:例如 “以太网交换机”用“以交换”、“y交换”、“以jh”、“y交h”可以匹配(包括空字符串),用“已交换”,“以换交”不能匹配
/// </summary>
/// <param name="source">源字符串</param>
/// <param name="part">source的子串</param>
/// <param name="ignoreCase">是否区分大小写</param>
/// <param name="chineseSpell">是否启用拼音匹配。为true时忽略 ignoreCase 参数</param>
/// <param name="continuous">是否必须连续匹配</param>
/// <returns>如果part属于source的一部分,则返回true;否则返回false</returns>
public static bool CleverMatch(string source,string part,bool ignoreCase,bool chineseSpell,bool continuous)
{
    //*********************算法***************************
    //1、循环 partString 中的字符,查找这些字符在 sourceString 中是否存在,只要有一个不存在则认为不匹配。
    //2、在检查字符在 sourceString 中是否存在时不能搜索整个区域,只搜索上次匹配之后的部分。这样是为了满足逐一匹配。例如:sf与assf匹配,而fs则不匹配。
    //3、如果启用拼音首字母选项。则在比较时需判断partString中的字符是否是因为,如果是英文,则与sourceString的拼音形式进行比较。注意这是忽略大小写的。
    //4、如果启用连续匹配,则需要判断本次匹配的位置是否与上次匹配的位置之间无间隔。如果有间隔,则需要重新循环 partString,从sourceString中合适的位置之后开始比较。具体做法:
    //        记录 partString 中第一个匹配的位置,如果发现不连续,则以这个位置作起点 重新循环partString做比较。
    //        记录上次配置的位置,本次匹配后判断与上次是否连续。第一个字符匹配时不需要做比较,因为没有“上一次”
    //****************************************************
 
    int k=-1;//存储找到单字母匹配的位置
    int j=-1;//存储partString中第一个字符匹配的位置,如果后面的字符不匹配了,再中这个字符之后搜索。
    int temp=-1;//存储上次匹配的位置,与k做比较,用于判断是否连续匹配。如果搜索的是第一个字符,则不需要比较是否连续
    string sourceString2=null;
    for(int i=0;i<part.Length;i++)
    {
        temp=k;
        if(chineseSpell==true)
        {
            if((int)part[i]>255)//中文使用中文来匹配
            {
                k=source.IndexOf(part[i],k+1);
                if(k<0)//有一个字符没匹配直接返回
                {
                    return false;
                }
                else//找到匹配
                {
                    #region 检查本次匹配是否与上次匹配的位置连续,如果不连续,则从第一个匹配字符的下一个字符开始搜索
                    if(continuous==true)
                    {
                        if(i==0)//第一个字符已经匹配,记录这个位置
                        {
                            j=k;
                        }
                        else
                        {
                            if(k-temp!=1)//不是连续匹配
                            {
                                i=-1;//回到第一个字符开始搜索
                                k=j;//从这个位置后开始搜索(搜索使用的是k+1)
                            }
                        }
                    }
                    #endregion
                }
            }
            else//发现字母时,将“源”变换为拼音首字母再来匹配
            {
                if(sourceString2==null)
                {
                    sourceString2=Socg.Common.GetChineseSpell(source);
                }
                k=sourceString2.IndexOf(part[i].ToString(),k+1,System.StringComparison.CurrentCultureIgnoreCase);
                if(k<0)
                {
                    return false;
                }
                else
                {
                    #region 检查本次匹配是否与上次匹配的位置连续,如果不连续,则从第一个匹配字符的下一个字符开始搜索
                    if(continuous==true)
                    {
                        if(i==0)//第一个字符已经匹配,记录这个位置
                        {
                            j=k;
                        }
                        else
                        {
                            if(k-temp!=1)//不是连续匹配
                            {
                                i=-1;//回到第一个字符开始搜索
                                k=j;//从这个位置后开始搜索(搜索使用的是k+1)
                            }
                        }
                    }
                    #endregion
                }
            }
        }
        else
        {
            if(ignoreCase==true)
            {
                k=source.IndexOf(part[i].ToString(),k+1,System.StringComparison.CurrentCultureIgnoreCase);
                if(k<0)
                {
                    return false;
                }
                else
                {
                    #region 检查本次匹配是否与上次匹配的位置连续,如果不连续,则从第一个匹配字符的下一个字符开始搜索
                    if(continuous==true)
                    {
                        if(i==0)//第一个字符已经匹配,记录这个位置
                        {
                            j=k;
                        }
                        else
                        {
                            if(k-temp!=1)//不是连续匹配
                            {
                                i=-1;//回到第一个字符开始搜索
                                k=j;//从这个位置后开始搜索(搜索使用的是k+1)
                            }
                        }
                    }
                    #endregion
                }
            }
            else
            {
                k=source.IndexOf(part[i].ToString(),k+1);
                if(k<0)
                {
                    return false;
                }
                else
                {
                    #region 检查本次匹配是否与上次匹配的位置连续,如果不连续,则从第一个匹配字符的下一个字符开始搜索
                    if(continuous==true)
                    {
                        if(i==0)//第一个字符已经匹配,记录这个位置
                        {
                            j=k;
                        }
                        else
                        {
                            if(k-temp!=1)//不是连续匹配
                            {
                                i=-1;//回到第一个字符开始搜索
                                k=j;//从这个位置后开始搜索(搜索使用的是k+1)
                            }
                        }
                    }
                    #endregion
                }
            }
        }
    }
    return true;
}
/// <summary>
/// 智能匹配=CleverMatch(source,part,true,true,true)
/// </summary>
/// <param name="source"></param>
/// <param name="part"></param>
/// <returns></returns>
public static bool CleverMatch(string source,string part)
{
    return CleverMatch(source,part,true,true,true);
}
#endregion

————————————————
版权声明:本文为CSDN博主「socg」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/socg/article/details/8689792

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值