2009-08-20 3 views
5

У меня есть список предметов (потенциально больших), из которых пользователь должен выбрать один. Я хотел бы разрешить пользователю ввести первые несколько букв нужного пункта, чтобы перейти в нужное место в списке. По умолчанию каждое нажатие клавиши переходит к первому элементу, начинающемуся с этой буквы, поэтому вы не можете вводить первые несколько писем. Есть ли простой способ сделать это? Любой CodeProject или другой такой пример?Перейти к элементу списка, введя несколько первых символов

Я искал часы и нашел любое количество образцов для IAutocomplete, но это не поможет здесь, потому что я должен гарантировать, что результат будет в списке.

Единственный способ, которым я могу это сделать, - это вывести из CListBox, самостоятельно захватить нажатия клавиш, найти элемент, запустить таймер, чтобы новые нажатия клавиш после достаточной паузы начнут новый поиск ... так как я ' m не шуточный MFC, это сложно. Любые советы очень ценятся.

Одна поясняющая нота: моя конечная цель - фактически получить это поведение клавиатуры для ComboBox стиля DropDownList (т. Е. Нет окна редактирования). Отсутствие поля редактирования исключает большинство автозаполненных кодов, а необходимость в функциональности ComboBox означает, что я не могу использовать CListCtrl самостоятельно.

ответ

10

После долгих ненужная боль, я обнаружил, что реальный правильный ответ - просто использовать LBS_SORT. Просто, указав этот стиль, базовый список ванили поддерживает инкрементный стиль быстрого поиска клавиш поиска, который я хотел. Без LBS_SORT (или CBS_SORT для combobox) вы получаете раздражающее и почти бесполезное поведение от перехода к первой букве. Я не пробовал LBS_SORT, потому что содержимое списка было добавлено в отсортированном порядке.

Таким образом, дюжина часов расследования пользовательских элементов управления и т. Д., Все на ничто, потому что в документации Microsoft не упоминается об этой важной поведенческой разнице в описании LBS_SORT !!

Спасибо всем, кто внес свой вклад.

+0

