我一直很疑惑百度、谷哥搜索框的下拉聯想功能是怎么實現的?是不斷地查詢數據庫嗎?其實到現在我也不知道,他們是怎么實現這么高效的。后來在博客園無意邂逅了“鹿神”,搜索引擎唉,聽起來就很高端。於是研究了一段時間后就產生了這個WPF的下拉聯想控件。
名稱:
簡拼:
全拼:
區號:
郵編:
這么強大的功能代碼一定會復雜吧?不是的哦,親~代碼只有短短幾句哦
界面如下:(下拉框后面的數字為查詢的延時,可見效率還是很高滴)
XAML:
<cop:CopAutoCompleted url="{Binding Text, ElementName=DirTextBox}" columnNames="{Binding Text, ElementName=UCSearchColTextBox}"
textName="{Binding Text, ElementName=TextNameTextBox}" maxItems="{Binding Text, ElementName=TextNameTextBox_Copy}"> <cop:CopAutoCompleted.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding City}" /> <TextBlock Text=" (" Foreground="#FF383838"/> <TextBlock Text="{Binding Spell}" Foreground="#FF383838" /> <TextBlock Text=")" Foreground="#FF383838"/> </StackPanel> </DataTemplate> </cop:CopAutoCompleted.ItemTemplate> </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的索引。代碼如下:

private void Button_Click_1(object sender, RoutedEventArgs e) { //設置索引文件夾 var directory = FSDirectory.GetDirectory(DirTextBox.Text, true); //創建一個索引,采用StandardAnalyzer對句子進行分詞 IndexWriter indexWriter = new IndexWriter(directory, new StandardAnalyzer()); var columnName= ColumnNameTextBox.Text.Split(','); //設置數據庫連接字符串 if (ComboBox1.Text=="Sqlite") { helper=new CopDb.CopDbHelper(CopDb.CopDbHelper.CopDbType.Sqlite,ConnTestBox.Text); } if (ComboBox1.Text=="Access") { helper = new CopDb.CopDbHelper(CopDb.CopDbHelper.CopDbType.Access, ConnTestBox.Text); } int timeOut = Environment.TickCount; var read = helper.ExecuteReader(SQLStrTextBox.Text); SqlTimeTextBox.Text = (Environment.TickCount - timeOut).ToString(); while (read.Read()) { //創建文檔 Document doc = new Document(); //添加字段 foreach (var item in columnName) { doc.Add(new Field(item, read[item].ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED)); } indexWriter.AddDocument(doc); } read.Close(); //對索引文件進行優化 indexWriter.Optimize(); indexWriter.Close(); MessageBox.Show("創建索引完成"); }
2、查詢索引
就是構造lucene查詢query時用PrefixQuery類就行,如下:
var cols = SearchColTextBox.Text.Split(','); BooleanQuery query = new BooleanQuery(); foreach (var item in cols) { query.Add(new PrefixQuery(new Term(item, SearchTextBox.Text)),BooleanClause.Occur.SHOULD); } //query.parse:注入查詢條件 var hits = search.Search(query);
3、ComboBox綁定數據源
數據源為ObservableCollection<dynamic>類型集合,后台我們只用動態構造出每一個查詢對象添加進集合里即可。初始化dynamic對象時還不能用ExpandoObject,雖然ExpandoObject很方便,但是這是一個封閉類,不能繼承。ComboBox在選中其中一項顯示到文本框里時,其實是執行了選中項數據源的ToString()方法。所以不能重載ExpandoObject的ToString()方法。所以這里自定義了一個輕量級的ExpandoObject類,繼承於DynamicObject實現。
代碼:
class dyData:DynamicObject { public dyData(string colName) { this.colName = colName; } //ToString時需要輸出的屬性 public string colName { get; set; } //用於存儲屬性名和對應的值 Dictionary<string, object> data = new Dictionary<string, object>(); //綁定時獲取對應屬性的值 public override bool TryGetMember(GetMemberBinder binder, out object result) { return data.TryGetValue(binder.Name,out result); } //用於添加屬性和對應的值 public void SetValue(string name, object value) { data.Add(name, value); } //重寫Tostring方法 public override string ToString() { try { return data[colName].ToString(); } catch (Exception ex) { MessageBox.Show("找不到列名"+colName,"設置text要顯示的項名時出錯",MessageBoxButton.OK,MessageBoxImage.Error); return null; } } }
這樣就實現了一個簡易的ExpandoObject了。接下來遍歷查詢結果,通過SetValue動態創建對象的屬性,添加進ObservableCollection<dynamic>數據集合,ComboBox直接數據綁定即可。
下載:demo
參考文獻:
后記
其實相同的功能我用查詢數據庫的方法,也實現過了,但是耗時每次都是100多毫秒。lucene估計有個緩存吧,速度會越來越快,而且經常被查尋的東西優先級別會提高,排在前面。
以我的經驗,寫關於美工的文章比邏輯的獲得的關注和推薦多得多。我也很想把通通玩Blend美工這個系列寫下去,畢竟我大部分的粉絲都來源於這個系列。但是,最近幾個月,都在糾結WF、WCF等等邏輯方面的,對美工沒什么好的創意。
寫博客圖個什么?不就是作為一個平凡的碼農,想要得到更多人的關注和認可,讓我覺得自己其實和民工還是有點區別的。
對了,我之前嵌在博客里的silverlight為什么都顯示不出來了?xap文件我都是放在博客園的文件里的。求大神解答。