ComboBox 智能过滤,模糊匹配,拼音首字母匹配
2013-03-19 01:47
316 查看
在一个项目总ComboBox中有很多项目,用户查找非常麻烦,系统自带的快速定位是匹配首字母,使用起来非常不方便。网上找了很多,都是基于Items.Add()的方式,这种方式不支持数据源的键值对应,只有自己写一个了,看看效果先!
看起来不错,允许从任意位置匹配项目,而且不需要是连续的,最重要的是支持汉语拼音首字母匹配。
实现原理:将数据源备份到变量DataSource2,在TextChanged事件中循环DataSource2,找到匹配后添加到临时表,循环结束后将临时表绑定到DataSource。注意,在绑定中不要修改DataSource,这样速度无法忍受。
该方法使用DataSource2作为原数据源的引用,DataSource只是过滤后的副本,好了,废话少说,看代码
//一下是相关函数
我的资源中有例程下载
看起来不错,允许从任意位置匹配项目,而且不需要是连续的,最重要的是支持汉语拼音首字母匹配。
实现原理:将数据源备份到变量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
我的资源中有例程下载
相关文章推荐
- 根据拼音首字母进行过滤的combobox
- C#中如何用拼音模糊匹配汉字的首字母
- 根据拼音首字母进行过滤的combobox
- Android实现ListView的A-Z字母排序和过滤搜索功能,实现汉字转成拼音
- AutoCompleteTextView输入汉字拼音首字母实现过滤提示(支持多音字)
- Android 实现ListView的A-Z字母排序和过滤搜索功能,实现汉字转成拼音
- FuzzyAutocomplete代码模糊匹配智能提示
- Android 实现ListView的A-Z字母排序和过滤搜索功能,实现汉字转成拼音 .
- Ext.form.ComboBox的模糊过滤
- Android 利用AutoCompleteTextView实现模糊搜索功能,搜索结果自动提示,识别拼音首字母并转汉字提示
- 支持拼音首字母查询的ComboBox
- 使用EditText对ListView进行过滤,并作拼音匹配
- NSPredicate 谓词总结 数组过滤 模糊匹配
- 模糊查询:有几种情况1.中文 2.连起来的拼音查询 3.首字母查询 需要一个jar包pinyin4j
- 不完全拼音模糊匹配
- oracle通配符过滤(模糊匹配)
- easyui combobox可编辑的情况下,只能首字母开始过滤的问题选项
- Ext.net ComboBox模糊匹配store数据
- Android实现ListView的A-Z字母排序和过滤搜索功能,实现汉字转成拼音