+1 для возврата с решением - спасибо, я не знал этого о списках. Я чувствую вашу боль. документации. 8- ( – RichieHindle

1

Можете ли вы использовать CListView CListCtrl? Они работают по умолчанию.

+0

Ну, я не знаю. Кажется, почти нет документации по CListView (Google, MSDN, Visual Studio Help), поэтому я не знаю, как ее использовать. Я все еще в Visual Studio .NET 2003, если это имеет какое-то влияние. Любые указатели на то, как попробовать CListView? – rfeague

+0

@rfeague: Извините! MFC называет его CListCtrl, несмотря на то, что базовый элемент управления является «ListView». Он называется «Управление списком» в панели инструментов «Редактор диалогов». – RichieHindle

+0

Спасибо, Ричи. Это происходит в правильном направлении, но причуды CListCtrl делают меня сумасшедшим. Я могу заставить его отобразить мой список, и он (в основном) перепрыгивает в нужный элемент, когда набирается несколько букв, но он не прокручивает первоначально выбранный элемент в виде, и он показывает элипсы в конце когда они выбраны (но только при их выборе). Итак, я думаю, вы дали мне правильный ответ, и я благодарю вас.Любые другие указатели, которые могут возникнуть по этим оставшимся проблемам, будут наиболее желанными. Я не могу поверить, сколько времени я инвестировал в эту непростую задачу. – rfeague

3

Я реализовал такую ​​функциональность в ядре Win32. Вот код.

Где-то в цикле сообщений, который обрабатывает вставку списка:

switch(message) 
{ 
    case WM_CHAR:  
    if(HandleListBoxKeyStrokes(hwnd, wParam) == FALSE) 
       return FALSE; 

....

Heres код (Propably не полностью завершена):

/* ======================================================================== */ 
/* ======================================================================== */ 
#define RETURNr(a, b) // homegrown asserts 

BOOLEAN HandleListBoxKeyStrokes(HWND hwnd, UINT theKey) 

{ 
    #define MAXCHARCACHEINTERVALL 600.0 // Max. milisecs time offset to consider as typed 'at once' 
    static char sgLastChars[255] = {'0'}; 
    static double sgLastCharTime = 0.; 

static HWND sgLasthwnd = NULL; 


if(GetSecs() - sgLastCharTime > MAXCHARCACHEINTERVALL || 
    sgLasthwnd != hwnd) 
    *sgLastChars = 0; 

if(theKey == ' ' && *sgLastChars == 0) 
    return TRUE; 

sgLastCharTime = GetSecs(); 
sgLasthwnd = hwnd; 

AppendChar(sgLastChars, toupper(theKey)); 

if(strlen(sgLastChars) > 1) 
{ 
     LONG l = GetWindowLong(hwnd, GWL_STYLE); 
     Char255 tx; 
     GetClassName(hwnd, tx, sizeof(tx)); 
     if( (! stricmp(tx, "Listbox") && 
       ! (l & (LBS_EXTENDEDSEL | LBS_MULTIPLESEL))) || 
      (! stricmp(tx, "ComboBox") && // combo Box support 
       l & CBS_DROPDOWNLIST && 
       ! (l & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)))) 
     { 
      long Count, l, BestMatch = - 1, BestMatchOff = 0; 
      long LBcmdSet[] = {LB_GETCOUNT, LB_GETTEXTLEN , LB_GETTEXT}; 
      long CBcmdSet[] = {CB_GETCOUNT, CB_GETLBTEXTLEN, CB_GETLBTEXT}; 
      long *cmdSet = (! stricmp(tx, "ComboBox")) ? CBcmdSet : LBcmdSet; 

      RETURNr((Count = SendMessage(hwnd, cmdSet[0], 0, 0)) != LB_ERR, 0); 
      for(int i = 0; i < Count; i++) 


     { 
        RETURNr((l = SendMessage(hwnd, cmdSet[1], i, 0)) != LB_ERR, TRUE); 
        RETURNr(l < sizeof(tx), TRUE); 
        RETURNr((l = SendMessage(hwnd, cmdSet[2], i, (LPARAM)&tx)) != LB_ERR, TRUE); 
        strupr(tx); 
        if(! strncmp(tx, sgLastChars, strlen(sgLastChars))) 
        { 
         SelListBoxAndNotify(hwnd, i); 
         return FALSE; 
        } 
        char *p; 
        if(p = strstr(tx, sgLastChars)) 
        { 
         int off = p - tx; 
         if(BestMatch == -1 || off < BestMatchOff) 
         { 
          BestMatch = i; 
          BestMatchOff = off; 
         } 
        } 
       } 
       // If text not found at start of string see if it matches some part inside the string 
       if(BestMatch != -1) 
         SelListBoxAndNotify(hwnd, BestMatch); 
       // Nothing found - dont process 
       return FALSE; 
      } 
     } 
     return TRUE; 
    } 
    /* ======================================================================== */ 
    /* ======================================================================== */ 

    void SelListBoxAndNotify(HWND hwnd, int index) 

    { 
    // i am sorry here - this is some XVT-toolkit specific code. 
    // it has to be replaced with something similar for native Win32 
     WINDOW win = xvtwi_hwnd_to_window(hwnd); 
     WINDOW parent = xvt_vobj_get_parent(win); 
     xvt_list_set_sel(win, index, 1); 
     EVENT evt; 
     memset(&evt, 0, sizeof(evt)); 
     evt.type = E_CONTROL; 
     evt.v.ctl.id = GetDlgCtrlID(hwnd); 
     evt.v.ctl.ci.v.lbox.dbl_click = FALSE; 
     xvt_win_dispatch_event(parent, &evt); 
    } 
    /* ======================================================================== */ 
    /* ======================================================================== */ 

double GetSecs(void) 

{ 
     struct timeb timebuffer; 
     ftime(&timebuffer); 
     return (double)timebuffer.millitm + 
       ((double)timebuffer.time * 1000.) - // Timezone needed for DbfGetToday 
       ((double)timebuffer.timezone * 60. * 1000.); 
} 
    /* ======================================================================== */ 
    /* ======================================================================== */ 

char AppendChar(char *tx, char C) 

{  int i; 

     i = strlen(tx); 
     tx[i ] = C; 
     tx[i + 1] = 0; 
     return(C); 
} 
+0

Ничего себе, спасибо Стефан! Я попробую. Похоже, я мог бы легко применить это к обычным ComboBox, что было бы невероятно. – rfeague

Смежные вопросы