您的位置:首页 > 其它

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

2013-03-19 01:47 316 查看
在一个项目总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


我的资源中有例程下载
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: