The new 'Split Button' control style.

'Split Button' seems to have been around for quite a while, but there is very little concrete reference material on it that I could find - from a pure Win32 perspective at least. Admittedly quite a few class libraries encapsulate it and give instructions on how to use their implementation. However this is not very helpful to me, except by similarity as I do not write Windows programmes using wrapper classes.

What little I can make out is through inference from the documentation for the BCN_DROPDOWN message and the NMBCDROPDOWN notification structure. There IS a fairly exhaustive skeleton - more of a zombie I would say given the meat on its bones - function under the "How to Handle the BCN_DROPDOWN Notification from a Split Button" item in the MSDN entry "Using Buttons". However, again any real guidance on how to actually and metaphorically 'implement' a Split Button in your own projects is more implied than stated.

Therefore I wonder if any of you chaps have used these new controls yourself from the bare win32 metal as it were? Do you HAVE to use a popup menu to access additional functionality? If you are only splitting two actions across one split button - say 'Browse for folder' and 'Reset' - could you not just work from the BCNDROPDOWN notification directly, performing the reset from there instead of using it in turn to conjure a menu and only THEN resetting the GUI from its eventual WM_COMMAND 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
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
#define _WIN32_WINNT _WIN32_WINNT_VISTA
#include <windows.h>

#pragma comment(lib, "comctl32.lib")
#include <commctrl.h>

#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' \
version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")

enum { ID_BTN, ON_CREATE = WM_USER+1 };

INT_PTR CALLBACK DialogProc(HWND hDialog, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
	case ON_CREATE:
		INITCOMMONCONTROLSEX iccx;
		iccx.dwSize = sizeof iccx;
		iccx.dwICC = ICC_STANDARD_CLASSES;
		InitCommonControlsEx(&iccx);

		CreateWindow(TEXT("Button"), TEXT("Split Button"), BS_SPLITBUTTON | WS_VISIBLE |
			WS_CHILD, 20, 20, 120, 40, hDialog, (HMENU)ID_BTN, (HINSTANCE)lParam, NULL);
		return TRUE;

	case WM_COMMAND:
		if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == ID_BTN)
		{
			MessageBox(hDialog, TEXT("Button clicked!"), TEXT("Test"), MB_OK);
			// button code

			return TRUE;
		}
		break;
	
	case WM_NOTIFY:
		switch (((LPNMHDR)lParam)->code)
		{
		case BCN_DROPDOWN:
			NMBCDROPDOWN* pDropDown = (NMBCDROPDOWN*)lParam;
			if (pDropDown->hdr.hwndFrom == GetDlgItem(hDialog, ID_BTN))
			{
				MessageBox(hDialog, TEXT("Drop-down clicked!"), TEXT("Test"), MB_OK);
				// dropdown code

				return TRUE;
			}
			break;
		}
		return FALSE;

	case WM_CLOSE:
		DestroyWindow(hDialog);
		return TRUE;

	case WM_DESTROY:
		PostQuitMessage(0);
		return TRUE;
	}
	return FALSE; 
}

int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nShowCmd)
{
	HWND hDialog = CreateWindow(WC_DIALOG, TEXT("Test"), WS_OVERLAPPEDWINDOW | WS_VISIBLE,
		200, 200, 500, 400, NULL, NULL, hInst, NULL);

	SetWindowLongPtr(hDialog, DWLP_DLGPROC, (LONG_PTR)DialogProc);
	SendMessage(hDialog, ON_CREATE, 0, (LPARAM)hInst);
	
	MSG msg;
	while (GetMessage(&msg, NULL, 0, 0) > 0)
	{
		if (!IsDialogMessage(hDialog, &msg))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}
	return msg.wParam;
}

If you are only splitting two actions across one split button - say 'Browse for folder' and 'Reset' - could you not just work from the BCNDROPDOWN notification directly, performing the reset from there instead of using it in turn to conjure a menu

Could you clarify what you are attempting to achieve? as I don't really follow you here.
Last edited on
Sure thing duky - and MANY thanks for such a lengthy code snippet, I will study it in depth once I have written this reply.

The scenario I am trying to describe is if you only want to combine two mutually-exclusive actions on the same split button. For instance you might want the main function of the button to be to open a dialog that lets you choose a folder, which choice is then displayed in an attendant edit control. In that situation you might very well want the 'split' function - however you get to it - to remove a previously chosen folder from the edit control and reset it to a default value.

Now, it seems like MS designed the split button to be able to offer many mutually exclusive commands. Therefore the behaviour their example code provides is to open a pop-up menu when the 'down arrow' of the split button is clicked. Programmatically this works by responding to the BCNDROPDOWN notification message and at that point opening a popup menu. When a menu item is chosen from that menu by the user a WM_COMMAND message will be issued with its LOWORD(wParam) set to the menu item's identifier, which is in turn received by the programme and acted upon appropriately.

That is fine and straightforward enough IF you have three or more behaviours you want to control from one split button/popup menu pairing. However, when you only have TWO behaviours, like in my set/reset example above it seems unnecessary both in terms of programming effort AND run-time processor cycles to go through the whole rigmarole of causing a popup menu to appear with only one menu item on it.

Instead of I wonder if it is not acceptable - again, only in the specific case of a split button offering just two operations - to short-circuit that process, do away with the summon popup-menu/receive-WM_COMMAND/perform-operation steps and just perform the desired action when first receiving the BCNDROPDOWN notification?

To get to the crux - do you HAVE to always produce a popup-menu in response to a click on a split button 'down arrow' or can you have that click perform an operation itself?
Ah I see! well in that case, yes you could handle the BCNDROPDOWN notification however you wish, but changing the expected behavior is going against the windows user experience.
http://en.wikipedia.org/wiki/Human_interface_guidelines

I think it might be better to separate the actions, maybe a static control that displays the folder and opens a file dialog when clicked, with an x button beside it to reset?

Let me know if you have and problems or queries about the snippet I posted.
Topic archived. No new replies allowed.