[win32] Window to handle text

Pages: 123
I removed WM_DESTROY but it still looks weird, and main window works perfectly
ConnectDlgProc should returns FALSE if it does not process a message

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
BOOL CALLBACK ConnectDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
        switch(msg)
        {
        case WM_INITDIALOG:
                {
                        return TRUE;
                }
        case WM_COMMAND:
                {
                        switch(LOWORD(wParam))
                        {
                        case IDOK:
                                {
                                        char RUsername[] = "";
                                        char RPassword[] = "";
                                        char Username[256];
                                        char Password[256];
                                        GetDlgItemText(hWnd, IDC_EDIT1, Username, sizeof(Username)/sizeof(Username[0]));
                                        GetDlgItemText(hWnd, IDC_EDIT2, Password, sizeof(Password)/sizeof(Password[0]));
                                        if(strcmp(RUsername,Username) == 0 && strcmp(RPassword,Password) == 0)
                                        {
                                                EndDialog(hWnd, IDOK);
                                                return TRUE;
                                        }
                                        else
                                        {
                                                MessageBox(hWnd, "Invalid Username or Password!", NULL, MB_ICONERROR);
                                        }
                                        break;
                                }
                        case IDCANCEL:
                                {
                                        EndDialog(hWnd, IDCANCEL);
                                        return TRUE;
                                }
                                break;
                        }
                        break;
                }
        }
        return FALSE;
}
wow, it's working :P Thank you! but I have few more questions :D

#1 can I add icon and smallicon same way as in normal window?

#2 how can I make IDC_EDIT1 as default edit control? i mean, if they start program, it will automatically "activate" it so they can write to it (they don't need to click on it), + how can I "jump" to IDC_EDIT2 when I press TAB while I'm in IDC_EDIT1 ? (connect dialog)

#3 how can I change look of my window to "windows 7 graphic" ? like in resource editor:

http://filebeam.com/2393d036b07bfbc5364a3ae236a7b8bb.jpg

#4 how can I contact u if I'll need some help again ? :P

edit: I forgot about few things:

#5 if I make window with resolution 640x480, it have like 620x440 or something (I used this code to get X and Y position)

1
2
3
4
5
6
7
8
9
case WM_LBUTTONDOWN:
	{
		iPosX = LOWORD(lParam);
		iPosY = HIWORD(lParam);
		char waCoord[20];
		wsprintf(waCoord, "(%i, %i)", iPosX, iPosY);
		::MessageBox(hWnd, waCoord, NULL, MB_OK);
		break;
	}


edit2: #6 can u post me that code where u have 2 edit controls, 1 read only, and second for input?

edit3: #7 is there any way to destroy buttons and then create new one while dialog is running? like, I have buttons "ok" - IDC_OK and "Cancel" - IDC_CANCEL, I'll select in menu "form 2" and they'll change to "accept" - IDC_ACCEPT and "decline" - IDC_DECLINE
Last edited on
#1 can I add icon and smallicon same way as in normal window?


Dialogs aren't smart enough to do it on their own, so I generally...

(a) declare a global variable

HICON g_hIcon = NULL;

(b) load the icon(s) in WinMain

1
2
	g_hIcon = (HICON)LoadImage(hInstance, MAKEINTRESOURCE(IDI_APP), IMAGE_ICON, 0, 0, 0);
	_ASSERTE(NULL != g_hIcon);


(c) set the icon in WM_INITDIALOG

1
2
3
	// Set the icon for this dialog (required for main dialogs)
	SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM)g_hIcon);
	SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM)g_hIcon);


Note that my icon file contains two images. With the right editor, a single .ico file can hold multiple image sizes/color depths.
Last edited on
#2 how can I make IDC_EDIT1 as default edit control?

Well, with the Visual Studio resource editor, you can set the control as the first in the tab order. So you might be able to do that with ResEdit?

Or you can set it programatically, in the WM_INITDIALOG handler, by calling SetFocus

Note that MSDN says:

The dialog box procedure should return TRUE to direct the system to set the keyboard focus to the control specified by wParam. Otherwise, it should return FALSE to prevent the system from setting the default keyboard focus.

okay so IDC_EDIT1 works, that thing with tab works too, but it don't works in main window, I have
1
2
3
4
{
    EDITTEXT        IDC_EDIT1, 9, 146, 252, 14, ES_AUTOHSCROLL
    DEFPUSHBUTTON   "Send", IDOK, 267, 146, 50, 14
}
but it won't activate it after creating dialog :P
#3 how can I change look of my window to "windows 7 graphic" ?

