Introduction
I am making an application that has a console similar to the one found in AutoCAD. I want the commands to be input in a ComboBox
that keeps a history in the dropdown. Commands are implemented by pressing the Enter key with a command typed in or selected from the dropdown. The user should also be able to use the arrow keys to scroll through previous commands. This is similar to consoles in video games such as Half-Life 2.
The Problem
With the regular CComboBox
, it is difficult to do an action when the user hits the Enter key. When the Enter key is pressed, I want the command to be put into the combobox
's history (the dropdown) and the command to be parsed and implemented. Also, I noticed that when a user presses the arrow keys to scroll through previous commands, they cannot go back to the original command they were working on. On top of that, when the up arrow key is pressed, it selects the previous command in the dropdown but when you press it again, it does nothing.
The Solution
First of all, I derived a class from CComboBox
called CConsoleCombo
. I added member variables, CString m_current
and int m_nPos
. m_current
holds the command the user is working on so if they scroll through their previous commands, they can always come back to it. m_nPos
is the current position in the history the user has scrolled to. I have to keep track of this position manually. The next thing I did was to override CComboBox
's PreTranslateMessage()
function. The overridden function looks like this:
BOOL CConsoleCombo::PreTranslateMessage(MSG* pMsg)
{
if (pMsg->message == WM_KEYDOWN)
{
CString str;
int nVirtKey = (int) pMsg->wParam;
switch (nVirtKey)
{
case VK_UP:
if (m_nPos == -1)
{
GetWindowText(str);
m_current = str;
}
if (m_nPos + 1 < GetCount())
{
m_nPos++;
GetLBText(m_nPos, str);
SetWindowText(str);
SetEditSel(0, str.GetLength());
}
return TRUE;
case VK_DOWN:
if (m_nPos - 1 == -1)
{
SetWindowText(m_current);
SetEditSel(m_current.GetLength(),
m_current.GetLength());
m_nPos = -1;
}
if (m_nPos - 1 >= 0)
{
m_nPos--;
GetLBText(m_nPos, str);
SetWindowText(str);
SetEditSel(0, str.GetLength());
}
return TRUE;
case VK_RETURN:
GetWindowText(str);
if (str != "")
{
InsertString(0, str);
m_nPos = -1;
SetWindowText("");
ParseCommand(str);
}
break;
}
}
return CComboBox::PreTranslateMessage(pMsg);
}
The function checks if the Windows message is WM_KEYDOWN
. Then if it is and the down key is pressed, it moves the position down (m_nPos
). For the up key, it moves the position up. When the up key is first pressed, the current command is stored (m_current
). When the down key is pressed and it is back to the starting position, the current command is restored. Scrolling through previous commands displays them selected in the ComboBox
's text field. If the WM_KEYDOWN
's wParam
is the Enter key and there is a command in the text field then that command is added to the history, the text field is cleared, and the ParseCommand()
function is performed. This is a virtual
member function which is explained in the Using the Code section of this article.
Using the Code
To use this class effectively, it is best to derive a class from it. In the demo, this class is CInput
. You must then override the ParseCommand(CString command)
function. In this function, you add the custom code you want to perform. For instance, parsing the command string and carrying out its instructions. For the sake of simplicity, in the demo, all this function does is to send a custom user message (WM_CCOMMAND
) to the dialog with the command in its wParam
. The dialog then takes this command and adds it to a read only edit box (this edit box is a custom control as well, check out my article 'Changing the background color of a read-only edit control'). This is a task that all consoles perform. This is also a neat and simple example of user messages.
Note: The demo attached to this article has a dialog with an edit box and two ComboBox
es. One is normal while the other one is a CConsoleCombo
. I have put both to show a comparison of the two. They both add string
s to the edit box but for the normal combobox
, you must hit the Add button.
History
- 25th January, 2005 - Initial post