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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章