See this thread:
"Chnage Look of Win32 Buttons"
http://www.cplusplus.com/forum/windows/48611/

Look for the post about manifests (#pragma comment(...)

#4 I'm only answering in this much details as I am out of work as the moment. Hopefully I will have less time soon, as I'll be back at work.

But if you post a clear question to this forum (ideally one issue per thread) you should get the answers you require. Very probably from someone else!

#5 I don't get this question. But you are correctly getting the position of the mouse. Or you could use

1
2
xPos = GET_X_LPARAM(lParam); 
yPos = GET_Y_LPARAM(lParam); 


Note that these are the mouse's client coord (i.e. wrt the top left corner of the window that received the message). Some other messages use screen coord (e.g. WM_CONTEXTMENU)

#6 I will tidy up my code and post it over the next few days.

#7 You can destroy and create windows on the fly, using DestroyWindow and CreateWindow. But here it sound like you just need to change the text they display.

You can use SetWindowText() to change the text they display. In this case the "Accept" button would still send an IDOK message so you'd to keep track of what you're doing to process the command correctly.

You can also enable/disable, hide/show, and move windows, inc. buttons:
See the MSDN entries for EnableWindow, MoveWindow, and ShowWindow.

For example, you could disable the OK button of the logon button until the user's typed in a password.
#5

yesterday I created window with
HWND hWnd = CreateWindowEx(NULL, "Window Class", "Chat Client", WS_OVERLAPPEDWINDOW, 200, 200, 640, 480, NULL, NULL, hInst, NULL);

but when I clicked to bottom right corner (it should be 640x480) i got something like x - 620, y - 440 if I remember right

#7 well, I tried

EnableWindow(IDC_EDIT3, FALSE);

but I'm getting this error

cannot convert parameter 1 from 'int' to 'HWND'

so I tried to replace with HWND(IDC_EDIT3), it will compile fine, but it won't do anything...

I tried ShowWindow too but I'm getting same problems

(I need to replace IDOK with IDACCEPT button, so I need to use 2 different buttons)

edit: #8 (I hope this is last :)) how can I do this thing?

you could disable the OK button of the logon button until the user's typed in a password.


I tried to add to WM_KEYDOWN function that will get text of IDC_EDIT1 (username) and if it's != NULL then it will display MessageBox, but it's not working :P
Last edited on
#5 Did the window have a frame? If it did, the client area would have been the screen size minus the frame size x 2 (prob. different for width and height).

If you need to calculate the size, you can get Windows to give you the frame size using GetSystemMetrics with SM_CYFIXEDFRAME/SM_CYSIZEFRAME/...

#7 You need to get the HWND from the ID

HWND hwnd = GetDlgItem(IDC_EDIT3);
EnableWindow(hwnd, FALSE);

(you can feed the o/p of the first call directly into the second, by I prefer this style).

You should be very wary of C-style casts. You're are telling the compiler that the id really a handle, which it isn't.

You could create all 4 buttons at the start and hide 2 (ShowWindow(hwnd, SW_HIDE)
and then swap which ones are visible (SW_HIDE/SW_SHOW). That is prob. better than creating and destroying (esp for a small no. of buttons).

Instead of changing the ID, you could also set a variable which identifies what you are doing at the time. Then when a WM_COMMAND message arrives, is is routed onto different handlers depending on what is going on at the time (i.e. the value of the variable). This is the better approach if you want the buttons to handle lots of different commands.
Last edited on
wow, cool, it rly works lol :P and what about that last thing? you mentioned I can Disable Connect Button (I know how to do that now) when password edit box is empty (I don't know how to do this...)

edit:

I was playing a little with that EnableWindow thing, and I wanted to create /ban command, it was working well so I wanted to make new array of chars, put there message and lower it and then check if it is /ban command, if yes then disable window... but i'm getting some annoying letters after conversion, here's the code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
case IDOK:
				{
					char buffer[256];
					char bufferSmall[256];
					char BanCommand[] = "/ban";
					GetDlgItemText(hMain, IDC_EDIT3, buffer, sizeof(buffer)/sizeof(buffer[0]));
					if(strcmp(buffer, "") == 0)
					{
					}
					else
					{
						MessageBox(hMain, buffer, NULL, MB_OK); // message
						HWND TextMessageBox = GetDlgItem(hMain, IDC_EDIT3);
						int i = 0;
						while(buffer[i])
						{
							bufferSmall[i] = buffer[i];
							if(islower(bufferSmall[i]))
								i++;
							else
							{
								tolower(bufferSmall[i]);
								i++;
							}
						}
						MessageBox(hMain, bufferSmall, NULL, MB_OK); // some really really really really really annoying letters
					/*	for(int i = 0; i < 4; i++)
						{
							if(strcmp(&buffer[i],&BanCommand[i]) == 0)
							{
								EnableWindow(TextMessageBox, FALSE);
								break;
							}
						}*/
						SetDlgItemText(hMain, IDC_EDIT3, "");
						SetFocus(TextMessageBox);
					}
				}


+ I have
1
2
3
4
5
case WM_INITDIALOG:
		{
			HWND TextMessageBox = GetDlgItem(hMain, IDC_EDIT3);
			SetFocus(TextMessageBox);
		}
but it's not working, I have same thing after u send message, and that one is working well, where can be problem?

edit: I just noticed that my WM_INITDIALOG is not working, I added messagebox there and it will just start...
Last edited on
#1 WM_INITDIALOG

Turns out it's not sent to dialogs when you are using a custom window class, as we are are doing here. So we have to do it ourselves!

Add this #define to the top of the source file

#define MY_WM_INITDIALOG (WM_USER + 1)

change WM_INITDIALOG: to MY_WM_INITDIALOG: in the main message loop

and add an extra case to the same switch statement : WM_CREATE

1
2
3
4
5
6
7
8
9
10
11
12
13
        case WM_CREATE:
        {
            // SendMessage sends a message and waits for return
            // PostMessage sends a message and continues on immediately
            // What we are doing here is adding MY_WM_INITDIALOG to the
            // message queue, so the message will arrive after the dialog
            // is constructed (because WM_CREATE arrives before the dialog
            // controls are created)
            PostMessage(hWnd, MY_WM_INITDIALOG, 0, 0);
            // Usually you return 0 for success, -1 for failure, but 
            // we're not really handling the message.
        }
        break;


And that should cause MY_WM_INITDIALOG to be called at the right time

#2 tabbing

Replace DefWindowProc() with DefDlgProc() at the end of WinProc

DefDlgProc is a special version of DefWindowProc that knows how to do the tabbing; it is not necessary if you don't need that capability.

#3 Disable logic

You need to handle the Edit control's EN_CHANGE notification. This is sent via a WM_COMMAND message, so the WM_COMMAND case of the ConnectDlgProc ends up like it is below.

You can see that it now checks for commands for the username and password controls (by id), and if they are any, looks to see if any are EN_CHANGE (i.e. notification that the tet has changed). If so, it checks the length of both strings and sets the button's enable state accordingly. (Note that I have changed the names of various things to make it easier for me to remember)

I am also disabling the "Connect" button in WM_INITDIALOG so set things up correctly.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
BOOL CALLBACK
ConnectDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);

    switch(uMsg)
    {
        case WM_INITDIALOG:
        {
            HWND hwndOK  = GetDlgItem(hwndDlg, IDOK);
            _ASSERTE(NULL != hwndOK);

            EnableWindow(hwndOK, FALSE);

            return TRUE;
        }
        // break not needed

        case WM_COMMAND:
        {
            int wmId    = LOWORD(wParam);
            int wmEvent = HIWORD(wParam);

            switch(wmId)
            {
                case IDOK:
                {
                    const char RUsername[] = "SEnergy";
                    const char RPassword[] = "pw";

                    char Username[256] = "";
                    char Password[256] = "";
                    GetDlgItemText(hwndDlg, IDC_EDIT_USERNAME, Username, sizeof(Username)/sizeof(char));
                    GetDlgItemText(hwndDlg, IDC_EDIT_PASSWORD, Password, sizeof(Password)/sizeof(char));

                    if(lstrcmp(RUsername, Username) == 0 && lstrcmp(RPassword, Password) == 0)
                    {
                        EndDialog(hwndDlg, IDOK);
                    }
                    else
                    {
                        MessageBox(hwndDlg, "Invalid Username or Password!", g_appName, MB_ICONERROR);
                    }

                    return TRUE;
                }
                break;

                case IDCANCEL:
                {
                    EndDialog(hwndDlg, IDCANCEL);
                    return TRUE;
                }
                break;

                case IDC_EDIT_USERNAME:
                case IDC_EDIT_PASSWORD:
                {
                    if(EN_CHANGE == wmEvent)
                    {
                        HWND hwndOK  = GetDlgItem(hwndDlg, IDOK);
                        _ASSERTE(NULL != hwndOK);

                        char Username[256] = "";
                        char Password[256] = "";
                        GetDlgItemText(hwndDlg, IDC_EDIT_USERNAME, Username, sizeof(Username)/sizeof(char));
                        GetDlgItemText(hwndDlg, IDC_EDIT_PASSWORD, Password, sizeof(Password)/sizeof(char));

                        BOOL bEnable = (0 < lstrlen(Username)) && (0 < lstrlen(Password));
                        EnableWindow(hwndOK, bEnable);
                    }
                }
                break;

                default:
                {
                }
            }
        }
        break;

        default:
        {
        }
    }

    return FALSE;
}

#1 Replacing IDOK with IDACCEPT might cause other problems.

If you want the return key to push the "Accept" button, as it does the "Send" button, you should prob. stick with IDOK.

That is if only one of the other is displayed as a time. Then your WM_COMMAND handler would change to:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
        case WM_COMMAND:
        {
            int wmId    = LOWORD(wParam);
            int wmEvent = HIWORD(wParam);

            switch(wmId)
            {
                case IDOK:
                {
                    if(showingAccept)
                    {
                        // do accept
                    }
                    else
                    {
                        // do send
                    }

                    ....


#2 /ban

I'm not sure I get this code, or what you mean by "annoying letters", but note that tolower() does not change a char in place (by ref). You need

bufferSmall[i] = tolower(bufferSmall[i]);

rather than

tolower(bufferSmall[i]);

But I don't see the need for bufferSmall. You don't need the data in buffer for other purposes, so you can replace this

1
2
3
4
5
6
7
8
9
10
11
12
13
			int i = 0;
			while(buffer[i])
			{
				bufferSmall[i] = buffer[i];
				if(islower(bufferSmall[i]))
					i++;
				else
				{
					bufferSmall[i] = tolower(bufferSmall[i]);
					i++;
				}
			}
			MessageBox(hMain, bufferSmall, NULL, MB_OK);


by

1
2
3
4
5
6
7
8
9
10
11
12
			int i = 0;
			while(buffer[i])
			{
				if(islower(buffer[i]))
					i++;
				else
				{
					buffer[i] = tolower(buffer[i]);
					i++;
				}
			}
			MessageBox(hMain, bufferSmall, NULL, MB_OK);


Or, as calling tolower() on a lower case char does nothing

1
2
3
4
5
6
7
			int i = 0;
			while(buffer[i])
			{
				buffer[i] = tolower(buffer[i]);
				i++;
			}
			MessageBox(hMain, bufferSmall, NULL, MB_OK);


or even use the corrresponding function -- _strlwr / _strlwr_s

1
2
			_strlwr_s(buffer, sizeof(buffer)/sizeof(char));
			MessageBox(hMain, bufferSmall, NULL, MB_OK);


And this doesn't make sense to me:

1
2
3
4
5
6
7
8
			for(int i = 0; i < 4; i++)
			{
				if(strcmp(&buffer[i],&BanCommand[i]) == 0)
				{
					EnableWindow(TextMessageBox, FALSE);
					break;
				}
			}


strcmp() only returns 0 if the two strings are exactly the same. Here you're testing bits of strings: for &BanCommand[2] = "an".

If you were meaning to test the first 4 chars of the string so see if they're "/ban", then this might work: assume the string is right, test each char, it one is wrong change mind and break loop.

1
2
3
4
5
6
7
8
9
10
11
		bool bDisable = true;
		for(int i = 0; i < 4; i++)
		{
			if(buffer[i] != BanCommand[i])
			{
				bDisable = false;
				break;
			}
		}
		if(bDisable)
			EnableWindow(hwndInput, bEnable);


But there's a function for this, too

1
2
3
		// "/ban" is 4 chars long
		if(strcmp(buffer, BanCommand, 4) == 0)
			EnableWindow(hwndInput, FALSE);

Last edited on
Once these loose ends are tied up, I think it's time to end this thread.

If there are no bugs in the code I sent you, I'll post my final version.

Andy
well, thanks, I have just few bugs, but ther are not necessary to fix, and few things to tell ya

#1 with DefDlgProc() PostQuitMessage(0) dont works, so I need to use DefWindowProc()

#2 I don't understand how does EN_CHANGE works, but I'll try to understand it :P

#3 I have no idea what do u mean with that
#1 Replacing IDOK with IDACCEPT might cause other problems.
cuz I don;'t have IDACCEPT anywhere :-O

#4 that /ban command, I wanted to get the first letters, and if they are /ban it will disable edit box, that u understood, I wanted to lower them cuz with that I should use /Ban and it should do same thing, but that thing with "for loop" works perfectly, I jusst changed it a little so it lowers characters :P -- you won't understand this probably, my english is bad :D

#5 if(strcmp(buffer, BanCommand, 4) == 0) function does not take 3 arguments

I hope that's all

oh yea, and whaat about that program with read only edit control ? :P

edit: and I forgot, thank u very much, u learned me a lot, more than thousands tutorials on internet cuz u explained me things I wanted to know, and I'm really, really grateful to you for that :)
Last edited on
well, thanks, I have just few bugs, but ther are not necessary to fix, and few things to tell ya


#1 with DefDlgProc() PostQuitMessage(0) dont works, so I need to use DefWindowProc()


Does for me. Are you handling "ID_FILE_EXIT" and "IDCANCEL" OK???

#2 I don't understand how does EN_CHANGE works, but I'll try to understand it :P


All WM_COMMAND's from controls arrive with both a control ID (e.g. IDACCEPT) and a notifcation code (e.g. EN_CHANGE). Sometimes you're just expecting one notification code, as with your buttons, so you can switch on just the control ID. But if you wanted to handle double-clicks differently to single clicks, you'd need to test for BN_DBLCLK and BN_CLICKED. (see MSDN entry for WM_COMMAND)

#3 I have no idea what do u mean with that / cuz I don;'t have IDACCEPT anywhere :-O


You did say you wanted to swap your button at runtime? Just warning you that if you do replace IDOK with IDACCEPT (or any other ID), then IsDialogMessage will not convert <return> to a button press. If this OK, no worries. If you need this to happen, you must either use IDOK or replace IsDialogMessage with custom accelerator handling (inc. tabbing code).

#4 that /ban command...

It works! cool!

#5 if(strcmp(buffer, BanCommand, 4) == 0) function does not take 3 arguments


Oop! meant strncmp() -- must have looked through the 'n'

oh yea, and whaat about that program with read only edit control ? :P

I have it! Now all loose ends are tied up, I'll check my code and post it to end the thread.

edit: and I forgot, thank u very much, u learned me a lot, more than thousands tutorials on internet cuz u explained me things I wanted to know, and I'm really, really grateful to you for that :)


:-)

So are you going on to code the rest of the app? The communication, security, and configuration code? (WinSock2, CryptoAPI, Registry API, ... ?)
#1 ID_FILE_EXIT? :P http://pastebin.com/hUYRk9Kg

#2 ok I'll check msdn :P

#3 well I changed my mind, I'll use ShowWindow instead :P

#4 :)

#5 well, it works with for loop, so I won't change it probably :P

and yes, I'm going to finish it, I ordered book with tutorials for MySQL DB connection, TCP/IP Connection etc, simply, things with sockets and classes :P
#1 ID_FILE_EXIT?

Well, looking back at an earlier version of your code, I think I mean IDM_EXIT1

Add this to the switch statement in the main window proc's WM_COMMAND handler (IDCANCEL handles the windows [x] button)

1
2
3
4
5
6
7
8
9
				case IDM_EXIT1:
				case IDCANCEL:
				{
					// you could ask the user if they're sure they really want
					// to exit here?
					DestroyWindow(hMain);
					return 0;
				}
				break;


And, add the menu (back?) to the main dialog using the resource editor, or by editing the text, so it reads;

MENU IDR_MENU1

(along with the CLASS, CAPTION, etc)
Last edited on
#1 ahaa, that was menu thing, I already deleted that (file > exit)

+ I have menu there :P file > ban/unban
I've uploaded the whole mini-project up on http://www.mediafire.com

If I got my access rights correct, this url should allow you to download my_chat.zip [1].
http://www.mediafire.com/?2mmks21lzwami6i

The zip file contains the following files:

app.ico		application, smiley icon
main.cpp	main cpp file -- all code
MyChat.jpg	screen shot
my_chat.rc	resource file
my_chat.vcproj	Visual C++ 2008 project file
resource.h	resource header (originally created with resedit [2]


This version :
- has been tidied up!
- has an about box
- uses a read-only window for the message output
- has code to respond, randomly to message - for testing purposes!

If you get back to me soon with any problems, I will probably notice. But hopefully you won't have to!

Andy

[1] Created using 7-zip : http://www.7-zip.org/
[2] http://www.resedit.net/
A post-final thought

Have you thought about using a chatbot to help you with your testing?

I bumped into this basic chatbot on CodeProject.com while looking about for someting (I forget what, but it was involved with the French vocab test app I mentioned previously), and added it to my "maybe useful one day" list.

So, just in case it's of use to you:

http://www.codeproject.com/KB/library/ProjectEliza.aspx
Last edited on
Pages: 123