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;
}
Related
I have a subclassed button that I am trying to highlight when the mouse cursor is over it. However, I cannot seem to get the TrackMouseEvent() function to work properly. Here is the code that creates the subclass:
hBtn = CreateWindow(L"button", L"", WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, 0, 0, 0, 0, hWnd, HMENU(400), hInst, NULL);
SetWindowSubclass(hBtn[0], subSIproc, 400, 0);
Here is the subclass procedure:
LRESULT CALLBACK subSIproc(HWND hButton, UINT iMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uId, DWORD_PTR dwRefData){
int HIflag=0;
switch(iMsg)
{
case WM_MOUSEMOVE:
{
TRACKMOUSEEVENT me{};
me.cbSize = sizeof(TRACKMOUSEEVENT);
me.dwFlags = TME_HOVER | TME_LEAVE;
me.hwndTrack = hButton;
me.dwHoverTime = HOVER_DEFAULT;
TrackMouseEvent(&me);
HIflag = 1;
RedrawWindow(hButton, NULL, NULL, RDW_INVALIDATE);
}
case WM_MOUSEHOVER:
{
HIflag = 2;
RedrawWindow(hButton, NULL, NULL, RDW_INVALIDATE);
}
case WM_MOUSELEAVE:
{
TRACKMOUSEEVENT me{};
me.cbSize = sizeof(TRACKMOUSEEVENT);
me.dwFlags = TME_HOVER | TME_LEAVE | TME_CANCEL;
me.hwndTrack = hButton;
me.dwHoverTime = HOVER_DEFAULT;
TrackMouseEvent(&me);
HIflag = 3;
RedrawWindow(hButton, NULL, NULL, RDW_INVALIDATE);
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hButton, &ps);
if(HIflag==0) FillRect(hdc, &ps.rcPaint, CreateSolidBrush(0x007F7F7F));
if(HIflag==1) FillRect(hdc, &ps.rcPaint, CreateSolidBrush(0x00FF0000));
if(HIflag==2) FillRect(hdc, &ps.rcPaint, CreateSolidBrush(0x000000FF));
if(HIflag==3) FillRect(hdc, &ps.rcPaint, CreateSolidBrush(0x0000FF00));
EndPaint(hButton, &ps);
}
}
return DefSubclassProc(hButton, iMsg, wParam, lParam);
}
The variable "HIflag" has four possible values, to determine which messages are being received, and when---'0' (gray) for no messages yet received; '1' (blue) for WM_MOUSEMOVE received; '2' (red) for WM_MOUSEHOVER received; and '3' for WM_MOUSELEAVE received.
Here is what is happening: When I initially run the program, the button is gray (no mouse messages received). The button remains gray, until I move the cursor onto the button. At that point, it turns green (indicating "WM_MOUSELEAVE"). It should turn red (for "WM_MOUSEHOVER") and not turn green until I move the cursor away from the button. The button now remains green, no matter where I move the cursor.
Does anyone know what I am doing wrong?
Your case blocks are missing breaks, so when WM_MOUSEMOVE is received then the code will fall through to the code for WM_MOUSEHOVER, which will then fall through to the code for WM_MOUSELEAVE, which will then fall through to the code for WM_PAINT, etc. So HIflag will always be 3 when any fall through to WM_PAINT occurs.
You are also leaking the HBRUSH that CreateSolidBrush() returns. You need to DeleteObject() the brush when you are done using it.
But most importantly, HIflag is a local variable inside of subSIproc(), so it will always be 0 when WM_PAINT is issued by the OS itself, rather than when it is reached as the result of fall through.
You need to store HIflag outside of subSIproc() on a per-button basis, such as inside the HWND itself using (Get|Set)WindowLongPtr(GWL_USERDATA), (Get|Set)Prop(), etc.
Try something more like this instead:
hBtn = CreateWindow(L"button", L"", WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, 0, 0, 0, 0, hWnd, HMENU(400), hInst, NULL);
SetProp(hBtn, TEXT("HIflag"), (HANDLE)0);
SetWindowSubclass(hBtn, subSIproc, 400, 0);
...
LRESULT CALLBACK subSIproc(HWND hButton, UINT iMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uId, DWORD_PTR dwRefData)
{
switch (iMsg)
{
case WM_NCDESTROY:
{
RemoveProp(hButton, TEXT("HIflag"));
break;
}
case WM_MOUSEMOVE:
{
TRACKMOUSEEVENT me{};
me.cbSize = sizeof(TRACKMOUSEEVENT);
me.dwFlags = TME_HOVER | TME_LEAVE;
me.hwndTrack = hButton;
me.dwHoverTime = HOVER_DEFAULT;
TrackMouseEvent(&me);
SetProp(hButton, TEXT("HIflag"), (HANDLE)1);
InvalidateRect(hButton, NULL, TRUE);
break;
}
case WM_MOUSEHOVER:
{
SetProp(hButton, TEXT("HIflag"), (HANDLE)2);
InvalidateRect(hButton, NULL, TRUE);
break;
}
case WM_MOUSELEAVE:
{
// tracking is cancelled automatically when this message is generated!
SetProp(hButton, TEXT("HIflag"), (HANDLE)3);
InvalidateRect(hButton, NULL, TRUE);
break;
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hButton, &ps);
COLORREF color;
switch ( (int) GetProp(hButton, TEXT("HIflag")) ) {
case 1: color = RGB(0x00, 0x00, 0xFF); break;
case 2: color = RGB(0xFF, 0x00, 0x00); break;
case 3: color = RGB(0x00, 0xFF, 0x00); break;
default: color = RGB(0x7F, 0x7F, 0x7F); break;
}
HBRUSH hBrush = CreateSolidBrush(color);
FillRect(hdc, &ps.rcPaint, hBrush);
DeleteObject(hBrush);
EndPaint(hButton, &ps);
return 0;
}
}
return DefSubclassProc(hButton, iMsg, wParam, lParam);
}
With that said, another way to color an owner-drawn button's background is to have the parent window handle the WM_CTLCOLORBTN message, or at least the WM_DRAWITEM message. This way, the parent window can store the button's HWND and its associated HIFlag together in an array somewhere, and not have to track the flag inside the HWND itself.
For example:
Buttons[index].hWnd = CreateWindow(L"button", L"", WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, 0, 0, 0, 0, hWnd, HMENU(index+1), hInst, NULL);
Buttons[index].HIflag = 0;
SetWindowSubclass(hBtn, subSIproc, index+1, 0);
...
LRESULT CALLBACK ParentWndProc(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
switch (uiMsg)
{
case WM_CTLCOLORBTN:
{
HWND hBtn = (HWND) lParam;
for (int index = 0; index < NumberOfButtons; ++index)
{
if (Buttons[index].hWnd == hBtn)
{
COLORREF color;
switch ( Buttons[index].HIflag ) {
case 1: color = RGB(0x00, 0x00, 0xFF); break;
case 2: color = RGB(0xFF, 0x00, 0x00); break;
case 3: color = RGB(0x00, 0xFF, 0x00); break;
default: color = RGB(0x7F, 0x7F, 0x7F); break;
}
return CreateSolidBrush(color);
}
}
break;
}
// or:
case WM_DRAWITEM:
{
DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT*) lParam;
COLORREF color;
switch ( Buttons[dis->CtlID-1].HIflag ) {
case 1: color = RGB(0x00, 0x00, 0xFF); break;
case 2: color = RGB(0xFF, 0x00, 0x00); break;
case 3: color = RGB(0x00, 0xFF, 0x00); break;
default: color = RGB(0x7F, 0x7F, 0x7F); break;
}
HBRUSH hBrush = CreateSolidBrush(color);
FillRect(dis->hDC, &(dis->rcItem), hBrush);
DeleteObject(hBrush);
return TRUE;
}
}
return DefWindowProc(hWnd, uiMsg, wParam, lParam);
}
LRESULT CALLBACK subSIproc(HWND hButton, UINT uiMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uId, DWORD_PTR dwRefData)
{
switch (uiMsg)
{
case WM_MOUSEMOVE:
{
TRACKMOUSEEVENT me{};
me.cbSize = sizeof(TRACKMOUSEEVENT);
me.dwFlags = TME_HOVER | TME_LEAVE;
me.hwndTrack = hButton;
me.dwHoverTime = HOVER_DEFAULT;
TrackMouseEvent(&me);
Buttons[uId-1].HIflag = 1;
InvalidateRect(hButton, NULL, TRUE);
break;
}
case WM_MOUSEHOVER:
{
Buttons[uId-1].HIflag = 2;
InvalidateRect(hButton, NULL, TRUE);
break;
}
case WM_MOUSELEAVE:
{
// tracking is cancelled automatically when this message is generated!
Buttons[uId-1].HIflag = 3;
InvalidateRect(hButton, NULL, TRUE);
break;
}
}
return DefSubclassProc(hButton, uiMsg, wParam, lParam);
}
I am using win32 API to develop an Application.I am using WM_LBUTTONDOWN message to handle two operation .
If a click on button X I want to perform operation A.
a)If a double click occur on button X I want to perform operation C as well
that is operation A
followed by operation C.
But while implementing I can either perform operation Aor operation C.
case WM_LBUTTONDOWN:
{
overallClicks++;
left1.x = LOWORD(lParam);
left1.y = HIWORD(lParam);
wsprintf(waCoord, _T("my coordinates are (%i,%i)"), left1.x, left1.y);
if (click_count == 0)
{
SetTimer(hWnd, TIMER_ID, GetDoubleClickTime(), NULL);
}
click_count++;
if (fun()) {
//do something
}
else {
MessageBox(NULL, _T("yo"), _T("yo"), MB_OK);
}
return 0;
// dwLastClickTime= GetMessageTime();
//SetTimer(hWnd,0,GetDoubleClickTime(),0);
//break;
// handleDoubleClicks(hWnd, message, wParam, lParam, ptLastClickPos, dwLastClickTime);
}
case WM_TIMER:
{
HandleTimer(hWnd, message, wParam, lParam);
}
LRESULT HandleTimer(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
BOOL i = KillTimer(hWnd, TIMER_ID);
if (click_count == 1)
{
// wchar_t* ptr = ;
MessageBox(hWnd, waCoord, _T("Left mouse button click"), MB_OK);
}
else if (click_count == 2)
{
MessageBox(hWnd, waCoord, TEXT("I appear when double clicked"), MB_OKCANCEL);
}
else if (click_count == 3)
{
MessageBox(hWnd, waCoord, TEXT("I appear when triple clicked"), MB_OKCANCEL);
}
else if (click_count > 3) {
MessageBox(hWnd, waCoord, TEXT("I appear when rage clicked"), MB_OKCANCEL);
}
click_count = 0;
return 0;
}
I would like elaborate for example I am double clicking on disabled button I want to show pop up that I have double clicked as well as that button is disabled.
With the help of an answer from your previous case.
Because your code logic uses if...else... conditional statements. The way I can think of is to add an additional timer in the case of double-click to complete the action when clicked.
void CALLBACK f(HWND hwnd, UINT uMsg, UINT timerId, DWORD dwTime)
{
EnableWindow(hwndButton, 0);
}
...
LRESULT CALLBACK OwnerDrawButtonProc(HWND hWnd, UINT uMsg, WPARAM wParam,
LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
switch (uMsg)
{
case WM_TIMER:
{
KillTimer(hWnd, TIMER_ID);
if (click_count == 1)
{
wchar_t waCoord[20];
wsprintf(waCoord, _T("(%i,%i)"), point.x, point.y);
MessageBox(hWnd, waCoord, _T("Left mouse button click"), MB_OK);
SetTimer(NULL, 0, 100, (TIMERPROC)&f);
}
else if (click_count == 2)
{
SetTimer(NULL, 0, 100, (TIMERPROC)&f);
MessageBox(hWnd, TEXT("Double click"), TEXT("I appear when double clicked"), MB_OKCANCEL);
}
click_count = 0;
return 0;
}
break;
case WM_LBUTTONDOWN:
{
if (click_count == 0)
{
SetTimer(hWnd, TIMER_ID, GetDoubleClickTime(), NULL);
}
click_count++;
return 0;
}
break;
case WM_NCDESTROY:
RemoveWindowSubclass(hWnd, &OwnerDrawButtonProc, 1);
break;
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
Remove the CS_DBLCLKS style when using the button control.
Updated:
Because the button is disabled, I'm sorry I didn't see this. In this way, the message of the parent window can only be used to determine whether the mouse coordinates are within the range of the button. If it is, double-click to perform operation C.
Only need to modify part of the code of the parent window:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static RECT rc;
static POINT pt = { 0 };
rc.left = 10;
rc.top = 10;
rc.right = 110;
rc.bottom = 110;
switch (message)
{
case WM_CREATE:
{
hwndButton = CreateWindow(
L"BUTTON", // Predefined class; Unicode assumed
L"OK", // Button text
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON , // Styles
10, // x position
10, // y position
100, // Button width
100, // Button height
hWnd, // Parent window
NULL, // No menu.
(HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE),
NULL); // Pointer not needed.
DWORD class_style = GetClassLongPtr(hwndButton, GCL_STYLE);
SetClassLongPtr(hwndButton, GCL_STYLE, class_style & (~CS_DBLCLKS));
SetWindowSubclass(hwndButton, &OwnerDrawButtonProc, IDC_OWNERDRAWBUTTON, 0);
return TRUE;
}
case WM_TIMER:
{
KillTimer(hWnd, TIMER_ID);
if (click_count == 2)
{
MessageBox(hWnd, TEXT("Double click"), TEXT("I appear when double clicked"), MB_OKCANCEL);
}
click_count = 0;
return 0;
}
case WM_LBUTTONDOWN:
{
POINT p;
p.x = GET_X_LPARAM(lParam);
p.y = GET_Y_LPARAM(lParam);
if (PtInRect(&rc, p))
{
pt = p;
if (click_count == 0)
{
SetTimer(hWnd, TIMER_ID, GetDoubleClickTime(), NULL);
}
click_count++;
}
return 0;
}
...
Debug:
I'm going to risk being shot down for being an idiot but I've sunk hours into trying to follow this windows tutorial with no luck: Custom Window Frame Using DWM
My main goal is to create a custom window frame that behaves in the same was as the default one (for example, can be maximised by being dragged to the top of the screen, has the normal minimise, maximise, exit buttons) but one that sports a different colour and perhaps some menu items within the frame. I'm fairly comfortable using wxWidgets but I am an absolute beginner (if even a beginner) using the Windows API. Unfortunately I've been led here anyway because it looks like the only way to achieve what I'm after.
Going back to the abovementioned linked tutorial, I've able to extend the frame using DwmExtendFrameIntoClientArea. This yields a result that looks like this on Windows 10 using a grey brush:
My first point of confusion happens when trying to follow the instructions under "Removing the Standard Frame". This is what I get when I try and follow the code example:
This looks similar enough to the example image in the tutorial (below) but all of the window functionality is now gone! I.e. clicking any of the top-right three buttons doesn't nothing and the window cannot be clicked, moved or resized.
I had hoped that this functionality would return once I'd added in the rest of the example code, which I won't reproduce as they are contained in the page I linked under Appendix B and C (titled 'Painting the Caption Title' and 'HitTestNCA Function' respectively).
Not only did the functionality not return, the example code didn't seem to do anything... and I ended up with the window as it was after my previous edit (pictured below - to be clear):
I haven't reproduced by code here because it's exactly the same as the code found in the link except the background colour is changed to grey and I've added a static 'Test widget' to give myself a point of reference for what the coordinates were doing.
If any kind soul could please tell me what I am doing wrong, or whether my goals are even achievable using the methods I've reluctantly chosen, I would greatly appreciate some advice.
Many thanks!
I created a project according to the documentation and did not reproduce this issue, but you can compare it with your project:
#include <windows.h>
#include <stdio.h>
#include <uxtheme.h>
#include <dwmapi.h>
#include <vssym32.h>
#include <windowsx.h>
#pragma comment(lib, "dwmapi.lib")
#pragma comment(lib, "uxtheme.lib")
#define RECTWIDTH(rc) (rc.right - rc.left)
#define RECTHEIGHT(rc) (rc.bottom - rc.top)
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT AppWinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
LRESULT CustomCaptionProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool* pfCallDWP);
void PaintCustomCaption(HWND hWnd, HDC hdc);
LRESULT HitTestNCA(HWND hWnd, WPARAM wParam, LPARAM lParam);
HWND createmainwindow()
{
WNDCLASSEXW wcex = { 0 };
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = GetModuleHandle(NULL);
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
wcex.lpszClassName = L"My_Class";
RegisterClassExW(&wcex);
HWND hWnd = CreateWindowW(wcex.lpszClassName, L"Test", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, wcex.hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
HWND staticctrl = CreateWindowW(L"STATIC", L"SETTINGS", SS_LEFT | WS_CHILD,
8, 27, 500, 300, hWnd, NULL, wcex.hInstance, NULL);
if (!staticctrl)
{
return FALSE;
}
ShowWindow(hWnd, SW_NORMAL);
UpdateWindow(hWnd);
ShowWindow(staticctrl, SW_NORMAL);
UpdateWindow(staticctrl);
}
void main()
{
HWND hWnd = createmainwindow();
MSG msg;
// Main message loop:
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, 0, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
bool fCallDWP = true;
BOOL fDwmEnabled = FALSE;
LRESULT lRet = 0;
HRESULT hr = S_OK;
// Winproc worker for custom frame issues.
hr = DwmIsCompositionEnabled(&fDwmEnabled);
if (SUCCEEDED(hr))
{
lRet = CustomCaptionProc(hWnd, message, wParam, lParam, &fCallDWP);
}
// Winproc worker for the rest of the application.
if (fCallDWP)
{
lRet = AppWinProc(hWnd, message, wParam, lParam);
}
return lRet;
}
//
// Message handler for the application.
//
LRESULT AppWinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
HRESULT hr;
LRESULT result = 0;
switch (message)
{
case WM_CREATE:
{}
break;
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
{
hdc = BeginPaint(hWnd, &ps);
PaintCustomCaption(hWnd, hdc);
// Add any drawing code here...
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// Message handler for handling the custom caption messages.
//
LRESULT CustomCaptionProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool* pfCallDWP)
{
LRESULT lRet = 0;
HRESULT hr = S_OK;
bool fCallDWP = true; // Pass on to DefWindowProc?
fCallDWP = !DwmDefWindowProc(hWnd, message, wParam, lParam, &lRet);
// Handle window creation.
if (message == WM_CREATE)
{
RECT rcClient;
GetWindowRect(hWnd, &rcClient);
// Inform application of the frame change.
SetWindowPos(hWnd,
NULL,
rcClient.left, rcClient.top,
RECTWIDTH(rcClient), RECTHEIGHT(rcClient),
SWP_FRAMECHANGED);
fCallDWP = true;
lRet = 0;
}
// Handle window activation.
if (message == WM_ACTIVATE)
{
// Extend the frame into the client area.
MARGINS margins;
margins.cxLeftWidth = 8; // 8
margins.cxRightWidth = 8; // 8
margins.cyBottomHeight = 20; // 20
margins.cyTopHeight = 27; // 27
hr = DwmExtendFrameIntoClientArea(hWnd, &margins);
if (!SUCCEEDED(hr))
{
// Handle error.
}
fCallDWP = true;
lRet = 0;
}
if (message == WM_PAINT)
{
HDC hdc;
{
PAINTSTRUCT ps;
hdc = BeginPaint(hWnd, &ps);
PaintCustomCaption(hWnd, hdc);
EndPaint(hWnd, &ps);
}
fCallDWP = true;
lRet = 0;
}
// Handle the non-client size message.
if ((message == WM_NCCALCSIZE) && (wParam == TRUE))
{
// Calculate new NCCALCSIZE_PARAMS based on custom NCA inset.
NCCALCSIZE_PARAMS* pncsp = reinterpret_cast<NCCALCSIZE_PARAMS*>(lParam);
pncsp->rgrc[0].left = pncsp->rgrc[0].left + 0;
pncsp->rgrc[0].top = pncsp->rgrc[0].top + 0;
pncsp->rgrc[0].right = pncsp->rgrc[0].right - 0;
pncsp->rgrc[0].bottom = pncsp->rgrc[0].bottom - 0;
lRet = 0;
// No need to pass the message on to the DefWindowProc.
fCallDWP = false;
}
//Handle hit testing in the NCA if not handled by DwmDefWindowProc.
if ((message == WM_NCHITTEST) && (lRet == 0))
{
lRet = HitTestNCA(hWnd, wParam, lParam);
if (lRet != HTNOWHERE)
{
fCallDWP = false;
}
}
*pfCallDWP = fCallDWP;
return lRet;
}
// Paint the title on the custom frame.
void PaintCustomCaption(HWND hWnd, HDC hdc)
{
RECT rcClient;
GetClientRect(hWnd, &rcClient);
HTHEME hTheme = OpenThemeData(NULL, L"CompositedWindow::Window");
if (hTheme)
{
HDC hdcPaint = CreateCompatibleDC(hdc);
if (hdcPaint)
{
int cx = RECTWIDTH(rcClient);
int cy = RECTHEIGHT(rcClient);
// Define the BITMAPINFO structure used to draw text.
// Note that biHeight is negative. This is done because
// DrawThemeTextEx() needs the bitmap to be in top-to-bottom
// order.
BITMAPINFO dib = { 0 };
dib.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
dib.bmiHeader.biWidth = cx;
dib.bmiHeader.biHeight = -cy;
dib.bmiHeader.biPlanes = 1;
dib.bmiHeader.biBitCount = 32;
dib.bmiHeader.biCompression = BI_RGB;
HBITMAP hbm = CreateDIBSection(hdc, &dib, DIB_RGB_COLORS, NULL, NULL, 0);
if (hbm)
{
HBITMAP hbmOld = (HBITMAP)SelectObject(hdcPaint, hbm);
// Setup the theme drawing options.
DTTOPTS DttOpts = { sizeof(DTTOPTS) };
DttOpts.dwFlags = DTT_COMPOSITED | DTT_GLOWSIZE;
DttOpts.iGlowSize = 15;
// Select a font.
LOGFONT lgFont;
HFONT hFontOld = NULL;
if (SUCCEEDED(GetThemeSysFont(hTheme, TMT_CAPTIONFONT, &lgFont)))
{
HFONT hFont = CreateFontIndirect(&lgFont);
hFontOld = (HFONT)SelectObject(hdcPaint, hFont);
}
// Draw the title.
RECT rcPaint = rcClient;
rcPaint.top += 8;
rcPaint.right -= 125;
rcPaint.left += 8;
rcPaint.bottom = 50;
DrawThemeTextEx(hTheme,
hdcPaint,
0, 0,
L"Test",
-1,
DT_LEFT | DT_WORD_ELLIPSIS,
&rcPaint,
&DttOpts);
// Blit text to the frame.
BitBlt(hdc, 0, 0, cx, cy, hdcPaint, 0, 0, SRCCOPY);
SelectObject(hdcPaint, hbmOld);
if (hFontOld)
{
SelectObject(hdcPaint, hFontOld);
}
DeleteObject(hbm);
}
DeleteDC(hdcPaint);
}
CloseThemeData(hTheme);
}
}
// Hit test the frame for resizing and moving.
LRESULT HitTestNCA(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
// Get the point coordinates for the hit test.
POINT ptMouse = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
// Get the window rectangle.
RECT rcWindow;
GetWindowRect(hWnd, &rcWindow);
// Get the frame rectangle, adjusted for the style without a caption.
RECT rcFrame = { 0 };
AdjustWindowRectEx(&rcFrame, WS_OVERLAPPEDWINDOW & ~WS_CAPTION, FALSE, NULL);
// Determine if the hit test is for resizing. Default middle (1,1).
USHORT uRow = 1;
USHORT uCol = 1;
bool fOnResizeBorder = false;
// Determine if the point is at the top or bottom of the window.
if (ptMouse.y >= rcWindow.top && ptMouse.y < rcWindow.top + 27)
{
fOnResizeBorder = (ptMouse.y < (rcWindow.top - rcFrame.top));
uRow = 0;
}
else if (ptMouse.y < rcWindow.bottom && ptMouse.y >= rcWindow.bottom - 20)
{
uRow = 2;
}
// Determine if the point is at the left or right of the window.
if (ptMouse.x >= rcWindow.left && ptMouse.x < rcWindow.left + 8)
{
uCol = 0; // left side
}
else if (ptMouse.x < rcWindow.right && ptMouse.x >= rcWindow.right - 8)
{
uCol = 2; // right side
}
// Hit test (HTTOPLEFT, ... HTBOTTOMRIGHT)
LRESULT hitTests[3][3] =
{
{ HTTOPLEFT, fOnResizeBorder ? HTTOP : HTCAPTION, HTTOPRIGHT },
{ HTLEFT, HTNOWHERE, HTRIGHT },
{ HTBOTTOMLEFT, HTBOTTOM, HTBOTTOMRIGHT },
};
return hitTests[uRow][uCol];
}
Then,
the window cannot be clicked, moved or resized.
We cannot expect that without implementing logic to handle caption button hit testing and frame resizing/moving.
You will get the black of the background color default(BITMAPINFO dib = { 0 };). But the screen shot was still gray. (This may be because the function didn't work (failed?). Or, when you test the code before, you've commented B, C , and then when you add B, C, didn't to uncomment it).
So basically I have this program that makes a transparent overlay for a game so I can make a kill counter. However, when I click on the game with the overlay on, nothing happens. I managed to make it so when you click on it then it sends a message to the game telling it to shoot, however, when I tried the same for moving my characters head it was just laggy and snappy. When I would move my head quickly, my cursor would also fly out of the game window. How can I fix this so when I play the game it would be like its not even there.
I have tried sending the message and setting the window active AND using setcapture. However, none of these worked. I have tried looking at other places but they didn't work either.
/* This is my while(true) loop: */
while (TRUE)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
{
break;
}
TranslateMessage(&msg); // translates virtual-key messages into character messages
DispatchMessage(&msg); // dispatches a message to WindowProc
}
if (gameProcId == 0)
{
gameProcId = GetProcId(L"ac_client.exe");
}
if (gameWnd == NULL)
{
gameWnd = FindWindow(NULL, L"AssaultCube");
}
if ((gameProc == NULL) && (gameProcId != 0))
{
gameProc = OpenProcess(PROCESS_VM_READ, false, gameProcId); // opens an existing local process and returns a handle to it
}
if (gameProc != NULL)
{
if ((!init_ok) || ((loops % 20) == 0))
{
RECT client_rect;
#pragma warning (suppress: 6387)
GetClientRect(gameWnd, &client_rect); // gets a windows coordinates, upper-left corner is (0,0)
w_res.X = client_rect.right;
w_res.Y = client_rect.bottom;
RECT bounding_rect;
#pragma warning (suppress: 6387)
GetWindowRect(gameWnd, &bounding_rect); // gets dimensions of a window
if (!init_ok)
{
if ((w_pos.X != bounding_rect.left) || (w_pos.Y != bounding_rect.top))
{
MoveWindow(hWnd, bounding_rect.left, bounding_rect.top,
client_rect.right, client_rect.bottom, false);
w_pos.X = bounding_rect.left;
w_pos.Y = bounding_rect.top;
}
//SetCursorPos(w_pos.X * 4, w_pos.Y * 4);
//ClipCursor(&gameRect);
}
else
{
if ((bounding_rect.left == 0) && (bounding_rect.top == 0))
{
MoveWindow(hWnd, bounding_rect.left, bounding_rect.top, // changes both the position and dimension of a window
client_rect.right, client_rect.bottom, false);
}
MoveWindow(hWnd, bounding_rect.left, bounding_rect.top, client_rect.right,
client_rect.bottom, false);
}
init_ok = true;
}
}
if (loops % 10 == 0)
{
if (FindWindow(NULL, L"AssaultCube") == NULL)
{
SendMessage(hWnd, WM_CLOSE, NULL, NULL); // calls WindowProc() and sends the message to a window
}
}
loops++;
if (loops > 100) loops = 0;
Render();
}
}
/* This is my WindowProc() function: */
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}break;
case WM_LBUTTONDOWN:
{
PostMessage(gameWnd, message, wParam, lParam);
return 0;
}
case WM_MOUSEMOVE:
{
SendMessage(gameWnd, message, wParam, lParam);
return 0;
}
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
You didn't post enough code to attempt to solve this problem, we don't see your call to CreateWindowEx().
Here is a working solution for a win32 overlay:
#include <iostream>
#include <windows.h>
using namespace std;
//global forward declerations
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void cleanUpObjects(HPEN pen);
int CALLBACK WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
RECT overlayWindowRect;
RECT gameWindowRect;
HWND gameWindowHandle;
gameWindowHandle = FindWindowA(0, "AssaultCube");
GetWindowRect(gameWindowHandle, &gameWindowRect);
WNDCLASSEX w;
w.cbSize = sizeof(WNDCLASSEX);
w.style = CS_HREDRAW | CS_VREDRAW;
w.lpfnWndProc = WndProc;
w.cbClsExtra = 0;
w.cbWndExtra = 0;
w.hInstance = hInstance;
w.hIcon = NULL;
w.hCursor = NULL;
w.hbrBackground = (HBRUSH)0;
w.lpszMenuName = NULL;
w.lpszClassName = "ClassName";
w.hIconSm = NULL;
if (!RegisterClassEx(&w))
{
MessageBox(NULL, "Could not Register Class", "Window Title", NULL);
return -1;
}
HWND hWnd = CreateWindowEx(WS_EX_TOPMOST | WS_EX_LAYERED | WS_EX_TRANSPARENT, "ClassName", "Window",
WS_CAPTION,
gameWindowRect.left, //x
gameWindowRect.top, // y
gameWindowRect.right - gameWindowRect.left, // width
gameWindowRect.bottom - gameWindowRect.top, // height
NULL, NULL,
hInstance, NULL);
if (!hWnd)
{
MessageBox(NULL, "Call to create window failed", "Win32 Guided tour", NULL);
return -1;
}
else
{
GetWindowRect(hWnd, &overlayWindowRect);
}
// Remove Borders around window
SetWindowLong(hWnd, GWL_STYLE, GetWindowLong(hWnd, GWL_STYLE) & ~(WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_BORDER));
SetWindowLong(hWnd, GWL_EXSTYLE, GetWindowLong(hWnd, GWL_EXSTYLE) & ~(WS_EX_DLGMODALFRAME | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE));
SetWindowPos(hWnd, NULL, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
// Make the Background Transparent
SetLayeredWindowAttributes(hWnd, RGB(255, 255, 255), 255, LWA_COLORKEY); // Replaces color white with transparancey
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
HDC myHDC = GetDC(hWnd);
// Drawing Stuff
HPEN myPen = CreatePen(PS_SOLID, 2, RGB(255, 0, 0));
HPEN originalPen;
originalPen = (HPEN)SelectObject(myHDC, myPen);
//main loop waits for messages
MSG msg;
while (true)
{
//peekmessage allows for program to do multiple things at once. faster than getmessage()
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
// if msg is quit, quit
if (msg.message == WM_QUIT)
break;
}
else
{
Rectangle(myHDC, 200, 200, 300, 400);
ZeroMemory(&gameWindowRect, sizeof(gameWindowRect)); //clear out the struct
GetWindowRect(gameWindowHandle, &gameWindowRect); // retrieves the games xy and height width and stores in gamewindowrect
if (gameWindowRect.right != overlayWindowRect.right) // checks if the x coordinates are the same
{
ZeroMemory(&gameWindowRect, sizeof(gameWindowRect)); // clear out struct once again
GetWindowRect(gameWindowHandle, &gameWindowRect); // get the dimensions of the game window again
MoveWindow(hWnd, gameWindowRect.left, gameWindowRect.top, gameWindowRect.right - gameWindowRect.left, gameWindowRect.bottom - gameWindowRect.top, TRUE);
// moves window to specific spot
}
}
Sleep(5);
}
cleanUpObjects(myPen);
cleanUpObjects(originalPen);
return msg.wParam;
}
LRESULT CALLBACK WndProc(
HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
PAINTSTRUCT ps;
HDC hdc;
switch (uMsg)
{
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
DestroyWindow(hWnd);
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
break;
}
return 0;
}
//cleans up objects
void cleanUpObjects(HPEN pen)
{
DeleteObject(pen);
}
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.