Перейти к элементу списка, набрав первые несколько символов - PullRequest
5 голосов
/ 21 августа 2009

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

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

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

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

Ответы [ 3 ]

11 голосов
/ 22 августа 2009

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

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

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

3 голосов
/ 21 августа 2009

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

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

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

....

Вот код (возможно, не полный):

/* ======================================================================== */
/* ======================================================================== */
#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);
}
1 голос
/ 21 августа 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...