c++ win32 add a hyperlink to a dialog - c++

I would like to add an About dialog to my Win32 application (developed using C++). How can I add a hyperlink to the dialog? I'm loading the dialog from a resource file (.rc). Is it possible to define this functionality from the .rc file?
My .rc file now looks like this:
IDD_ABOUTBOX DIALOGEX 0, 0, 218, 118
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU | DS_CENTER
CAPTION "About My App"
FONT 8, "MS Shell Dlg"
BEGIN
ICON IDI_APP_ICON,IDC_STATIC,13,88,15,15
LTEXT "MY url http://www.myurl.com",IDC_STATIC,15,6,194,24,SS_NOPREFIX
DEFPUSHBUTTON "OK",IDOK,95,98,50,14,WS_GROUP
END

You can use a SysLink Control on Windows XP or above.
You can define it from the .rc file like this:
In resource.rc:
CONTROL "<a>Link</a>",IDC_SYSLINK1,"SysLink",WS_TABSTOP,7,7,53,12
In resource.h:
#define IDC_SYSLINK1 1001

Best way to do the highlighting without any external libraries, still looks and feels the same way any control would do it, even makes the mouse cursor into a finger pointing icon.
/* Start of HyperLink URL */
#define PROP_ORIGINAL_FONT TEXT("_Hyperlink_Original_Font_")
#define PROP_ORIGINAL_PROC TEXT("_Hyperlink_Original_Proc_")
#define PROP_STATIC_HYPERLINK TEXT("_Hyperlink_From_Static_")
#define PROP_UNDERLINE_FONT TEXT("_Hyperlink_Underline_Font_")
LRESULT CALLBACK _HyperlinkParentProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK _HyperlinkProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
static void CreateHyperLink(HWND hwndControl);
/* End of HyperLink URL */
static void CreateHyperLink(HWND hwndControl)
{
// Subclass the parent so we can color the controls as we desire.
HWND hwndParent = GetParent(hwndControl);
if (NULL != hwndParent)
{
WNDPROC pfnOrigProc = (WNDPROC)GetWindowLong(hwndParent, GWL_WNDPROC);
if (pfnOrigProc != _HyperlinkParentProc)
{
SetProp(hwndParent, PROP_ORIGINAL_PROC, (HANDLE)pfnOrigProc);
SetWindowLong(hwndParent, GWL_WNDPROC, (LONG)(WNDPROC)_HyperlinkParentProc);
}
}
// Make sure the control will send notifications.
DWORD dwStyle = GetWindowLong(hwndControl, GWL_STYLE);
SetWindowLong(hwndControl, GWL_STYLE, dwStyle | SS_NOTIFY);
// Subclass the existing control.
WNDPROC pfnOrigProc = (WNDPROC)GetWindowLong(hwndControl, GWL_WNDPROC);
SetProp(hwndControl, PROP_ORIGINAL_PROC, (HANDLE)pfnOrigProc);
SetWindowLong(hwndControl, GWL_WNDPROC, (LONG)(WNDPROC)_HyperlinkProc);
// Create an updated font by adding an underline.
HFONT hOrigFont = (HFONT)SendMessage(hwndControl, WM_GETFONT, 0, 0);
SetProp(hwndControl, PROP_ORIGINAL_FONT, (HANDLE)hOrigFont);
LOGFONT lf;
GetObject(hOrigFont, sizeof(lf), &lf);
lf.lfUnderline = TRUE;
HFONT hFont = CreateFontIndirect(&lf);
SetProp(hwndControl, PROP_UNDERLINE_FONT, (HANDLE)hFont);
// Set a flag on the control so we know what color it should be.
SetProp(hwndControl, PROP_STATIC_HYPERLINK, (HANDLE)1);
}
LRESULT CALLBACK _HyperlinkParentProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
WNDPROC pfnOrigProc = (WNDPROC)GetProp(hwnd, PROP_ORIGINAL_PROC);
switch (message)
{
case WM_CTLCOLORSTATIC:
{
HDC hdc = (HDC)wParam;
HWND hwndCtl = (HWND)lParam;
BOOL fHyperlink = (NULL != GetProp(hwndCtl, PROP_STATIC_HYPERLINK));
if (fHyperlink)
{
LRESULT lr = CallWindowProc(pfnOrigProc, hwnd, message, wParam, lParam);
SetTextColor(hdc, RGB(0, 0, 192));
return lr;
}
break;
}
case WM_DESTROY:
{
SetWindowLong(hwnd, GWL_WNDPROC, (LONG)pfnOrigProc);
RemoveProp(hwnd, PROP_ORIGINAL_PROC);
break;
}
}
return CallWindowProc(pfnOrigProc, hwnd, message, wParam, lParam);
}
LRESULT CALLBACK _HyperlinkProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
WNDPROC pfnOrigProc = (WNDPROC)GetProp(hwnd, PROP_ORIGINAL_PROC);
switch (message)
{
case WM_DESTROY:
{
SetWindowLong(hwnd, GWL_WNDPROC, (LONG)pfnOrigProc);
RemoveProp(hwnd, PROP_ORIGINAL_PROC);
HFONT hOrigFont = (HFONT)GetProp(hwnd, PROP_ORIGINAL_FONT);
SendMessage(hwnd, WM_SETFONT, (WPARAM)hOrigFont, 0);
RemoveProp(hwnd, PROP_ORIGINAL_FONT);
HFONT hFont = (HFONT)GetProp(hwnd, PROP_UNDERLINE_FONT);
DeleteObject(hFont);
RemoveProp(hwnd, PROP_UNDERLINE_FONT);
RemoveProp(hwnd, PROP_STATIC_HYPERLINK);
break;
}
case WM_MOUSEMOVE:
{
if (GetCapture() != hwnd)
{
HFONT hFont = (HFONT)GetProp(hwnd, PROP_UNDERLINE_FONT);
SendMessage(hwnd, WM_SETFONT, (WPARAM)hFont, FALSE);
InvalidateRect(hwnd, NULL, FALSE);
SetCapture(hwnd);
}
else
{
RECT rect;
GetWindowRect(hwnd, &rect);
POINT pt = { LOWORD(lParam), HIWORD(lParam) };
ClientToScreen(hwnd, &pt);
if (!PtInRect(&rect, pt))
{
HFONT hFont = (HFONT)GetProp(hwnd, PROP_ORIGINAL_FONT);
SendMessage(hwnd, WM_SETFONT, (WPARAM)hFont, FALSE);
InvalidateRect(hwnd, NULL, FALSE);
ReleaseCapture();
}
}
break;
}
case WM_SETCURSOR:
{
// Since IDC_HAND is not available on all operating systems,
// we will load the arrow cursor if IDC_HAND is not present.
HCURSOR hCursor = LoadCursor(NULL, IDC_HAND);
if (NULL == hCursor)
hCursor = LoadCursor(NULL, IDC_ARROW);
SetCursor(hCursor);
return TRUE;
}
}
return CallWindowProc(pfnOrigProc, hwnd, message, wParam, lParam);
}
Here is how to use it:
CreateHyperLink(GetDlgItem(Dialog_HWND_GOES_HERE, STATIC_TEXT_IDENIFIER_GOES_HERE));
Where the static label can get clicked in the main dialogs subclass do something like this..
if (HIWORD(wParam) == BN_CLICKED) { //Buttons, checkboxs, labels, static labels clicked
switch (LOWORD(wParam))
{
case STATIC_TEXT_IDENIFIER_GOES_HERE:
ShellExecute(NULL, "open", "http://www.google.com", NULL, NULL, SW_SHOWNORMAL);
break;
}
}

