How to handle click events on a Windows treeview items - windows

How can I handle individual items being clicked in a MS Windows treeview ?
My windows proc has :
LRESULT CALLBACK WndProcTreeView(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
PAINTSTRUCT paintStruct;
HDC hDC;
switch (message)
{
case WM_PAINT:
{
hDC = BeginPaint(hwnd, &paintStruct);
EndPaint(hwnd, &paintStruct);
break;
}
case WM_NOTIFY:
{
switch (reinterpret_cast<LPNMHDR>(lParam)->code) {
case NM_CLICK:
MessageBox(nullptr, "click", "click", MB_OK);
}
}
default:
{
return DefWindowProc(hwnd, message, wParam, lParam);
break;
}
}
Which outputs a message box when I click on the treeview control. How can I handle individual elements ?
Example of a treeview item being added to the list :
std::string vTxt = std::string("Vertex count : ") + std::to_string(mesh.v.size());
tvinsert.hInsertAfter = mesh_items[mesh_items.size() - 1];
tvinsert.hParent = mesh_items[mesh_items.size() - 1];
tvinsert.item.mask = TVIF_TEXT;
tvinsert.item.pszText = (LPSTR)vTxt.c_str();
mesh_items_sub.push_back((HTREEITEM)SendMessage(hwnd, TVM_INSERTITEM, 0, (LPARAM)&tvinsert));
I have seen using SendDlgItemMessage instead (which gives an ID as LOWORD(wParam) inside the windows proc) but it requires IDs set in a resource file - which I don't know how to create.

Two things I needed for my code to work : first give each item a lparam value and changing TVIF_TEXT as item's mask to TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM (TVIF_PARAM allowing for an lparam to be passed to the window proc thus identifying the controller).
Working code excerpt :
TV_INSERTSTRUCT tvinsert;
// ...
tvinsert.hInsertAfter = Root;
tvinsert.hParent = Root;
tvinsert.item.pszText = std::string("some text...").c_str();
tvinsert.item.lParam = ID_SOME_ID; // << #defined constant or plain int
tvinsert.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM;
root_sub.push_back((HTREEITEM)SendMessage(hwnd, TVM_INSERTITEM, 0, (LPARAM)&tvinsert));
// window proc code below
LRESULT CALLBACK WndProcTreeView(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
PAINTSTRUCT paintStruct;
HDC hDC;
switch (message)
{
case WM_PAINT:
{
hDC = BeginPaint(hwnd, &paintStruct);
EndPaint(hwnd, &paintStruct);
break;
}
case WM_NOTIFY:
{
LPNM_TREEVIEW pntv = (LPNM_TREEVIEW)lParam;
if (pntv->hdr.code == TVN_SELCHANGED) {
switch (pntv->itemNew.lParam) {
case ID_SOME_ID:
std::cout << "ID_SOME_ID selected caught here..." << std::endl;
break;
}
}
}
default:
{
return DefWindowProc(hwnd, message, wParam, lParam);
break;
}
}
return 0;
}
Good explanation/example here (in french) http://chgi.developpez.com/windows/treeview/

Related

Why the window of control coulnd't appear?

I saw an article on the Microsoft website MSDN that introduced an advanced way of writing.
I apply it to the extension of the class, and usually I don't have a problem.
But recently I encountered a problem when I tried to write a class.
I want to implement a listview class to provide us some convenience when we manipulate the listview control.
template <class DERIVED_TYPE>
class BaseWindow
{
public:
LPCTSTR className = "myWindows";
LPCTSTR Caption = "myWindows";
DWORD Style = WS_OVERLAPPEDWINDOW | WS_VISIBLE;
UINT classStyle = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
DWORD ExStyle = 0;
...
other_property
...
static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
DERIVED_TYPE *pThis = NULL;
if (uMsg == WM_NCCREATE)
{
CREATESTRUCT* pCreate = (CREATESTRUCT*)lParam;
pThis = (DERIVED_TYPE*)pCreate->lpCreateParams;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pThis);
if (pThis)pThis->m_hwnd = hwnd;
}else{
pThis = (DERIVED_TYPE*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if(pThis)pThis->m_hwnd = hwnd;
}
if (pThis)
{
return pThis->HandleMessage(hwnd, uMsg, wParam, lParam);
}else{
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
ATOM RegClass()
{
WNDCLASS wc = { 0 };
wc.lpfnWndProc = BaseWindow::WindowProc;
wc.hInstance = hInst;
wc.lpszClassName = className;
wc.style = classStyle;
wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
return RegisterClass(&wc);
}
BOOL CreateWinEx( LPCTSTR className_,LPCTSTR Caption_,
DWORD Style_,DWORD ExStyle_,
int x_,int y_,int w_,int h_,
HWND hWndParent_,HMENU hMenu_,HINSTANCE hInst_)
{
m_hwnd = CreateWindowEx(
ExStyle_, className_, Caption_, Style_,
x_, y_, w_, h_,
hWndParent_, hMenu_, hInst_, this
);
return (m_hwnd ? TRUE : FALSE);
}
BOOL Create()
{
ATOM rst = RegClass();
if(rst == 0) return FALSE;
m_hwnd = CreateWindowEx(
ExStyle, className, Caption, Style,
x, y, w, h,
hWndParent, hMenu, hInst, this
);
return (m_hwnd ? TRUE : FALSE);
}
}
Based on the above, I have derived two classes, one as the main form and the other as the listview class that I am going to design.
class FirstWindow : public BaseWindow<FirstWindow>
{
public:
FirstWindow(){};
...
some_property
...
LRESULT HandleMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK ButtonProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
public:
};
class listView : public BaseWindow<listView>
{
public:
listView();
int init(LPCTSTR className_,HWND hWndParent_,int x,int y,int w,int h)
{
this->className = className_;
this->hWndParent = hWndParent_;
this->Style = WS_CHILD| WS_VISIBLE | ~WS_CAPTION;
this->x = x;
this->y = y;
this->w = w;
this->h = h;
this->Create();
hWndMsgWindow = m_hwnd;
CreateListView(hWndMsgWindow);
ShowWindow(hWndMsgWindow,SW_SHOW);
ShowWindow(hWndListView,SW_SHOW);
UpdateWindow(hWndMsgWindow);
UpdateWindow(hWndListView);
idebug("listview::init >> hWndMsgWindow:%d,parent:%d, error:%d\n",hWndMsgWindow,GetParent(hWndMsgWindow), GetLastError());
idebug("listview::init >> hWndListView:%d,parent:%d,error:%d\n",hWndListView,GetParent(hWndListView), GetLastError());
return 1;
}
LRESULT HandleMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
LRESULT listView::CreateListView(HWND hwndParent_)
{
InitCommonControls();
bool rst = CreateWinEx(WC_LISTVIEW, NULL,
WS_CHILD | WS_VISIBLE | LVS_REPORT |LVS_EDITLABELS | LVS_NOCOLUMNHEADER | LVS_OWNERDATA |LVS_OWNERDRAWFIXED | WS_BORDER,0,
x,y,w,h,
hwndParent_, NULL, GetModuleHandle(NULL));
if(rst)
hWndListView = m_hwnd;
idebug("CreateListView >> rst:%d,hwndlistview:%d,parent:%d,hwndParent_:%d, error:%d\n",rst,hWndListView,GetParent(hWndListView),hwndParent_, GetLastError());
return rst;
}
LRESULT AddItem();
LRESULT OnListViewNotify(HWND hwnd, LPARAM lParam);
void OwnerDraw(LPDRAWITEMSTRUCT lpdis, HDC hdc, HWND hWnd, const TCHAR* szDraw);
void on_drawItem(HWND hWnd, LPARAM lParwm);
void on_notify(HWND hWnd, WPARAM wParwm, LPARAM lParam);
void on_create(HWND hWnd);
void on_measureItem(LPARAM lParam);
bool add_item();
bool add_item_sub();
...
some_property
...
};
fire FirstWindow first
FirstWindow * pFirstWnd;
listView lv;
int main(int argc, char** argv)
{
FirstWindow win;
pFirstWnd = &win;
win.className = "FirstWindow";
win.Style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
win.x = 100;
win.y = 100;
win.w = 600;
win.h = 300;
bool rst = win.Create();
if (!rst)
{
idebug("getlasterror:%d,line:201\n", GetLastError());
bug.ShowErr();
}
else {
lv.init("mylistview",pFirstWnd->m_hwnd,20,20,300,200);
ShowWindow(lv.m_hwnd, SW_SHOW);
}
MSG msg = {};
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
The goal of the listview_class is to be able to receive and process window messages independently. not like this:
LRESULT FirstWindow::HandleMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE:
{
//lv.init("mylistview2",pFirstWnd->m_hwnd,20,20,300,200);
//ShowWindow(lv.m_hwnd, SW_SHOW);
break;
}
case WM_NOTIFY:
{
lv.on_notify(hWnd,wParam,lParam);
break;
}
...
}
default:
return DefWindowProc(m_hwnd, uMsg, wParam, lParam);
}
return TRUE;
}
Instead, I want to process messages independently within listview class,like this:
LRESULT listView::HandleMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
static HDC hdc;
HRESULT lResult;
switch (uMsg)
{
case WM_CREATE:
{
idebug("class_listview >> WM_CREATE >> hWnd :%d, className:%s, parent:%d\n",hWnd, className , GetParent(hWnd));
//HWND h = CreateWindowEx(0, WC_LISTVIEW, NULL,
WS_CHILD | WS_VISIBLE | LVS_REPORT |LVS_EDITLABELS | LVS_NOCOLUMNHEADER | LVS_OWNERDATA |LVS_OWNERDRAWFIXED | WS_BORDER,
20,20,300,200,
hWnd,NULL, GetModuleHandle(NULL),NULL);
//ShowWindow(h,SW_SHOW);
//UpdateWindow(hWnd);
//idebug("class_listview >> WM_CREATE >> hwnd:%d,parent:%d,error:%d\n",h,GetParent(h), GetLastError());
//ShowWindow(h,SW_SHOW);
//UpdateWindow(h);
break;
}
case WM_NOTIFY:
{
on_notify(hWnd,wParam,lParam);
break;
}
case WM_MEASUREITEM:
{
on_measureItem(lParam);
break;
}
case WM_DRAWITEM:
{
on_drawItem(hWnd,lParam);
break;
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 2));
EndPaint(hWnd, &ps);
idebug("class_listview >> WM_PAINT->hWnd:%d,id:%d,err:%d\n", hWnd, id, GetLastError());
break;
}
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 0;
}
The problem now is that i can't see other control windows except that FirstWindow can display normally.
update:
some message from debug:
class_listview >> WM_CREATE >> hWnd :1443146, className:mylistview, parent:262350,error:0
listView >> WM_SIZE >> hWnd:1443146,parent:262350
CreateListView >> rst:1,hwndlistview:853318,parent:1443146,hwndParent_:1443146, error:5
listview::init >> hWndMsgWindow:1443146,parent:262350, error:5
listview::init >> hWndListView:853318,parent:1443146,error:5
the key of the problem is the return value of WM_NOTIFY message.
i typed :
case WM_NOTIFY:
{
retnrn on_notify(hWnd,wParam,lParam);
}
the problem was resolved.

