原文:通通WPF随笔(1)——基于lucene.NET让ComboBox拥有强大的下拉联想功能


  我一直很疑惑百度、谷哥搜索框的下拉联想功能是怎么实现的?是不断地查询数据库吗?其实到现在我也不知道,他们是怎么实现这么高效的。后来在博客园无意邂逅了“鹿神”,搜索引擎唉,听起来就很高端。于是研究了一段时间后就产生了这个WPF的下拉联想控件。

名称:

简拼:

全拼:

区号:

邮编:

  这么强大的功能代码一定会复杂吧?不是的哦,亲~代码只有短短几句哦

界面如下:(下拉框后面的数字为查询的延时,可见效率还是很高滴)

XAML:

  1. <cop:CopAutoCompleted url="{Binding Text, ElementName=DirTextBox}" columnNames="{Binding Text, ElementName=UCSearchColTextBox}"
    textName="{Binding Text, ElementName=TextNameTextBox}" maxItems="{Binding Text, ElementName=TextNameTextBox_Copy}">
  2. <cop:CopAutoCompleted.ItemTemplate>
  3. <DataTemplate>
  4. <StackPanel Orientation="Horizontal">
  5. <TextBlock Text="{Binding City}" />
  6. <TextBlock Text=" (" Foreground="#FF383838"/>
  7. <TextBlock Text="{Binding Spell}" Foreground="#FF383838" />
  8. <TextBlock Text=")" Foreground="#FF383838"/>
  9. </StackPanel>
  10. </DataTemplate>
  11. </cop:CopAutoCompleted.ItemTemplate>
  12. </cop:CopAutoCompleted>

属性介绍:(该控件继承于ComboBox,只是多了下面4个属性)

  url:设置索引所在的文件夹(稍后会介绍如何创建索引)

  columnNames:设置需要检索的列名

  textName:选择下拉项后显示在text里的列名

  maxItems:下拉框最多显示多少项(如果显示内容过多的话会有延时的感觉,经测试延时是由于后台banding的数据集合改变跟新到界面时产生的,不是lucene的效率问题)

  ItemTemplate:玩WPF的都懂的,设置下拉显示数据的布局内容。这样的话就有了很高的可扩展性和灵活性。

 

1.总体思路


  (1)创建lucene索引:在网上找一个全国城市的数据库,用代码提取出来,分别对里面的各列创建索引。

  (2)查询索引:通过lucene的PrefixQuery类构造查询语句,就可以实现前缀查询出整体。

  (3)ComboBox绑定:这里数据源绑定到ObservableCollection<dynamic>集合(自动通知,方便啊),而其中的每一项为根据查询出的每一项结果动态构造的对象。所以用到dynamic运行时解析。

  

2.详细设计


  基本知识我这里就不详细说了,可参看文章最后的参考文献。

1、创建lucene索引

  我在网上找全国城市数据库时找找到的一个比较全面的是Access的,所以这里特地写了一个创建索引的功能:

  (之前比较流行通用数据库访问层,我基于反射自己写了一个通用数据库DBHelper,由于电脑上没有数据库环境,所以只测试了Access和Sqlite)

  其实就是根据查询结果,对需要创建索引的列添加lucene的索引。代码如下:

  1. private void Button_Click_1(object sender, RoutedEventArgs e)
  2. {
  3. //设置索引文件夹
  4. var directory = FSDirectory.GetDirectory(DirTextBox.Text, true);
  5.  
  6. //创建一个索引,采用StandardAnalyzer对句子进行分词
  7. IndexWriter indexWriter = new IndexWriter(directory, new StandardAnalyzer());
  8.  
  9. var columnName= ColumnNameTextBox.Text.Split(',');
  10.  
  11. //设置数据库连接字符串
  12. if (ComboBox1.Text=="Sqlite")
  13. {
  14. helper=new CopDb.CopDbHelper(CopDb.CopDbHelper.CopDbType.Sqlite,ConnTestBox.Text);
  15. }
  16. if (ComboBox1.Text=="Access")
  17. {
  18. helper = new CopDb.CopDbHelper(CopDb.CopDbHelper.CopDbType.Access, ConnTestBox.Text);
  19. }
  20.  
  21. int timeOut = Environment.TickCount;
  22. var read = helper.ExecuteReader(SQLStrTextBox.Text);
  23. SqlTimeTextBox.Text = (Environment.TickCount - timeOut).ToString();
  24. while (read.Read())
  25. {
  26. //创建文档
  27. Document doc = new Document();
  28. //添加字段
  29. foreach (var item in columnName)
  30. {
  31. doc.Add(new Field(item, read[item].ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED));
  32. }
  33. indexWriter.AddDocument(doc);
  34. }
  35. read.Close();
  36.  
  37. //对索引文件进行优化
  38. indexWriter.Optimize();
  39. indexWriter.Close();
  40. MessageBox.Show("创建索引完成");
  41. }