Related

How to paint a text control on mouse hover?

I am having troubles painting my static text controls. I have done some reading on this matter, and I have also tried some codes, but none of them worked. The closest that I managed to get was until the text control detects my mouse hover event. For this, I use control subclassing. However, I don't know what I can do to paint the text controls upon mouse hover.
My questions are:
I am confused whether to place the code responsible for painting the text control either in WM_MOUSEMOVE or WM_MOUSEHOVER in the subclass. The reason is because in the WM_MOUSEMOVE, I set it so that when the rectangle area of the text control detects the cursor, painting should happen. At the same time, I feel weird because shouldn't that particular role already be done by WM_MOUSEHOVER?
I really need help on how I could highlight the text control when the mouse is hovered.
// global text control handler
HWND txtHwnd; //somewhere in the global area of codes
//class for mouse event tracking
class MouseTrackEvents
{
bool m_bMouseTracking;
public:
MouseTrackEvents() : m_bMouseTracking(false) {}
void OnMouseMove(HWND hwnd)
{
if (!m_bMouseTracking)
{
// Enable mouse tracking.
TRACKMOUSEEVENT tme;
tme.cbSize = sizeof(tme);
tme.hwndTrack = hwnd;
tme.dwFlags = TME_HOVER;
tme.dwHoverTime = 100; //0.1s
TrackMouseEvent(&tme);
m_bMouseTracking = true;
}
}
void Reset(HWND hwnd)
{
m_bMouseTracking = false;
}
};
//subclass proc for text control
LRESULT CALLBACK OwnerTxtProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) {
//local mouse track object
MouseTrackEvents mouseTrack;
const int numOfMsg = 4;
int myMsgBox[numOfMsg]; //limit to only 4
HBRUSH hbrBkgndA = CreateSolidBrush(RGB(0, 255, 0));
//just want to print what wparam is..
int wmId, wmEvent;
wchar_t wParamHI[1000];
wchar_t wParamLO[1000];
//for painting
COLORREF hbrHighlight;
//HBRUSH hbrHighlight;
HDC txtHDC;
RECT txtRect;
POINT txtPt;
RECT txtRt;
switch (uMsg) {
case WM_CREATE: {
//testing purposes
myMsgBox[0] = MessageBox(nullptr, L"Yessir!", L":D", MB_OK | MB_ICONINFORMATION);
break;
}
case WM_MOUSEMOVE: {
mouseTrack.OnMouseMove(hWnd); //activate mouse tracking
GetWindowRect(hWnd, &txtRt); //see what value lies within
GetCursorPos(&txtPt);
if (PtInRect(&txtRt, txtPt)) {
//to check whether cursor is in text rect or not
MessageBox(nullptr, L"Okea..nice..!", L":D", MB_OK | MB_ICONINFORMATION);
//i dont know what to do here
//i was thinking that painting should be placed here...
//HBRUSH hbrBkgndEdit = CreateSolidBrush(RGB(100, 100, 255));
}
else {
//to check whether cursor is in text rect or not
MessageBox(nullptr, L"Oh noooo!", L":D", MB_OK | MB_ICONINFORMATION);
}
break;
}
case WM_MOUSEHOVER: {
//do something when txt ctrl is hovered
//to test whether wm_mousehover works, and the text control is mouse hovered
//myMsgBox[1] = MessageBox(nullptr, L"Yahooooo!", L":D", MB_OK | MB_ICONINFORMATION);
OutputDebugString(L"--------------This text control is being hovered---------------\n");
//testing painting when hovered
//plan is to send message to hChild to manage the painting since it is the parent window
//HDC theHDC = (HDC)wParam;
//SendMessage(hWnd, WM_CTLCOLORSTATIC, (WPARAM)theHDC, (LPARAM)txtHwnd);
//SendMessage(hWnd, WM_CTLCOLORSTATIC, (WPARAM)theHDC, lParam);
//for painting
//hbrHighlight = (COLORREF)(GetSysColor(COLOR_HIGHLIGHT));
//HDC hdcStatic = (HDC)wParam;
////SetTextColor(hdcStatic, RGB(0, 0, 0));
//SetBkColor(hdcStatic, hbrHighlight);
//for painting II
//HDC txtCtrlMe = (HDC)wParam;
//SetTextColor(txtCtrl, RGB(0, 0, 0));
//SetBkColor(txtCtrlMe, RGB(0, 255, 0));
//SendMessage(hChild, WM_CTLCOLORSTATIC, (WPARAM)txtCtrlMe, (LPARAM)txtHwnd);
//DeleteBrush(hbrBkgndA);
mouseTrack.Reset(hWnd);
//return (INT_PTR)hbrHighlight;
//mouseTrack.Reset(hWnd);
break;
//return TRUE;
}
//case WM_CTLCOLORSTATIC:
//{
// HDC txtCtrlMe = (HDC)wParam;
// //SetTextColor(txtCtrl, RGB(0, 0, 0));
// SetBkColor(txtCtrlMe, RGB(0, 255, 0));
// if (hbrBkgndA == NULL)
// {
// hbrBkgndA = CreateSolidBrush(RGB(255, 255, 255)); //eqivalent to delete brush objet
// }
// return (INT_PTR)hbrBkgndA;
// //break;
//}
//painting
/*
SendMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
SendMessage(hChild, WM_CTLCOLORSTATIC, (WPARAM)txtCtrlMe, (LPARAM)txtHwnd);
*/
//you can just ignore this, this is just for me to see whats inside wParam of the subclass
case WM_LBUTTONDOWN: {
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
int HI = swprintf_s(wParamHI, 1000, L"wParamHI: %d", wmEvent);
MessageBox(nullptr, wParamHI, L":D", MB_OK | MB_ICONINFORMATION);
int LO = swprintf_s(wParamLO, 1000, L"wParamLO: %d", wmId);
MessageBox(nullptr, wParamLO, L":D", MB_OK | MB_ICONINFORMATION);
break;
}
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
//child window proc, which contains the static text control
LRESULT CALLBACK WndProcChild(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
//...some other codes...
txtHwnd = CreateWindow(L"Static", L"Hello World!", WS_VISIBLE | WS_CHILD | SS_NOTIFY, 100, 100, 120, 15, hChild, (HMENU)testingTxtID, nullptr, nullptr);
//control subclassing
//a.k.a attaching subclass proc to the static text control
SetWindowSubclass(txtHwnd, OwnerTxtProc, 0, 0);
//...some other codes...
default: {
return DefWindowProc(hChild, message, wParam, lParam);
}
return 0;
}
Please ignore the codes I commented out in WM_MOUSEHOVER and also WM_CTLCOLORSTATIC inside OwnerTxtProc(...). They are just codes that I used to try to paint the text control, which didn't work.
All painting operations for your control should be only inside of a WM_CTLCOLORSTATIC handler, or even a WM_PAINT handler.
To trigger a repaint of the control, simply invalidate the control's client area using InvalidateRect().
If you want to change the painting conditions, store the relevant information in variables that the painting code uses, and then update those variables before invalidating the control.
Try something like this:
HWND txtHwnd;
bool bMouseTracking = false;
COLORREF bkgndColor = ...; // whatever you want...
COLORREF txtColor = ...; // whatever you want...
HBRUSH hbrBkgnd = nullptr;
const UINT APP_CTLCOLORSTATIC = WM_APP + 1;
LRESULT CALLBACK StaticTxtProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) {
switch (uMsg) {
case WM_NCDESTROY: {
RemoveWindowSubclass(hWnd, StaticTxtProc, uIdSubclass);
if (hbrBkgnd) {
DeleteBrush(hbrBkgnd);
hbrBkgnd = NULL;
}
bMouseTracking = false;
break;
}
case WM_MOUSEMOVE: {
if (!bMouseTracking) {
TRACKMOUSEEVENT tme = {};
tme.cbSize = sizeof(tme);
tme.hwndTrack = hWnd;
tme.dwFlags = TME_HOVER;
tme.dwHoverTime = 100; //0.1s
bMouseTracking = TrackMouseEvent(&tme);
}
RECT txtRt;
GetWindowRect(hWnd, &txtRt);
POINT txtPt;
GetCursorPos(&txtPt);
if (PtInRect(&txtRt, txtPt)) {
bkgndColor = ...; // whatever you want...
txtColor = ...; // whatever you want...
InvalidateRect(hWnd, NULL, TRUE);
}
break;
}
case WM_MOUSEHOVER: {
mMouseTracking = false;
bkgndColor = ...; // whatever you want...
txtColor = ...; // whatever you want...
InvalidateRect(hWnd, NULL, TRUE);
break;
}
case APP_CTLCOLORSTATIC: {
HDC hdc = reinterpret_cast<HDC>(wParam);
SetTextColor(hdc, txtColor);
SetBkColor(hdc, bkgndColor);
if (hbrBkgnd)
DeleteBrush(hbrBkgnd);
hbrBkgnd = CreateSolidBrush(bkgndColor);
return reinterpret_cast<LRESULT>(bkgndColor);
}
case WM_LBUTTONDOWN: {
// ...
break;
}
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
LRESULT CALLBACK WndProcChild(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_CREATE: {
bkgndColor = ...; // whatever you want...
txtColor = ...; // whatever you want...
txtHwnd = CreateWindow(L"Static", L"Hello World!", WS_VISIBLE | WS_CHILD | SS_NOTIFY, 100, 100, 120, 15, hChild, (HMENU)testingTxtID, nullptr, nullptr);
SetWindowSubclass(txtHwnd, StaticTxtProc, 0, 0);
break;
}
case WM_CTLCOLORSTATIC: {
return SendMessage(reinterpret_cast<HWND>(lParam), APP_CTLCOLORSTATIC, wParam, 0);
}
//...some other codes...
default: {
return DefWindowProc(hChild, message, wParam, lParam);
}
}
return 0;
}
(Posting solution on behalf of the question author to move it to the answer space).
I managed to deal with my problem on this matter.
//global variable(s)
COLORREF bkgndColor;
COLORREF txtColor;
HBRUSH hbrBkgnd = nullptr;
const UINT APP_CTLCOLORSTATIC = WM_APP + 1;
//class for mouse event
class MouseTrackEvents
{
bool m_bMouseTracking;
public:
MouseTrackEvents() : m_bMouseTracking(false) {}
void OnMouseMove(HWND hwnd)
{
if (!m_bMouseTracking)
{
// Enable mouse tracking.
TRACKMOUSEEVENT tme;
tme.cbSize = sizeof(tme);
tme.hwndTrack = hwnd;
tme.dwFlags = TME_HOVER | TME_LEAVE;
tme.dwHoverTime = 10; //1ms
TrackMouseEvent(&tme);
m_bMouseTracking = true;
}
}
void Reset(HWND hwnd)
{
m_bMouseTracking = false;
}
};
//proc for static text contorl subclass
LRESULT CALLBACK OwnerTxtProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) {
//local mouse track variable
MouseTrackEvents mouseTrack;
RECT txtRt;
GetWindowRect(hWnd, &txtRt);
POINT txtPt;
GetCursorPos(&txtPt);
switch (uMsg) {
//...some other codes...
case WM_NCDESTROY: {
RemoveWindowSubclass(hWnd, OwnerTxtProc, uIdSubclass);
if (hbrBkgnd) {
DeleteBrush(hbrBkgnd);
hbrBkgnd = NULL;
}
mouseTrack.Reset(hWnd);
break;
}
case WM_MOUSEMOVE: {
mouseTrack.OnMouseMove(hWnd); //activate mouse tracking
break;
}
case WM_MOUSEHOVER: {
if (PtInRect(&txtRt, txtPt)) {
OutputDebugString(L"--------------This text control is being hovered---------------\n");
bkgndColor = RGB(0, 0, 200); // blue
txtColor = RGB(200, 0, 0); // red
InvalidateRect(hWnd, NULL, FALSE);
}
else {
OutputDebugString(L"--------------This text control is being left---------------\n");
bkgndColor = RGB(100, 0, 0); // red bg
txtColor = RGB(0, 100, 0); // green txt
InvalidateRect(hWnd, NULL, FALSE);
}
mouseTrack.Reset(hWnd);
break;
}
case WM_MOUSELEAVE: {
if (!(PtInRect(&txtRt, txtPt))) {
OutputDebugString(L"--------------This text control is being hovered---------------\n");
bkgndColor = RGB(100, 0, 0); // red bg
txtColor = RGB(0, 100, 0); // green txt
InvalidateRect(hWnd, NULL, FALSE);
}
mouseTrack.Reset(hWnd);
break;
}
case APP_CTLCOLORSTATIC: {
HDC hdc = reinterpret_cast<HDC>(wParam);
SetTextColor(hdc, txtColor);
SetBkColor(hdc, bkgndColor);
if (hbrBkgnd) {
DeleteBrush(hbrBkgnd);
}
hbrBkgnd = CreateSolidBrush(bkgndColor);
//return reinterpret_cast<LRESULT&>(bkgndColor); //--> error --> so i added '&'
return (LRESULT)hbrBkgnd;
}
//...some other codes...
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
//parent of static text control
LRESULT CALLBACK WndProcChild(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
//...some other codes...
switch (message)
{
case WM_CTLCOLORSTATIC:
{
return SendMessage(reinterpret_cast<HWND>(lParam), APP_CTLCOLORSTATIC, wParam, 0);
}
case WM_CREATE: //put all sorts of stuff when the context menu is called
{
//font to be set to
hFont = CreateFont(17, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, TEXT("Segoe UI"));
//set font of the textCtrl
//doesnt work if handler target = hChild
for (int i = 0; i < textCtrlSize; i++) {
SendMessage(textCtrl[i], WM_SETFONT, (WPARAM)hFont, TRUE);
}
bkgndColor = RGB(100, 0, 0); // red bg
txtColor = RGB(0, 100, 0); // green txt
txtHwnd = CreateWindow(L"Static", L"Hello World!", WS_VISIBLE | WS_CHILD | SS_NOTIFY, 100, 100, 120, 30, hChild, (HMENU)testingTxtID, nullptr, nullptr);
SetWindowSubclass(txtHwnd, OwnerTxtProc, 0, 0);
break;
}
//...some other codes...
default:
return DefWindowProc(hChild, message, wParam, lParam);
}
return 0;
}

text menu as created window behaviour of highlighting

I have created a Windows application. The elements that I create are using subclassing as I wanted to handle mouse hover events.
DWORD dwStyleOfIcons = SS_BITMAP | SS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER;
img1 = CreateWindow(L"STATIC", NULL, dwStyleOfIcons,
posX, posY, imgWt, imgHt,
hWnd, (HMENU)ICON1_CLICKED, hInst, NULL);
SetWindowSubclass(img1, StaticSubClassWndProc, ICON1_CLICKED, 0);
In my StaticSubClassWndProc(), I handle WM_MOUSEMOVE, WM_MOUSELEAVE, and WM_MOUSEHOVER:
LRESULT CALLBACK StaticSubClassWndProc (HWND hwndsubclass, UINT msg, WPARAM wp, LPARAM lp, UINT_PTR uidsubclass , DWORD_PTR dwrefdata)
{
...
switch(Msg)
{
case WM_MOUSEHOVER: {
if(uidsubclass == ICON1_CLICKED){
texture = "texture2.bmp";
modifyImage(texture);
}
break;
}
case WM_MOUSELEAVE: {
if(uidsubclass == ICON1_CLICKED){
texture = "texture.bmp";
modifyImage(texture);
}
break;
}
There are many STATIC items in my application, which all I wanted the behavior of a pop up context menu, like when I hover over the image it changes to a selected image, and when the cursor is out of view the image changes back to normal. I was able to do that.
I was able to do this for images which act as icons, but how do I do it for static text controls? Essentially, in a pop up menu, the selected text is all highlighted:
Is there no simpler way to make my elements in this window behave like a pop up menu? All I want is this custom structure of pop up menu behavior.
I think you did not handle the TrackMouseEvent function correctly, which caused your child window to be unable to process the WM_MOUSEHOVER and WM_MOUSELEAVE messages.
I tested the following code and it worked for me:
HBITMAP hBmp1 = (HBITMAP)LoadImage(NULL, L"test1.bmp", IMAGE_BITMAP, 200, 300, LR_LOADFROMFILE);
HBITMAP hBmp2 = (HBITMAP)LoadImage(NULL, L"test2.bmp", IMAGE_BITMAP, 200, 300, LR_LOADFROMFILE);
HWND img1;
LRESULT CALLBACK StaticSubClassWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, UINT_PTR uidsubclass, DWORD_PTR dwrefdata)
{
switch (msg)
{
case WM_MOUSEMOVE:
{
TRACKMOUSEEVENT lpEventTrack;
lpEventTrack.cbSize = sizeof(TRACKMOUSEEVENT);
lpEventTrack.dwFlags = TME_HOVER | TME_LEAVE;
lpEventTrack.hwndTrack = img1;
lpEventTrack.dwHoverTime = 100;
TrackMouseEvent(&lpEventTrack);
break;
}
case WM_MOUSEHOVER:
{
if (uidsubclass == ICON1_CLICKED) {
SendMessage(hwnd, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hBmp1);
}
break;
}
case WM_MOUSELEAVE:
{
if (uidsubclass == ICON1_CLICKED) {
SendMessage(hwnd, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hBmp2);
}
break;
}
default:
return DefSubclassProc(hwnd, msg, wParam, lParam);
}
}
But you need to be aware that WM_MOUSEHOVER and WM_MOUSELEAVE will trigger frequently, so I don't think you should use this method to load pictures when the mouse is hovered or left, which will frequently trigger the loading of pictures.

WM_LBUTTONDOWN and WM_KEYDOWN message are not receiving in Modeless dialogbox Win32

I'm developing a Win32 application to capture video using Windows Media Foundation. I have to display Play/Pause/Stop bitmap image on video window with transparent. I have created a modeless dialog box and shown the transparent image on the dialog using below code.
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmID;
switch (unMessage)
{
case WM_COMMAND:
switch(LOWORD(wParam))
{
case ID_BTN_VIDEOSTART:
InitModelessDialog();
break;
}
break;
}
return CallWindowProc ( g_lpfnWndProc, hWnd, unMessage, wParam, lParam );
}
void InitModelessDialog()
{
DIBSECTION ds;
DWORD dwFlag = MID_EXT_STYLES | MID_CLR_KEY | MID_ALPHA_VAL;
g_hWndVideoPause = CreateDialog(g_hInstance, MAKEINTRESOURCE(IDD_DLG_VIDEORECORD), g_hwndPreview, DlgProc_VideoPause);
g_hPauseBmp = (HBITMAP)LoadImage(g_hInstance, MAKEINTRESOURCE(IDB_BITMAP_PAUSE), IMAGE_BITMAP, 0, 0, 0);
GetObject(g_hPauseBmp, sizeof(ds), &ds);
g_hPauseDC = CreateCompatibleDC(NULL);
SelectObject(g_hPauseDC, g_hPauseBmp);
if(dwFlag & MID_EXT_STYLES)
SetWindowLong(g_hWndVideoPause, GWL_EXSTYLE, GetWindowLong(g_hWndVideoPause, GWL_EXSTYLE) | (WS_EX_LAYERED | WS_EX_TRANSPARENT));
if(dwFlag & (MID_CLR_KEY | MID_ALPHA_VAL))
{
DWORD dwTemp = 0;
if(dwFlag & MID_ALPHA_VAL)
dwTemp = LWA_ALPHA;
if(dwFlag & MID_CLR_KEY)
dwTemp |= LWA_COLORKEY;
SetLayeredWindowAttributes(g_hWndVideoPause, UI_COLOR_KEY, (255 * UI_TRANSPARENCY_PERCENT) / 100, dwTemp);
}
SetParent(g_hWndVideoPause,g_hwndPreview);
dlgWidth = ds.dsBm.bmWidth;
dlgHeight = ds.dsBm.bmHeight;
SetWindowPos(g_hWndVideoPause, HWND_TOP, 0, 0, ds.dsBm.bmWidth, ds.dsBm.bmHeight, SWP_SHOWWINDOW | SWP_NOMOVE);
HDC hDlgDC = GetDC(g_hWndVideoPause);
StretchBlt(hDlgDC, 0, 0, dlgWidth, dlgHeight, g_hPauseDC, 0, 0, ds.dsBm.bmWidth, ds.dsBm.bmHeight, SRCCOPY);
ReleaseDC(g_hWndVideoPause, hDlgDC);
if(g_hPauseDC)
{
DeleteDC(g_hPauseDC);
g_hPauseDC = NULL;
}
if(g_hPauseBmp)
{
DeleteObject(g_hPauseBmp);
g_hPauseBmp = NULL;
}
}
INT_PTR CALLBACK DlgProc_VideoPause(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
PAINTSTRUCT ps;
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_PAINT:
{
HDC hDC = GetDC(hDlg);
if(g_hPauseDC)
{
StretchBlt(hDC, 0, 0, dlgWidth, dlgHeight, g_hPauseDC, 0, 0, dlgWidth, dlgHeight, SRCCOPY);
}
ReleaseDC(hDlg, hDC);
}
break;
case WM_ERASEBKGND:
{
return 1;
}
break;
case WM_LBUTTONDOWN:
OutputDebugString(L"DlgProc_VideoPause WM_LBUTTONDOWN pressed\r\n");
break;
case WM_KEYDOWN:
OutputDebugString(L"DlgProc_VideoPause WM_KEYDOWN pressed\r\n");
break;
}
return (INT_PTR)FALSE;
}
When clicking on the drawn image, WM_KEYDOWN event is going to parent window. I wanna receive the WM_LBUTTONDOWN and WM_KEYDOWN event in my dialog proc window.What I have to do to receive this notification in my dialog proc?
I didn't create my dialog as Child window since I couldn't apply transparent for my child dialog.So only I'm creating a modeless dialog box.
Am I missing anything here?Please help me to solve this issue.
Thanks in advance.

Win32 API: How to catch escape key in Edit control?

I found that for some unknown reason when focus is in Edit control, the Escape key never produces a messages.
Below is a code to create a parent window, and Edit control above it. In MyCallBckProcedure() I laid printf() under a *WM_COMMAND*, to catch a messages, produced by Edit. More over -- I even tried to print all the messages catched in MyCallBckProcedure(); but if focus on Edit, the escape key never produces any of messages.
What a weird problem could be here?
#include <iostream>
#include <windows.h>
#include <stdio.h>
#define IDC_MAIN_EDIT 101
LRESULT __stdcall MyCallBckProcedure( HWND window, unsigned msg, WPARAM wp, LPARAM lp ){
printf("%x ",msg);
switch(msg){
case WM_COMMAND://here should be catched the escape key of Edit
printf("%x ",msg);
break;
case WM_KEYDOWN:
printf("%x ",msg);
if(wp == VK_ESCAPE)PostQuitMessage(0);
break;
case WM_DESTROY:
std::cout << "\ndestroying window\n" ;
PostQuitMessage(0);
return 0;
default:
return DefWindowProc( window, msg, wp, lp ) ;
break;
case WM_SIZE:{
HWND hEdit;
RECT rcClient;
GetClientRect(window, &rcClient);
hEdit = GetDlgItem(window, IDC_MAIN_EDIT);
SetWindowPos(hEdit, NULL, 0, 0, rcClient.right, rcClient.bottom, SWP_NOZORDER);
}
break;
}
}
int main(){
const char* const myclass = "myclass";
WNDCLASSEX wndclass = { sizeof(WNDCLASSEX), CS_DBLCLKS, MyCallBckProcedure,
0, 0, GetModuleHandle(0), LoadIcon(0,IDI_APPLICATION),
LoadCursor(0,IDC_ARROW), HBRUSH(COLOR_WINDOW+1),
0, myclass, LoadIcon(0,IDI_APPLICATION) };
if(RegisterClassEx(&wndclass)<0){
printf("ERR: in registering window class\n");
return 1;
}
//Creating window
HWND window = CreateWindowEx( 0, myclass, "title",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, GetModuleHandle(0), 0 );
if(!window){
printf("ERR: in creating window\n");
return 1;
}
ShowWindow( window, SW_SHOWDEFAULT );
MSG msg;
//creating TextBox on the window
HFONT hfDefault;
HWND hEdit;
hEdit = CreateWindowEx(0, "edit", "",
WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_HSCROLL | ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
window, (HMENU)IDC_MAIN_EDIT, GetModuleHandle(NULL), NULL);
if(hEdit == NULL){
MessageBox(window, "Could not create edit box.", "Error", MB_OK | MB_ICONERROR);
return 1;
}
hfDefault = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
SendMessage(hEdit, WM_SETFONT, (WPARAM)hfDefault, MAKELPARAM(FALSE, 0));
//Now resize TextBox to fill whole parent window
RECT RectSize;
GetClientRect(window,&RectSize);
hEdit = GetDlgItem(window,IDC_MAIN_EDIT);
SetWindowPos(hEdit, 0,0,0,RectSize.right,RectSize.bottom,SWP_NOZORDER);
//Process messages
while(GetMessage(&msg,0,0,0) ){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
Pressing ESC while the Edit is in focus does not generate a WM_COMMAND message to the Edit's parent window. It generates WM_KEYDOWN, WM_KEYUP, WM_CHAR and WM_UNICHAR messages to the Edit window itself.
Update: You are only handling messages that are destined for the parent window. You need to assign a message procedure to the edit window instead, eg:
WNDPROC lpEditWndProc;
LRESULT CALLBACK MyEditCallBckProcedure(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if( (uMsg == WM_CHAR) && (wParam == VK_ESCAPE) )
{
PostQuitMessage(0);
return 0;
}
return CallWindowProc(lpEditWndProc, hWnd, uMsg, wParam, lParam);
}
...
HWND hEdit = CreateWindowEx(...);
#ifdef _WIN64
lpEditWndProc = (WNDPROC) SetWindowLongPtr(hEdit, GWLP_WNDPROC, (LONG_PTR)&MyEditCallBckProcedure);
#else
lpEditWndProc = (WNDPROC) SetWindowLongPtr(hEdit, GWL_WNDPROC, (LONG_PTR)&MyEditCallBckProcedure);
#endif
Alternatively:
LRESULT CALLBACK MyEditCallBckProcedure(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
if( (uMsg == WM_CHAR) && (wParam == VK_ESCAPE) )
{
PostQuitMessage(0);
return 0;
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
...
HWND hEdit = CreateWindowEx(...);
SetWindowSubclass(hEdit, &MyEditCallBckProcedure, 0, 0);

coloring the static control in select-for-folder dialog that is without the WM_CTLCOLORSTATIC which this dialog window doesn't receive

I managed to draw a rectangle in this control but there are two problems. First is that the white rectangle is shown after moving the select folder dialog around with a mouse. I know that the problem here is to hook up to the right message. I've chosen WM_ERASEBKGND that is fired when dialog window is created but then it has no effect, it must be called when portion of the control not shown before is back on screen, so I must drag the window to the edge so part of the control is not visible and drag it back, then the white rectangle is shown. But there second problem emerges. It also covers text of the control.
So here's my try, any ideas ?
#include <windows.h>
#include <shlobj.h>
WNDPROC origStaticProc;
LRESULT CALLBACK myStaticProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
switch (uMsg) {
case WM_ERASEBKGND: {
HDC dc = GetDC(hWnd);
RECT clientRect;
GetClientRect(hWnd,&clientRect);
FillRect(dc, &clientRect, (HBRUSH)GetStockObject(WHITE_BRUSH));
ReleaseDC(hWnd, dc);
break;
}
}
return CallWindowProc(origStaticProc, hWnd, uMsg, wParam, lParam );
}
int CALLBACK BrowseCallBackProc( HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData) {
switch(uMsg) {
case BFFM_INITIALIZED: {
HWND static_control = NULL;
char szClassName[_MAX_PATH];
for (HWND hChild = GetWindow(hwnd, GW_CHILD); hChild != NULL; hChild = GetNextWindow(hChild, GW_HWNDNEXT))
{
if ((GetWindowLong(hChild, GWL_STYLE) & WS_VISIBLE) == 0) continue;
GetClassName(hChild, szClassName, _countof(szClassName));
if (!strcmp("Static",szClassName)) {
static_control = hChild;
break;
}
}
HFONT hFont = CreateFont (13, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, TEXT("Fixedsys"));
SendMessage(static_control, WM_SETFONT, (WPARAM)hFont, TRUE);
origStaticProc = ( WNDPROC ) SetWindowLongW( static_control, GWL_WNDPROC,( LONG ) myStaticProc );
break;
}
}
}
int main() {
using namespace std;
BROWSEINFOW bi;
LPITEMIDLIST pidl;
LPMALLOC pMalloc;
if (SUCCEEDED (::SHGetMalloc (&pMalloc))) {
::ZeroMemory (&bi,sizeof(bi));
bi.hwndOwner = NULL;
bi.lpszTitle = L"I should be visible on a white background. Now you must drag me to edge of the screen and back.";
bi.pszDisplayName = 0;
bi.pidlRoot = 0;
bi.ulFlags = BIF_NEWDIALOGSTYLE | BIF_VALIDATE | BIF_USENEWUI | BIF_UAHINT;
bi.lpfn = BrowseCallBackProc;
bi.lParam = (LPARAM)L"d:\\";
pidl = ::SHBrowseForFolderW(&bi);
}
}
how it looks like:
how it of course should be:
Got it. The problem is that I didn't know that the callback function used for SHBrowseForFolder is different than the callback for window itself. It came out I must do another function to subclass the dialog.
It's UGLY and unsafe code but it compiles and illustrates the principle.
#include <windows.h>
#include <shlobj.h>
WNDPROC origBffProc;
LRESULT CALLBACK bffProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) {
static HBRUSH hBrush = (HBRUSH)GetStockObject(HOLLOW_BRUSH);
switch (uMsg) {
case WM_CTLCOLORSTATIC: {
HDC hdcStatic = (HDC) wParam;
SetTextColor(hdcStatic, RGB(255,0,0));
SetBkColor(hdcStatic, RGB(255,255,0));
return (INT_PTR)hBrush;
}
}
return CallWindowProc(origBffProc, hWnd, uMsg, wParam, lParam );
}
int CALLBACK BrowseCallBackProc( HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData) {
switch(uMsg) {
case BFFM_INITIALIZED: {
HWND static_control = NULL;
char szClassName[_MAX_PATH];
for (HWND hChild = GetWindow(hwnd, GW_CHILD); hChild != NULL; hChild = GetNextWindow(hChild, GW_HWNDNEXT))
{
if ((GetWindowLong(hChild, GWL_STYLE) & WS_VISIBLE) == 0) continue;
GetClassName(hChild, szClassName, _countof(szClassName));
if (!strcmp("Static",szClassName)) {
static_control = hChild;
break;
}
}
HFONT hFont = CreateFont (13, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, TEXT("Fixedsys"));
PostMessage(static_control, WM_SETFONT, (WPARAM)hFont, TRUE);
origBffProc = ( WNDPROC ) SetWindowLongW( hwnd, GWL_WNDPROC,( LONG ) bffProc );
break;
}
}
}
int main() {
using namespace std;
BROWSEINFOW bi;
LPITEMIDLIST pidl;
LPMALLOC pMalloc;
if (SUCCEEDED (::SHGetMalloc (&pMalloc))) {
::ZeroMemory (&bi,sizeof(bi));
bi.hwndOwner = NULL;
bi.lpszTitle = L"You are about to erase a directory, be careful! (and don't blame me)";
bi.pszDisplayName = 0;
bi.pidlRoot = 0;
bi.ulFlags = BIF_NEWDIALOGSTYLE | BIF_VALIDATE | BIF_USENEWUI | BIF_UAHINT;
bi.lpfn = BrowseCallBackProc;
bi.lParam = (LPARAM)L"d:\\";
pidl = ::SHBrowseForFolderW(&bi);
}
}