Introduction
There are several building blocks in programming that are available in most of the languages and make life easy. For example, the ability to pop up a message. In Win32, that would be MessageBox(). Another building block is Inputbox
. InputBox() is a very handy command for prompting the user for an input. You can find it in VBA and Visual Basic. However, while Message Boxes can be easily displayed in any C++ program, there is no direct way for calling InputBox
like command. For that reason, I created SGInputBox()
. I am providing this code as a Static library, and with no Resources, which I think is the most comfortable way to be used, and it can be used by Console Applications, plain Win32 applications or any other type of C++ application.
The Function
The goal is to create a very simple and easy to use InputBox
function that can be called like this:
LPWSTR result = SG_InputBox::GetString(
L"Code Project Demo - by Michael Haephrati",
L"What is your name");
and as a result, we would like to see something like:
Or... even better, why not add a "default" value, so the user will be shown this value as default and will then be able either to confirm or to edit it. To make it even more user friendly, we will already "select all" the default text, so if the user types different text, it will replace the old one.
For supporting default text (but not forcing the programmer using this library to have one), we add an optional argument:
LPWSTR GetString(LPCTSTR szCaption, LPCTSTR szPrompt, LPCTSTR szDefaultText = L"");
and call the function like this:
LPWSTR result = SG_InputBox::GetString(
L"Code Project Demo - by Michael Haephrati",
L"What is your name",
L"My name is Michael");
We can also make the Dialog a bit fancy, set background and foreground colors, align the text to the center and set a default button. These will be explained in my "How To" section, as these are tips and tricks not necessarily associated with the scope of the article.
The Test Program
For the purpose of testing the SGInputBox()
function, I have created a default Windows Console application where all I do is link to the InputBox.lib. The best thing is to work with a single Solution (.sln) where there is the Static Library project and the Test Program project, both refer to $(SolutionDir)\$(Configuration)\$(Platform)\ so you can easily link to the library and can define it once and for all configurations (x64 / Win32 / Debug, etc.):
$(SolutionDir)\$(Configuration)\$(Platform)\InputBox.lib;
Then I have created a minimal header file just to make it possible to call our SGInputBox()
function:
#include <Windows.h>
class SG_InputBox
{
public:
static LPWSTR GetString(LPCTSTR szCaption, LPCTSTR szPrompt, LPCTSTR szDefaultText = L"");
};
Then, all that is left to do, is to call the function and use the return value it returns.
The most part of our main()
function will then be:
#include "SG_InputBoxLib.h"
int main()
{
wprintf(L"Testing the InputBox static lib\n");
LPWSTR result = SG_InputBox::GetString(
L"Code Project Demo - by Michael Haephrati",
L"What is your name",
L"My name is Michael");
wprintf(L"User entered '%s'\n", result);
}
How To ...
In my various articles, I always try to include some useful code that can be used for many purposes other than within the scope of the article.
Aligning Text
I wrote the setTextAlignment()
function for the purpose of aligning text. I placed it in my utils.cpp file, as it is advised to place any helper functions in a set of utils.cpp / utils.h source files, which can be used by other projects.
void setTextAlignment(HWND hwnd,int intTextAlignment)
{
LONG_PTR s;
LONG_PTR textalignment = GetWindowLongPtr(hwnd, GWL_STYLE);
if (textalignment != intTextAlignment)
{
if (intTextAlignment == 0)
{
s = GetWindowLongPtr(hwnd, GWL_STYLE);
s = s & ~(SS_LEFT);
SetWindowLongPtr(hwnd, GWL_STYLE, (LONG_PTR)s);
}
else if (intTextAlignment == 1)
{
s = GetWindowLongPtr(hwnd, GWL_STYLE);
s = s & ~(SS_CENTER);
SetWindowLongPtr(hwnd, GWL_STYLE, (LONG_PTR)s);
}
else if (intTextAlignment == 2)
{
s = GetWindowLongPtr(hwnd, GWL_STYLE);
s = s & ~(SS_RIGHT);
SetWindowLongPtr(hwnd, GWL_STYLE, (LONG_PTR)s);
}
textalignment = intTextAlignment;
if (textalignment == 0)
{
s = GetWindowLongPtr(hwnd, GWL_STYLE);
s = s | (SS_LEFT);
SetWindowLongPtr(hwnd, GWL_STYLE, (LONG_PTR)s);
}
else if (textalignment == 1)
{
s = GetWindowLongPtr(hwnd, GWL_STYLE);
s = s | (SS_CENTER);
SetWindowLongPtr(hwnd, GWL_STYLE, (LONG_PTR)s);
}
else if (textalignment == 2)
{
s = GetWindowLongPtr(hwnd, GWL_STYLE);
s = s | (SS_RIGHT);
SetWindowLongPtr(hwnd, GWL_STYLE, (LONG_PTR)s);
}
SetWindowPos(hwnd, 0, 0, 0, 0, 0,
SWP_NOZORDER | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE |
SWP_NOCOPYBITS | SWP_DRAWFRAME);
}
}
Setting a Default Button
We would like that the Confirm button will be shown as Default, indicating that if the user hits the ENTER key, it will be as if he/she pressed that button. To do so, we use the following code:
SendMessage((HWND)m_hWndOK, BM_SETSTYLE,
(WPARAM)LOWORD(BS_DEFPUSHBUTTON), MAKELPARAM(TRUE, 0));
SendMessage((HWND)m_hWndCancel, BM_SETSTYLE,
(WPARAM)LOWORD(BS_PUSHBUTTON), MAKELPARAM(TRUE, 0));
Setting Background and Foreground Colors
First, I have chosen a nice background color and defined it once so I can use it later in one or several places:
#define SOFT_BLUE RGB(206,214,240)
Then I defined a Brush:
HBRUSH SG_InputBox::hbrBkgnd = NULL;
Then, in our Dialog's Callback
function:
LRESULT CALLBACK SG_InputBox::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
We address the WM_CTLCOLORSTATIC message:
case WM_CTLCOLORSTATIC:
{
HDC hdcStatic = (HDC)wParam;
if (hbrBkgnd == NULL)
{
hbrBkgnd = CreateSolidBrush(SOFT_BLUE);
}
SetTextColor(hdcStatic, RGB(0, 0, 0));
SetBkColor(hdcStatic, SOFT_BLUE);
return (INT_PTR)hbrBkgnd;
}
break;
Setting Default Text and Selecting It
SendMessage(m_hWndEdit, EM_SETSEL, 0, -1);
SendMessage(m_hWndEdit, EM_REPLACESEL, 0, (LPARAM)szDefaultText);
SendMessage(m_hWndEdit, EM_SETSEL, 0, -1);
SetFocus(m_hWndEdit);
Handling Errors
When the creation of any element of the window or the window (dialog) itself fails, we call REPORT_ERROR
. This is in fact a MACRO I created:
#define REPORTERROR ReportError(__FUNCTION__)
and the actual ReportError()
function then gets as its parameter the name of the function where the error occurred, which is useful. The ReportError()
function can be expanded to log any "wprintf
" so then you can always check the log file.
{
DWORD error = GetLastError();
LPVOID lpMsgBuf;
DWORD bufLen = FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
0, NULL);
wprintf(L"%S: Error '%s'\n", CallingFunction,(wchar_t *)lpMsgBuf);
}
Further Reading
History
- 12th June, 2019: Initial version