I am trying to develop a dropdown combobox that will open up when sufficient
leading characters have
been entered to uniquely match one of the strings. This was working fine
until I added a PreTranslateMessage() handler to look for an Enter (return)
key which would close the combobox dropdown. I want to use this in a dialog
where the Enter key will do the default action. Without this modification,
the user has to hit the enter key twice, once to close the drop down and
again for the default OnOK action of the dialog. Thus I look for the Return
key and close the drop down. It's at this point that the GetCurSel() returns
a -1 for some funny reason.
This is the listing for this message handler:
////////////////////////////////////////////////////////////////////////
//
// we need to check for backspace or delete. when we see
// one of these characters, we don't do the autocomplete
// since the same text would be written over the deleted stuff
//
BOOL
CAutoComboBox::PreTranslateMessage(MSG* pMsg)
{
// if we have a key pressed and we are autocompleting
if(pMsg->message == WM_KEYDOWN) {
int key = pMsg->wParam;
if(m_bAutoComplete) {
// set to not autocomplete on the next real character
if(key == VK_DELETE || key == VK_BACK) m_bAutoComplete = false;
}
// ?? remove
int iSelectedRow = GetCurSel();
int iCount = GetCount();
TRACE(_T("PreTranslate1::m_iSelectedRow=%d: iSelectedRow=%d\n")
, m_iSelectedRow, iSelectedRow);
TRACE(_T("Key=%d: Count=%d\n"), pMsg->wParam, iCount);
// if we have the enter key, close the drop down here so that
// we only have to hit enter once
if(key == VK_RETURN) {
// this is due to a problem of sail numbers 1 and 100. the
// iSelectedRow is sometimes CB_ERR for an unknown reason
// and the nested SelChange message will change the internal
// selected row (m_iSelectedRow)
int iSelectedRowSave = m_iSelectedRow;
ShowDropDown(FALSE);
// if closing the drop down has changed the selected row
if(m_iSelectedRow != iSelectedRowSave) {
m_iSelectedRow = iSelectedRowSave;
}
}
// ?? remove
iSelectedRow = GetCurSel();
TRACE(_T("PreTranslate2::m_iSelectedRow=%d: iSelectedRow=%d\n")
, m_iSelectedRow, iSelectedRow);
}
return CComboBox::PreTranslateMessage(pMsg);
Quote:
}
The TRACEs are for me to try and track down what is happening. I've also
added code to compensate for
the bad value of the selected row.
The ShowDropDown(FALSE) issues an CBN_SELCHANGE notification messsage that I
have connected to my own routine as follows:
void
CAutoComboBox::OnSelChange()
{
int iSelectedRow = GetCurSel();
int iCount = GetCount();
// ?? remove
TRACE(_T("OnSelChange1::iSelectedRow=%d: m_iSelectedRow=%d\n")
, iSelectedRow, m_iSelectedRow);
// only change the selected row if a real selection
if(iSelectedRow >= 0) {
m_iSelectedRow = iSelectedRow;
}
// make sure that we only use a good row
else if(m_iSelectedRow < iCount) {
SetCurSel(m_iSelectedRow);
}
else {
// this indicates no current selection
m_iSelectedRow = -1;
}
// ?? remove
CString line;
// get the text from the user input
GetWindowText(line);
// ?? remove
iSelectedRow = GetCurSel();
TRACE(_T("OnSelChange2::iSelectedRow=%d: m_iSelectedRow=%d: line=%s\n")
, iSelectedRow, m_iSelectedRow, (LPCSTR) line);
Quote:
}
My combobox has 15 entries of which the first 3 lines are as follows:
0
1
100
...
and I type in 1 and then a 0. The first 1 matches the second line. The
second character, the 0, then
allows the unique match to the 100. At this point I hit the Enter key to do
the selection.
Now the output in the debug window is as follows:
...
1> PreTranslate1::m_iSelectedRow=-1: iSelectedRow=-1
2> Key=49: Count=15
3> PreTranslate2::m_iSelectedRow=-1: iSelectedRow=-1
4> SelectedRow: 1 Line: 1 Matched Text: 1
5> PreTranslate1::m_iSelectedRow=1: iSelectedRow=-1
6> Key=48: Count=15
7> PreTranslate2::m_iSelectedRow=1: iSelectedRow=-1
8> SelectedRow: 2 Line: 10 Matched Text: 100
9> PreTranslate1::m_iSelectedRow=2: iSelectedRow=-1
10> Key=13: Count=15
11> OnSelChange1::iSelectedRow=1: m_iSelectedRow=2
12> OnSelChange2::iSelectedRow=1: m_iSelectedRow=1: line=100
13> PreTranslate2::m_iSelectedRow=2: iSelectedRow=1
The 1 key is listed as 49. Line 4 comes from the matching part of the code
and says that we have selected
line 1 by matching a text string of "1". The next character the zero or key
48 shows up as the PreTranslate1:: of line 5. Here the iSelectedRow is -1 or
CB_ERR. There really is a selected row though, row one. We continue through
to the Return (13) on line 10 and again the selected row is returned as
CB_ERR or -1. This also shows the OnSelChange action as a result of the
ShowDropDown(FALSE). In this code, the GetCurSel() returns a 1 (line 11
shows the iSelectedRow at this point). This is the selection of two
characters ago!
I ran Spy to log the messages that were being sent to the combobox and got
the following:
<00001> 00000A14 S CB_GETCURSEL
<00002> 00000A14 R CB_GETCURSEL index:CB_ERR
<00003> 00000A14 S CB_GETCOUNT
<00004> 00000A14 R CB_GETCOUNT cItems:15
<00005> 00000A14 S CB_GETCURSEL
<00006> 00000A14 R CB_GETCURSEL index:CB_ERR
<00007> 00000A14 S ..CB_FINDSTRING indexStart:0 lpszFind:0096AF8C ("1")
<00008> 00000A14 R ..CB_FINDSTRING index:1
<00009> 00000A14 S ..CB_GETLBTEXTLEN index:1
<00010> 00000A14 R ..CB_GETLBTEXTLEN cchText:1
<00011> 00000A14 S ..CB_GETLBTEXT index:1 lpszBuffer:0096AC6C
<00012> 00000A14 R ..CB_GETLBTEXT cchText:1 lpszBuffer:0096AC6C ("1")
<00013> 00000A14 S ..CB_SHOWDROPDOWN fShow:True
<00014> 00000A14 R ..CB_SHOWDROPDOWN lResult:True
<00015> 00000A14 S ..CB_SETCURSEL index:1
<00016> 00000A14 R ..CB_SETCURSEL lResult:0001
<00017> 00000A14 S ..CB_SETEDITSEL ichStart:1 ichEnd:65535
<00018> 00000A14 R ..CB_SETEDITSEL lResult:True
<00019> 00000A14 S CB_GETCURSEL
<00020> 00000A14 R CB_GETCURSEL index:CB_ERR
<00021> 00000A14 S CB_GETCOUNT
<00022> 00000A14 R CB_GETCOUNT cItems:15
<00023> 00000A14 S CB_GETCURSEL
<00024> 00000A14 R CB_GETCURSEL index:CB_ERR
<00025> 00000A14 S ..CB_FINDSTRING indexStart:0 lpszFind:0096AF8C ("10")
<00026> 00000A14 R ..CB_FINDSTRING index:2
<00027> 00000A14 S ..CB_GETLBTEXTLEN index:2
<00028> 00000A14 R ..CB_GETLBTEXTLEN cchText:3
<00029> 00000A14 S ..CB_GETLBTEXT index:2 lpszBuffer:0096AC6C
<00030> 00000A14 R ..CB_GETLBTEXT cchText:3 lpszBuffer:0096AC6C ("100")
<00031> 00000A14 S ..CB_FINDSTRING indexStart:2 lpszFind:0096AF8C ("10")
<00032> 00000A14 R ..CB_FINDSTRING index:2
<00033> 00000A14 S ..CB_SHOWDROPDOWN fShow:True
<00034> 00000A14 R ..CB_SHOWDROPDOWN lResult:True
<00035> 00000A14 S ..CB_SETCURSEL index:2
<00036> 00000A14 R ..CB_SETCURSEL lResult:0002
<00037> 00000A14 S ..CB_SETEDITSEL ichStart:2 ichEnd:65535
<00038> 00000A14 R ..CB_SETEDITSEL lResult:True
<00039> 00000A14 S CB_GETCURSEL
<00040> 00000A14 R CB_GETCURSEL index:CB_ERR
<00041> 00000A14 S CB_GETCOUNT
<00042> 00000A14 R CB_GETCOUNT cItems:15
<00043> 00000A14 S CB_SHOWDROPDOWN fShow:False
<00044> 00000A14 S ....CB_GETCURSEL
<00045> 00000A14 R ....CB_GETCURSEL index:1
<00046> 00000A14 S ....CB_GETCOUNT
<00047> 00000A14 R ....CB_GETCOUNT cItems:15
<00048> 00000A14 S ....CB_GETCURSEL
<00049> 00000A14 R ....CB_GETCURSEL index:1
<00050> 00000A14 R CB_SHOWDROPDOWN lResult:True
<00051> 00000A14 S CB_GETCURSEL
<00052> 00000A14 R CB_GETCURSEL index:1
<00053> 00000A14 S .CB_GETDROPPEDSTATE
<00054> 00000A14 R .CB_GETDROPPEDSTATE fDropped:False
The first part is the matching of the strings. However, on both lines
<00019> and <00039> is the CB_GETCURSEL that returns the CB_ERR.
Line <00043> is the CBN_SHOWDROPDOWN and then we get the nested message to
the OnSelChange() routine which does the CB_GETCURSEL with a return of 1.
How can it be that on line <00039> the current selection is a CB_ERR but the
message almost immediately
after (albeit nested) returns a one. The one is wrong though since the last
set was on line <00036>
where the current selection was set to 2.
It's a shame that we can't see inside the combo box code and so don't know
where the numbers are coming
from. If I could watch the place where the current selection was stored, I
could find the code that
changed it!
--
Edward E.L. MItchell
Pharsight Corp.
(508)771-0806
500 Ocean St., Unit 134,
Hyannis, MA 02601