2、查询索引

  

  就是构造lucene查询query时用PrefixQuery类就行,如下:

  1. var cols = SearchColTextBox.Text.Split(',');
  2.  
  3. BooleanQuery query = new BooleanQuery();
  4.  
  5. foreach (var item in cols)
  6. {
  7. query.Add(new PrefixQuery(new Term(item, SearchTextBox.Text)),BooleanClause.Occur.SHOULD);
  8. }
  9. //query.parse:注入查询条件
  10. var hits = search.Search(query);

3、ComboBox绑定数据源

  

  数据源为ObservableCollection<dynamic>类型集合,后台我们只用动态构造出每一个查询对象添加进集合里即可。初始化dynamic对象时还不能用ExpandoObject,虽然ExpandoObject很方便,但是这是一个封闭类,不能继承。ComboBox在选中其中一项显示到文本框里时,其实是执行了选中项数据源的ToString()方法。所以不能重载ExpandoObject的ToString()方法。所以这里自定义了一个轻量级的ExpandoObject类,继承于DynamicObject实现。

代码:

  1. class dyData:DynamicObject
  2. {
  3. public dyData(string colName)
  4. {
  5. this.colName = colName;
  6. }
  7. //ToString时需要输出的属性
  8. public string colName { get; set; }
  9. //用于存储属性名和对应的值
  10. Dictionary<string, object> data = new Dictionary<string, object>();
  11. //绑定时获取对应属性的值
  12. public override bool TryGetMember(GetMemberBinder binder, out object result)
  13. {
  14. return data.TryGetValue(binder.Name,out result);
  15. }
  16. //用于添加属性和对应的值
  17. public void SetValue(string name, object value)
  18. {
  19. data.Add(name, value);
  20. }
  21.  
  22. //重写Tostring方法
  23. public override string ToString()
  24. {
  25. try
  26. {
  27. return data[colName].ToString();
  28. }
  29. catch (Exception ex)
  30. {
  31. MessageBox.Show("找不到列名"+colName,"设置text要显示的项名时出错",MessageBoxButton.OK,MessageBoxImage.Error);
  32. return null;
  33. }
  34. }
  35.  
  36. }

  这样就实现了一个简易的ExpandoObject了。接下来遍历查询结果,通过SetValue动态创建对象的属性,添加进ObservableCollection<dynamic>数据集合,ComboBox直接数据绑定即可。

下载:demo

参考文献:

  lucene,你也会(7篇)——第一篇 快速入门

  使用Lucene.Net实现全文检索

  WPF地区选择控件(内附下载地址)

  

后记


  其实相同的功能我用查询数据库的方法,也实现过了,但是耗时每次都是100多毫秒。lucene估计有个缓存吧,速度会越来越快,而且经常被查寻的东西优先级别会提高,排在前面。

  以我的经验,写关于美工的文章比逻辑的获得的关注和推荐多得多。我也很想把通通玩Blend美工这个系列写下去,毕竟我大部分的粉丝都来源于这个系列。但是,最近几个月,都在纠结WF、WCF等等逻辑方面的,对美工没什么好的创意。  

  写博客图个什么?不就是作为一个平凡的码农,想要得到更多人的关注和认可,让我觉得自己其实和民工还是有点区别的。

  对了,我之前嵌在博客里的silverlight为什么都显示不出来了?xap文件我都是放在博客园的文件里的。求大神解答。

  

  

