Adding a lookup control to the dictionary: Searching Pinyin

Finally we start searching. For now, the search algorithm is going to be very simple: The string you type into the edit control will be treated as the start of a Pinyin word or phrase. We'll make it fancier later.

Here is where a lot of the groundwork (some of which I called out explicitly and some of which I slipped in without calling attention to it) starts to pay off.

Up until now, the items in the listview came directly from the dictionary. Of course, when a word is being looked up, we want to reduce the list to those that match the word or phrase being searched for. We will introduce a new member m_vMatch which is a vector of pointers to the items we actually want to display.

class RootWindow : public Window
 // const DictionaryEntry& Item(int i) { return m_dict.Item(i); }
 // int Length() { return m_dict.Length(); }
 const DictionaryEntry& Item(int i) { return *m_vMatch[i]; }
 int Length() { return m_vMatch.size(); }
 void OnCommand(UINT id, UINT cmd);
 void Refilter();
 vector<const DictionaryEntry*> m_vMatch;

By tweaking our Item and Length member functions, we can now render out of the list of matches instead of out of the entire dictionary.

LRESULT RootWindow::OnCreate()
 // ListView_SetItemCount(m_hwndLV, Length());
 m_hwndLastFocus = m_hwndEdit;

 return 0;

Since the list of matches is at most the number of words in the dictionary, we can reserve that size up front and avoid needless reallocations. Once we've done that, we call our new Refilter method to compute the matches (which populates the listview). It is Refilter that will do the ListView_SetItemCount, so there's no point in us doing it here.

void RootWindow::OnCommand(UINT id, UINT cmd)
 switch (id) {
 case IDC_EDIT:
  switch (cmd) {
  case EN_CHANGE:

  // add to RootWindow::HandleMessage()
  case WM_COMMAND:
   OnCommand(GET_WM_COMMAND_ID(wParam, lParam),
             GET_WM_COMMAND_CMD(wParam, lParam));

We also rebuild the list of matches if the user makes a change to the edit control. This means that there is no need for a "Search" button. The listview auto-filters as you type.

void RootWindow::Refilter()
 WCHAR szBuf[256];
 DWORD cchBuf = GetWindowText(m_hwndEdit, szBuf, 256);
 for (int i = 0; i < m_dict.Length(); i++) {
  const DictionaryEntry& de = m_dict.Item(i);
  if (StrCmpNIW(de.m_pszPinyin, szBuf, cchBuf) == 0) {
 ListView_SetItemCount(m_hwndLV, Length());
 ListView_SetItemState(m_hwndLV, -1, 0, LVIS_SELECTED);
 InvalidateRect(m_hwndLV, NULL, FALSE);

Building the list of matches is rather simple and anticlimactic. We get the string the user typed into the edit control and walk through all the words in the dictionary, seeing if the Pinyin begins with the user's typing. If so, then we add it to the match vector.

Once the match list is built up, we tell the listview how many we found, clear the selection (so that the selection doesn't appear to move around from one word to another as items are filtered in or out), and invalidate the client rectangle to trigger a repaint.

That's all there is to it. If you run this program and start typing into the edit control, you'll see the list of words in the listview grow and shrink as you type.

That's all for this month. Next month, we'll work on expanding the scope of the search.

Comments (4)
  1. AC says:

    This is really such a great series of articles. Much better than the tutorials or books which show trivial solutions to trivial problems. I really enjoy them. Thanks a lot for sharing your knowledge with us.

    I discovered your site just recently, but I’m preparing myself to really compile the programs from all the steps up to now. Actually, I started from your first scratch program (in C) and I’m trying to follow the whole evolution. I’m slowly progressing because I’m also trying to check for meaningful comments.

    And please ignore the low count of comments for the last few articles. I’m sure there are more people learning from all of them, even when they don’t comment.

  2. jojjo says:

    I agree, great series.

    One thing that came to mind though – Won’t the application become quite unresponsive if the dictionary is large? What happens if the user types while the application is in refiltering?

  3. Try running the program – it’s quite responsive. It was this instant filtering that first impressed my colleague Ben, and it was his enthusiasm for the program that prompted me to turn it into a series.

  4. Jon says:

    That’s why, after doing several rounds of comparisons of desktop search software, i always come back to X1 / yahoo search: instant filtering of the listbox which contains the list of ALL the files on my drive, more than >500k, for every typed characters! it has many rough edges, but the instantenous response is impressive (not to mention that it has also instanenous *preview*, on the right-side half of the window). it seems they’ve implement owner-drawned controls – and probably haven’t read your blog – they don’t behave very well with minimize / maximize / show-desktop etc.

Comments are closed.