How do I force a redraw of WIN32 scrolled window while holding scrollbar down

We've noticed an issue with ScrollWindowEx on our Windows app. Because there are a lot of child windows, if you hold down the scrollbar and move around, it causes a huge stall in DesktopWindowManager.
This is documented here
Why is my MFC application hanging after interacting with both scroll-bars?
So, I've taken the call to ScrollWindowEx out and now I can move my scrollbar with no issues, however the window and all its children only get drawn at the new position when I let go of the scrollbar.
It seems that my entire program stalls and gets stuck in the message loop when the scroll bar is held down. This isnt a problem if we can force the window that the scrollbar is attached to to update. But how do I do that ?
I've tried this
UpdateWindow(pPane);
RedrawWindow(pPane,NULL,NULL,RDW_ALLCHILDREN|RDW_INVALIDATE|RDW_ERASE | RDW_INTERNALPAINT | RDW_UPDATENOW);
And it does update and redraw - you can see the glitching - BUT it redraws it at the position that it was last left at. Not where the new position of the scroll bar would set it.
Im calling
SetScrollInfo(...)
with the correct parameters, otherwise it wouldn't work normally
How can I get it to redraw the window and its children with the correct parameters, whilst the scroll bar is held down?
This is my scroll message handler
case WM_VSCROLL:
{
int iVScrollPos;
SCROLLINFO vsi;
ZeroMemory(&vsi, sizeof(SCROLLINFO));
vsi.cbSize=sizeof(SCROLLINFO);
vsi.fMask=SIF_ALL;
GetScrollInfo(hWnd,SB_VERT,&vsi);
iVScrollPos=vsi.nPos;
switch(LOWORD(wParam))
{
case SB_LINEUP:
if(vsi.nPos>vsi.nMin)
vsi.nPos=vsi.nPos-10;
break;
case SB_PAGEUP:
vsi.nPos = vsi.nPos - vsi.nPage;
break;
case SB_LINEDOWN:
if(vsi.nPos<vsi.nMax)
vsi.nPos=vsi.nPos+10;
break;
case SB_PAGEDOWN:
vsi.nPos = vsi.nPos + vsi.nPage;
break;
case SB_THUMBTRACK:
vsi.nPos=vsi.nTrackPos;
break;
}
vsi.nMin=0;
vsi.nMax=pOW->dialogysize;
vsi.nPage=sy;
SetScrollInfo(hWnd,SB_VERT,&vsi,TRUE);
GetScrollInfo(hWnd, SB_VERT, &vsi);
if(vsi.nPos != iVScrollPos)
{
RedrawWindow(hWnd,NULL,NULL,RDW_ALLCHILDREN|RDW_INVALIDATE|RDW_ERASE | RDW_INTERNALPAINT | RDW_UPDATENOW);
}
The window redraws as you move the scroll bar, but in the same place it was when you held the mouse down on the scrollbar. It onlyupdates position when you let go of the mouse button.
Thanks
Shaun
EDIT - Ive created a sample that reproduces the behaviour. Note we have a separate window class to hold the multiple edit windows. I havent handled the WM_SIZE and WM_MOVE messages in this demo but the code should work with the window as it is.
IF USESCROLL is defined in and you move the scrollbar up and down it casues a lockup in DesktopWindowManager
IF USESCROLL is not commented in, then the trackPos in the structure still gets updated and I call reDrawWindow, but the child edit windows dont move as I scroll the scrollbar.
As I dont want to have the lockup, how can I get them to move please ?
Thanks again
Shaun
#include <windows.h>
#include <stdio.h>
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK PaneProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{
// Register the window class.
WNDCLASS wc = { };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = "Class";
RegisterClass(&wc);
wc.lpfnWndProc = PaneProc;
wc.hInstance = hInstance;
wc.lpszClassName = "Pane";
RegisterClass(&wc);
// Create the window.
HWND hwnd = CreateWindowEx(
0, // Optional window styles.
(LPCSTR)"Class", // Window class
(LPCSTR)"Test", // Window text
WS_OVERLAPPEDWINDOW| WS_BORDER | WS_CAPTION | WS_CLIPSIBLINGS | WS_THICKFRAME | WS_SYSMENU, // Window style
200,200,400,400,
NULL,NULL,hInstance,NULL);
if (hwnd == NULL)
{
return 0;
}
HWND hwndPane = CreateWindowEx(
0, // Optional window styles.
(LPCSTR)"Pane", // Window class
(LPCSTR)"Test", // Window text
WS_OVERLAPPEDWINDOW|WS_VISIBLE|WS_VSCROLL, // Window style
220,220,400,400,
hwnd,NULL,hInstance,NULL);
if (hwndPane == NULL)
{
return 0;
}
for(int i=0;i<200;i++)
{
HWND eb = CreateWindowEx(WS_EX_CLIENTEDGE, TEXT("Edit"), NULL,ES_AUTOHSCROLL | WS_CHILD| WS_VISIBLE, 16, 16+24*i, 64, 24, hwndPane, (HMENU)i+10000, hInstance, NULL);
char tmp[64];
sprintf(tmp,"%d",i);
SetWindowText(eb,tmp);
}
ShowWindow(hwnd, nCmdShow);
ShowWindow(hwndPane, nCmdShow);
// Run the message loop.
while(1)
{
MSG msg = { };
while (PeekMessage(&msg, NULL, 0,0,PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Sleep(10);
}
return 0;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH) (COLOR_WINDOW+1));
EndPaint(hwnd, &ps);
}
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
LRESULT CALLBACK PaneProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE:
int iVScrollPos;
SCROLLINFO vsi;
ZeroMemory(&vsi, sizeof(SCROLLINFO));
vsi.cbSize=sizeof(SCROLLINFO);
vsi.fMask=SIF_ALL;
GetScrollInfo(hwnd,SB_VERT,&vsi);
iVScrollPos=vsi.nPos;
vsi.nMin=0;
vsi.nMax=16+24*200+40;
vsi.nPage=400;
SetScrollInfo(hwnd,SB_VERT,&vsi,TRUE);
GetScrollInfo(hwnd, SB_VERT, &vsi);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_VSCROLL:
{
int iVScrollPos;
SCROLLINFO vsi;
ZeroMemory(&vsi, sizeof(SCROLLINFO));
vsi.cbSize=sizeof(SCROLLINFO);
vsi.fMask=SIF_ALL;
GetScrollInfo(hwnd,SB_VERT,&vsi);
iVScrollPos=vsi.nPos;
switch(LOWORD(wParam))
{
case SB_THUMBTRACK:
vsi.nPos=vsi.nTrackPos;
break;
}
vsi.nMin=0;
vsi.nMax=16+24*200+40;
vsi.nPage=400;
SetScrollInfo(hwnd,SB_VERT,&vsi,TRUE);
GetScrollInfo(hwnd, SB_VERT, &vsi);
if(vsi.nPos != iVScrollPos)
{
float ScrollAmtY=-(vsi.nPos - iVScrollPos);
#define USESCROLL
#ifdef USESCROLL
int ok=ScrollWindowEx(hwnd ,0,ScrollAmtY,NULL,NULL,NULL,NULL,SW_INVALIDATE|SW_ERASE|SW_SCROLLCHILDREN);
#else
UpdateWindow(hwnd);
RedrawWindow(hwnd,NULL,NULL,RDW_ALLCHILDREN|RDW_INVALIDATE|RDW_ERASE | RDW_INTERNALPAINT | RDW_UPDATENOW);
#endif
}
return 0;
}
break;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

How to handle unhandled Win32 Message Reflection to Sublcassed Controls via OCM_ messages

I found the following on CodeProject. It makes sense except if the sub-classed control doesn't handle the OCM_ message which means the default handling of the original message never occurs. Is there an elegant solution instead of having to always sync up the messages this function sends with the sub-classed windows procedures?
LRESULT DefParentProc(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam)
{
switch (umsg) {
case WM_NOTIFY:
{
NMHDR* nmhdr = (NMHDR*)lparam;
if (nmhdr->hwndFrom != NULL)
return SendMessage(nmhdr->hwndFrom, umsg + OCM__BASE, wparam, lparam);
break;
}
// All of these provide the control's HHWND in LPARAM
case WM_COMMAND:
case WM_CTLCOLORBTN:
case WM_CTLCOLOREDIT:
case WM_CTLCOLORDLG:
case WM_CTLCOLORLISTBOX:
case WM_CTLCOLORMSGBOX:
case WM_CTLCOLORSCROLLBAR:
case WM_CTLCOLORSTATIC:
case WM_VKEYTOITEM:
case WM_CHARTOITEM:
if (lparam != 0)
return SendMessage((HWND)lparam, umsg + OCM__BASE, wparam, lparam);
break;
// All of these provide ID of the control in WPARAM:
case WM_DRAWITEM:
case WM_MEASUREITEM:
case WM_DELETEITEM:
case WM_COMPAREITEM:
if (wparam != 0) {
HWND hwndControl = GetDlgItem(hwnd, wparam);
if (hwndControl)
return SendMessage(hwndControl, umsg + OCM__BASE, wparam, lparam);
}
break;
// Note we do not reflect WM_PARENTNOTIFY -> OCM_PARENTNOTIFY as that
// usually does not make much sense.
}
return DefWindowProc(hwnd, umsg, wparam, lparam);
}
There is not really a clean solution here.
You could ensure that all child controls subtract OCM__BASE when calling DefWindowProc() for unhandled OCM_... messages.
LRESULT WINAPI DefChildProc(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam)
{
switch (umsg) {
...
}
if ((umsg >= OCM__BASE) && (umsg <= OCM__MAX)) {
umsg -= OCM__BASE;
}
return DefWindowProc(hwnd, umsg, wparam, lparam);
}
Otherwise, you could have each OCM_... message carry a pointer to a struct in its WPARAM or LPARAM, where the struct contains the real WPARAM/LPARAM and the output LRESULT, and then each child control can return TRUE if a given OCM_... message is handled. The parent can then call DefWindowProc() with the original WM_... message if SendMessage(OCM_...) returns FALSE.
struct OCMInfo
{
LPARAM lParam;
LRESULT lResult;
};
LRESULT WINAPI DefParentProc(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam)
{
switch (umsg) {
case WM_NOTIFY:
{
NMHDR* nmhdr = (NMHDR*)lparam;
if (nmhdr->hwndFrom != NULL)
{
OCMInfo info;
info.lParam = lParam;
info.lResult = 0;
if (SendMessage(nmhdr->hwndFrom, umsg + OCM__BASE, wparam, (LPARAM)&info))
return info.lResult;
}
break;
}
// All of these provide the control's HHWND in LPARAM
case WM_COMMAND:
case WM_CTLCOLORBTN:
case WM_CTLCOLOREDIT:
case WM_CTLCOLORDLG:
case WM_CTLCOLORLISTBOX:
case WM_CTLCOLORMSGBOX:
case WM_CTLCOLORSCROLLBAR:
case WM_CTLCOLORSTATIC:
case WM_VKEYTOITEM:
case WM_CHARTOITEM:
if (lparam != 0)
{
OCMInfo info;
info.lParam = lParam;
info.lResult = 0;
if (SendMessage((HWND)lparam, umsg + OCM__BASE, wparam, (LPARAM)&info))
return info.lResult;
}
break;
// All of these provide ID of the control in WPARAM:
case WM_DRAWITEM:
case WM_MEASUREITEM:
case WM_DELETEITEM:
case WM_COMPAREITEM:
if (wparam != 0) {
HWND hwndControl = GetDlgItem(hwnd, wparam);
if (hwndControl)
{
OCMInfo info;
info.lParam = lParam;
info.lResult = 0;
if (SendMessage(hwndControl, umsg + OCM__BASE, wparam, (LPARAM)&info))
return info.lResult;
}
}
break;
// Note we do not reflect WM_PARENTNOTIFY -> OCM_PARENTNOTIFY as that
// usually does not make much sense.
}
return DefWindowProc(hwnd, umsg, wparam, lparam);
}
LRESULT WINAPI DefChildProc(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam)
{
switch (umsg) {
case OCM__NOTIFY:
{
OCMInfo* info = (OCMInfo*)lparam;
NMHDR* nmhdr = (NMHDR*)(info->lparam);
if (...) {
...
info->lResult = ...;
return TRUE;
}
break;
}
case OCM__COMMAND:
{
OCMInfo* info = (OCMInfo*)lparam;
if (...) {
...
info->lResult = ...;
return TRUE;
}
break;
}
...
}
return DefWindowProc(hwnd, umsg, wparam, lparam);
}

How to handle results from a PropertySheet?

I find different examples in the Internet of how to program a PropertySheet in WinAPI, but they are not complete.
The code I am using is shown below. I have a PropertySheet with 3 Tabs, each one with a Dialog.
The different Dialogs are called, when I click the Tabs, so far it is working.
However, when I leave the PropertySheet pressing OK button, how do I get the contents in the Textboxes etc. of each Dialog?
Normally I used to do that in the DialogProc when WM_COMMAND/IDOK was received using:
GetDlgItemText( hDlg,IDC_TEXTBOX1, buf, 100);
But in the PropertySheet there is only one OK Button for all dialogs, no WM_COMMAND/IDOK is received in the DialogProc.
What shall I do?
Resource_file:
IDD_DIALOG_1 DIALOGEX 0, 0, 385, 186
STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
BEGIN
LTEXT "param",IDC_STATIC,6,23,39,10
EDITTEXT IDC_TEXTBOX1,48,20,237,15
END
C Source:
LRESULT CALLBACK
Dialog1(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
char buf[500];
char* ptr;
int p; // =lParam, rin of edited person
int f;
switch (message)
{
case WM_INITDIALOG:
{
SetDlgItemText(hDlg, IDC_TEXTBOX1, "something");
return 0;
}
case WM_COMMAND:
{
switch (LOWORD(wParam))
{
case IDOK: // never reached (OK Button belongs to the PropertySheet!)
}
}
}
return FALSE;
} /* Dialog1 */
INT_PTR DoPropertySheet(HWND hwndOwner, LPARAM p)
{
PROPSHEETPAGE psp[3];
PROPSHEETHEADER psh;
memset(psp,0,sizeof(psp));
for(int i=0;i<3; i++)
{
psp[i].dwSize = sizeof(PROPSHEETPAGE);
psp[i].dwFlags = PSP_USETITLE;
psp[i].hInstance = hInstance;
psp[i].lParam = p;
}
psp[0].pszTemplate = MAKEINTRESOURCE(IDD_DIALOG_1);
psp[0].pfnDlgProc = (DLGPROC)Dialog1;
psp[1].pszTemplate = MAKEINTRESOURCE(IDD_DIALOG_2);
psp[1].pfnDlgProc = (DLGPROC)Dialog2;
psp[2].pszTemplate = MAKEINTRESOURCE(IDD_DIALOG_3);
psp[2].pfnDlgProc = (DLGPROC)Dialog3;
psh.dwSize = sizeof(PROPSHEETHEADER);
psh.dwFlags = PSH_PROPSHEETPAGE | PSH_NOAPPLYNOW;
psh.hwndParent = hwndOwner;
psh.hInstance = hInstance;
psh.pszIcon = 0;
psh.nPages = sizeof(psp) / sizeof(PROPSHEETPAGE);
psh.nStartPage = 0;
psh.ppsp = (LPCPROPSHEETPAGE) &psp;
psh.pfnCallback = NULL;
if (PropertySheet(&psh)) // 0:cancel, otherwise:1
{
//get contens of propertySheet here?? how??
}
return 0;
}
when user press OK or Apply all your pages got PSN_APPLY notification code. so you need look for WM_NOTIFY with PSN_APPLY code
when user press cancel you got PSN_RESET notification
INT_PTR CALLBACK PPDlgProc(HWND hwnd, UINT umsg, WPARAM wParam, LPARAM lParam)
{
union {
LPARAM lp;
NMHDR* hdr;
PSHNOTIFY* psn;
};
switch (umsg)
{
case WM_NOTIFY:
lp = lParam;
switch (hdr->code)
{
case PSN_APPLY:
DbgPrint("apply");
break;
case PSN_RESET:
DbgPrint("cancel\n");
break;
}
break;
}
return 0;
}

Not get control in this case CDDS_ITEMPREPAINT| CDDS_SUBITEM:

I am using NM_CUSTOMDRAW for changing the color of Tree View item & its child on some conditions.I want to get subitems control individually but dwDrawStage never get this case CDDS_ITEMPREPAINT| CDDS_SUBITEM my code snippet is here:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
int j;
HWND hwndTree;
TVITEM tv;
TVHITTESTINFO hti;
char achBuf[100];
switch (message)
{
case WM_CREATE:
RECT rcClient; // dimensions of client area
// handle to tree-view control
// Ensure that the common control DLL is loaded.
InitCommonControls();
// Get the dimensions of the parent window's client area, and create
// the tree-view control.
GetClientRect(hWnd, &rcClient);
hwndTV = CreateWindowEx(0,
WC_TREEVIEW,
TEXT("Tree View"),
WS_VISIBLE | WS_CHILD | WS_BORDER | TVS_HASLINES,
0,
0,
rcClient.right,
rcClient.bottom,
hWnd,
(HMENU)ID_TREEVIEW,
hInst,
NULL);
// Initialize the image list, and add items to the control.
// InitTreeViewImageLists and InitTreeViewItems are application-
// defined functions, shown later.
if (!InitTreeViewItems(hwndTV))
{
DestroyWindow(hwndTV);
return FALSE;
}
break;
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
case IDC_MAIN_BUTTON:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
//TreeView_EnsureVisible(hwndTV,hti);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
EndPaint(hWnd, &ps);
break;
case WM_NOTIFY:
{
LPNMTREEVIEW pnm = (LPNMTREEVIEW)lParam;
if (pnm->hdr.code == NM_CUSTOMDRAW)
{
//LPNMLVCUSTOMDRAW lplvcd = (LPNMLVCUSTOMDRAW)lParam;
//TreeView_GetItem(
//pnm->iItem
LPNMTVCUSTOMDRAW lplvcd = (LPNMTVCUSTOMDRAW)lParam;
//pnm->iItem
//TreeView_GetItem(hWnd,pnm->iItem);
switch (lplvcd->nmcd.dwDrawStage)
{
case CDDS_PREPAINT :
return CDRF_NOTIFYITEMDRAW;
break;
case CDDS_ITEMPREPAINT:
if(found_unMatched(lplvcd->nmcd.lItemlParam))
{
hwndTree = GetDlgItem(hWnd,ID_TREEVIEW);
lplvcd->clrText = RGB(0, 255,0);
ZeroMemory(&tv, sizeof(TVITEM));
tv.hItem=(HTREEITEM)lplvcd->nmcd.dwItemSpec;
tv.mask=TVIF_TEXT|TVIF_HANDLE;
tv.cchTextMax=100;
tv.pszText=(LPWSTR)achBuf;
if(TreeView_GetItem(hwndTree,&tv))
{
achBuf;
}
hChild =(HTREEITEM)TreeView_GetChild(hwndTree,(HTREEITEM)lplvcd->nmcd.dwItemSpec);
hSibling=(HTREEITEM)TreeView_GetNextSibling(hwndTree,hChild);
}
return CDRF_NOTIFYSUBITEMDRAW;
break;
case CDDS_POSTPAINT:
return CDRF_DODEFAULT;
break;
case CDDS_ITEMPOSTPAINT| CDDS_SUBITEM :
hwndTree = GetDlgItem(hWnd,ID_TREEVIEW);
ZeroMemory(&tv, sizeof(TVITEM));
tv.hItem=(HTREEITEM)lplvcd->nmcd.dwItemSpec;
tv.mask=TVIF_TEXT|TVIF_HANDLE;
tv.cchTextMax=100;
tv.pszText=(LPWSTR)achBuf;
if(TreeView_GetItem(hwndTree,&tv))
{
achBuf;
}
if((HTREEITEM)lplvcd->nmcd.dwItemSpec==hSibling)
{
lplvcd->clrText = RGB(176, 0,0);
}
return CDRF_NEWFONT;
break;
case CDDS_ITEMPOSTPAINT:
double_click=1;
break;
}
}}break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
this will color my parent node but child nodes(subitem) remained default(black) and return CDRF_NOTIFYSUBITEMDRAW didnt work. Please help!!!

Resources