Given that you have a Win32 dialog box with static
text in an LTEXT
control,
how do you make it so that users can easily copy that
text to the clipboard?
The traditional solution is to create a borderless read-only edit control
(which draws as static text by default).
Add it to the tab order by setting the WS_TABSTOP
style, and maybe even
give it a keyboard accelerator for accessibility.
Starting in Windows Vista,
version 6 of the common controls provides an alternative.
(A less accessible alternative, mind you.)
Static text controls automatically copy their contents
to the clipboard when you double-click them
if you set the SS_NOTIFY
style.
Let’s try it:
#include <windows.h> #include <windowsx.h> #include <commctrl.h> #pragma comment(linker, \ "\"/manifestdependency:type='Win32' "\ "name='Microsoft.Windows.Common-Controls' "\ "version='6.0.0.0' "\ "processorArchitecture='*' "\ "publicKeyToken='6595b64144ccf1df' "\ "language='*'\"") INT_PTR CALLBACK DlgProc( HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_INITDIALOG: return TRUE; case WM_COMMAND: switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: EndDialog(hdlg, 0); break; } } return FALSE; } int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpCmdLine, int nShowCmd) { return DialogBox(hinst, MAKEINTRESOURCE(1), NULL, DlgProc); } // resource file #include <windows.h> 1 DIALOG 50, 50, 100, 50 STYLE DS_SHELLFONT | WS_SYSMENU CAPTION "Sample" FONT 8, "MS Shell Dlg" BEGIN LTEXT "Sample text 1",100,10,10,80,10,SS_NOTIFY LTEXT "Sample text 2",101,10,20,80,10,SS_NOTIFY LTEXT "Sample text 3",102,10,30,80,10 END
Run this program and double-click on the text controls,
and observe that the text gets copied to the clipboard,
or at least it does for the first two, since I
set the SS_NOTIFY
style on them.
Now, when the double-click to copy
feature was added to the static control,
there was no way to suppress it.
The STN_DBLCLK
notification is documented
as ignoring its return code,
so it would be a compatibility problem if suddenly it started
studying its return code so that the parent could respond
“No, I handled the click, don’t do your default action.”
Instead, if you want to disable the double-click to copy
feature on a SS_NOTIFY
static control,
you have to subclass the static control and eat
the clicks yourself.
LRESULT CALLBACK SuppressCopyOnClick( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) { switch (uMsg) { case WM_LBUTTONDBLCLK: return 0; // eat the double-click case WM_NCDESTROY: RemoveWindowSubclass(hwnd, SuppressCopyOnClick, uIdSubclass); break; } return DefSubclassProc(hwnd, uMsg, wParam, lParam); } INT_PTR CALLBACK DlgProc( HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_INITDIALOG: SetWindowSubclass(GetDlgItem(hdlg, 101), SuppressCopyOnClick, 0, 0); return TRUE; ...
“Why would you add a style that enabled a feature, and then disable the feature?”
Maybe you want some other aspects of the feature but not the
copy-on-double-click behavior.
Maybe somebody else is adding the SS_NOTIFY
style behind your back.
For example, a UI framework might add it automatically to all
static controls.
And actually, in that UI framework case,
you probably want the STN_DBLCLK
notification
to be fired when a double-click occurs,
because you added an
OnDoubleClick
handler to your class.
You just don’t want the copy-to-clipboard behavior.
We can fix that by firing the notification in our subclass
procedure.
case WM_LBUTTONDBLCLK: if (GetWindowStyle(hwnd) & SS_NOTIFY) { FORWARD_WM_COMMAND(GetParent(hwnd), GetDlgCtrlID(hwnd), hwnd, STN_DBLCLK, SendMessage); } return 0; // message handled
To illustrate this change, we’ll make our dialog box beep
when it gets a double-click notification.
In real life, of course, you would do whatever you want
to happen on the “double click on a static control” event.
Actually, in real life, the code that responds to the
STN_DBLCLK
lives inside your framework, and it turns around and
raises an OnDoubleClick
event,
but for simplicity, we’ll just code it inline.
case WM_COMMAND: switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: EndDialog(hdlg, 0); break; case 100: case 101: case 102: switch (GET_WM_COMMAND_CMD(wParam, lParam)) { // Obviously we would do something more interesting here case STN_DBLCLK: MessageBeep(MB_OK); break; } }
Each of the static controls on the dialog behaves differently.
The first one is SS_NOTIFY
with no subclassing,
so double-clicking copies the text to the clipboard and also
beeps.
The second one is
SS_NOTIFY
with subclassing to disable
the copy-to-clipboard, so
double-clicking merely beeps.
And the third one doesn’t have the SS_NOTIFY
style at all, so it neither copies the next nor responds
to double-click.
0 comments