2009-08-20 21 views
5

Ho un elenco di elementi (potenzialmente grandi) da cui l'utente deve selezionare uno. Mi piacerebbe per permettere all'utente di digitare le prime lettere della voce desiderata per saltare al posto giusto nella lista. Per impostazione predefinita, ogni pressione passa alla prima voce che inizia con quella lettera, quindi non è possibile digitare le prime diversi lettere. C'è un modo semplice per fare questo? Qualsiasi CodeProject o altro esempio simile?Vai alla voce casella di riepilogo digitando primi caratteri

Ho cercato ore e ho trovato un numero qualsiasi di campioni per IAutocomplete, ma ciò non sarà di aiuto in questo caso perché devo garantire che il risultato sia nella lista.

L'unico modo in cui posso pensare di farlo è derivare da CListBox, acquisire personalmente le sequenze di tasti, trovare l'oggetto, eseguire un timer in modo che le nuove sequenze di tasti dopo una pausa sufficiente avviino una nuova ricerca ... poiché io m non un jock MFC, questo è scoraggiante. Qualche consiglio molto apprezzato.

Un chiarimento nota: il mio obiettivo finale è in realtà per ottenere questo comportamento della tastiera per una casella combinata di stile DropDownList (vale a dire senza casella di modifica). La mancanza di una casella di modifica esclude la maggior parte del codice di completamento automatico e la necessità della funzionalità di ComboBox significa che non posso usare CListCtrl da solo.

risposta

10

Dopo tanto dolore inutile, ho scoperto che la vera risposta corretta è semplicemente usare LBS_SORT. Specificando semplicemente questo stile, la lista di base di vanilla supporta lo stile di scorciatoia da tastiera di ricerca incrementale che volevo. Senza LBS_SORT (o CBS_SORT per una combobox), si ottiene il comportamento irritante e quasi inutile del primo salto alla prima lettera. Non ho provato LBS_SORT perché i miei contenuti delle liste sono stati aggiunti comunque in ordine.

Quindi la dozzina di ore di indagini sui controlli personalizzati, ecc., Tutto inutile perché la documentazione Microsoft non fa menzione di questa importante differenza comportamentale nella descrizione di LBS_SORT !!

Grazie a tutti coloro che hanno contribuito.

+0

+1 per tornare con la soluzione - grazie, non lo sapevo su listbox. Sento il tuo dolore. la documentazione. 8- ( – RichieHindle

1

È possibile utilizzare uno CListView CListCtrl? Funzionano così di default.

+0

Beh, non lo so. Sembra che non ci sia quasi nessuna documentazione su CListView (Google, MSDN, Guida di Visual Studio), quindi non so come usarlo. Sono ancora in Visual Studio .NET 2003, se questo ha un impatto. Qualche suggerimento su come provare CListView? – rfeague

+0

@rfeague: Siamo spiacenti! MFC lo chiama un CListCtrl nonostante il nome per il controllo sottostante sia "ListView". Si chiama "Controllo elenco" nella finestra degli strumenti dell'editor di dialogo. – RichieHindle

+0

Grazie Richie. Questo sta andando nella giusta direzione, ma le stranezze di CListCtrl mi stanno facendo impazzire. Posso farlo visualizzare la mia lista, e fa (principalmente) saltare all'elemento giusto quando vengono digitate alcune lettere, ma non scorre l'elemento selezionato inizialmente in vista, e mostra gli ellissi alla fine del oggetti quando sono selezionati (ma solo quando sono selezionati). Quindi, penso che tu mi abbia dato la risposta giusta, e ti ringrazio.Qualsiasi altro suggerimento che potresti avere su questi problemi rimanenti sarebbe il benvenuto. Non posso credere quanto tempo ho investito in questo problema dovrebbe essere-semplice. – rfeague

3

Ho implementato tale funzionalità in nucleo Win32. Ecco il codice.

qualche parte nel vostro ciclo di messaggi che elabora la casella di riepilogo inserto:

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

....

Heres il codice (propably non del tutto completo):

/* ======================================================================== */ 
/* ======================================================================== */ 
#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

Wow, grazie Stefan! Farò un tentativo. Sembra che potrei applicarlo facilmente a un normale ComboBox, che sarebbe favoloso. – rfeague

Problemi correlati