通通WPF随笔(1)——基于lucene.NET让ComboBox拥有强大的下拉联想功能的更多相关文章

  1. 通通WPF随笔(4)——通通手写输入法(基于Tablet pc实现)

    原文:通通WPF随笔(4)--通通手写输入法(基于Tablet pc实现) 从我在博客园写第一篇博客到现在已经有1年半了,我的第一篇博客写的就是手写识别,当时,客户需求在应用中加入手写输入功能,由于第 ...

  2. 通通WPF随笔(3)——艺术二维码素材生成器

    原文:通通WPF随笔(3)--艺术二维码素材生成器 最近公司让我开发一个条形码的生成控件,花了半天时间搞定觉得不过瘾,什么年代了该用二维码了吧.于是wiki了一下二维码的资料. 比较常见的就是QR码( ...

  3. 通通WPF随笔(2)——自己制作轻量级asp.net网站服务

    原文:通通WPF随笔(2)--自己制作轻量级asp.net网站服务 大学玩asp.net时就发现VS在Debug时会起一个web服务,这东西也太神奇了服务起得这么快,而相对于IIS又这么渺小. 前几个 ...

  4. 【selenium】基于python语言,如何用select选择下拉框

    在项目测试中遇到了下拉框选择的控件,来总结下如何使用select选择下拉框: 下图是Select类的初始化描述,意思是,给定元素是得是select类型,不是就抛异常.接下来给了例子:要操作这个sele ...

  5. 一款基于jQuery的联动Select下拉框

    今天我们要来分享一款很实用的jQuery插件,它是一个基于jQuery多级联动的省市地区Select下拉框,并且值得一提的是,这款联动下拉框是经过自定义美化过的,外观比浏览器自带的要漂亮许多.另外,这 ...

  6. WebGIS中兴趣点简单查询、基于Lucene分词查询的设计和实现

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/. 1.前言 兴趣点查询是指:输入框中输入地名.人名等查询信息后,地图上可 ...

  7. 基于lucene实现自己的推荐引擎

    基于lucene实现自己的推荐引擎 推荐常用算法之-基于内容的推荐 推荐算法

  8. Apache Solr采用Java开发、基于Lucene的全文搜索服务器

    http://docs.spring.io/spring-data/solr/ 首先介绍一下solr: Apache Solr (读音: SOLer) 是一个开源.高性能.采用Java开发.基于Luc ...

  9. 基于lucene的案例开发:查询语句创建PackQuery

    转载请注明出处:http://blog.csdn.net/xiaojimanman/article/details/44656141 http://www.llwjy.com/blogdetail/1 ...

随机推荐

  1. strong & weak 的理解

    import "ViewController.h" @interface ViewController () /*weak*/ @property (nonatomic,weak) ...

  2. thinkphp中ajax使用实例(thinkphp内置支持ajax)

    thinkphp中ajax使用实例(thinkphp内置支持ajax) 一.总结 1.thinkphp应该是内置支持ajax的,所以请求类型里面才会有是否是ajax // 是否为 Ajax 请求 if ...

  3. matlab 下的集成学习工具箱

    matlab 当前支持的弱学习器(weak learners)类型分别为: 'Discriminant' 'knn' 'tree' 可通过 templateTree 定义: 1. fitcensemb ...

  4. Hdu4771(杭州赛区)

    Stealing Harry Potter's Precious Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 ...

  5. 【codeforces 750D】New Year and Fireworks

    time limit per test2.5 seconds memory limit per test256 megabytes inputstandard input outputstandard ...

  6. Android Studio 2.3.1导出jar文件不能生成release解决办法

    升级了AS之后,在项目中的时候,有个需求需要把通过AS导出一个模块,需要以jar的形式导出来,研究了一下,按照网上的描述操作了一遍,不知道是AS版本问题还是自己操作问题,发现使用 ./gradlew ...

  7. 英文构词法 —— ant、ent 后缀

    1. -ant:--人 accountant:会计, account(ac+count):计数,账户: assistant:助手: assist:帮助 descendant:后裔: descend:下 ...

  8. 用决策树模型求解回归问题(regression tree)

    How do decision trees for regression work? 决策树模型既可以求解分类问题(对应的就是 classification tree),也即对应的目标值是类别型数据, ...

  9. 十个最有“钱景”的IT技能, 你掌握了哪个?

    IT行业的失业率仍然徘徊在历史低点,其中某些岗位(如网络和安全工程师和软件开发商)的失业率在1%左右. Robert Half Technology最近的一项调查显示,大多数CIO将扩大IT团队或专注 ...

  10. Qt5信号与槽C++11风格连接简介

    最近在论坛上看到了这个方面的问题,详见这里. 随后浅浅地学习了一下子,看到了Qt官方论坛上给出的说明,觉得C++11的functional连接方法还是比Qt4既有的宏连接方法有很大不同. 官方论坛的文 ...