How to show a window before processing in MFC - mfc

How can I get a window visible before processing?
I'm trying to do something like this:
void CMyClass::OnButton1Clicked()
{
CString class_str = AfxRegisterWndClass(CS_VREDRAW | CS_HREDRAW, ::LoadCursor(NULL,IDC_ARROW),(HBRUSH)::GetStockObject(WHITE_BRUSH),::LoadIcon(NULL, IDI_APPLICATION));
CWnd* wnd = new CWnd();
wnd->Create(class_str,"Test",WS_CHILD | WS_VISIBLE | WS_BORDER, CRect(0,0,100,100), AfxGetMainWnd(), 1432);
//AfxMessageBox("test"); // <- if this is executed wnd gets visible before sleep()
Sleep(5000); //this would be processing
wnd->DestroyWindow();
delete wnd;
return;
}
The window doesn't show at all. I've tried displaying it in an UI thread but it doesn't show either. If I do a message loop until window is created, it shows up but after a while the main message loop asserts.
I know it shouldn't be made like this but rather processing in worker thread, but with current code I'm working with it would require changes too big.

Disclaimer: I don't know MFC, but I know the windows API, which is what MFC was built on top of.
Perhaps you need to make a call to ShowWindow(). Also, are you handling the WM_CREATE message? If so, perhaps you are returning the wrong value.
If the above doesn't apply or make sense, disregard.

Related

Create Button and Reference in message map

Im creating a button on my oncreate using message map.
I am unable to get a callback message from ON_BN_CLICKED when passing a reference to ICL_OK.
I don't believe its a parenting issue. The window is an CFrameWnd and parent is a CMainFrame.
Even getting all the messages and I can swtich between what I want to do as I have list boxes and input boxes to add also and edit / get response.
Thanks
Cant go into the gui main thread loop. Message map is the way I need to achieve this.
okBtn.Create(_T("Ok Button"), WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
CRect(10, 10, BUTTON_WIDTH, HEIGHT), this, ICL_OK);
To click the button and get a response. Instead in using OnCmdMsg and getting a reference to its nID which I don't like. I want BN_CLICKED to work.
Refering to this answer
Message map macros
I can again confirm oncmdmsg works but wm_command event does not fire.
Message map macros
UPDATE: Still not working, alternative is to use ON_COMMAND_RANGE and still fires the WM_COMMAND so just have to restrict the amount of messages it handles. Hope it helps someone. If you want to generate a button the solution below might help you.
You are writing that the button is not showing in the window. There is a reason for that, and I would guess this: You define the button in a subroutine/method/function instead of defining it in its parent class.
Instead, in its parent class, whether that is the CMainFrame or some other Window, define a button like:
class CMainFrame : public CFrameWnd
{
/// bunch of stuff, including OnCreate() or OnCreateClient()
CButton m_button;
};
In the class that houses the button, assuming CMainFrame for now, create the button... ideally in OnCreate() or OnCreateClient()
call the baseclass version then your button create....
int CMainFrame::OnCreate(LPCREATESTRUCT lpcs)
{
int ret = __super::OnCreate(lpcs);
if (ret != -1) {
m_button.Create(_T("Ok Button"), WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, CRect(10, 10, BUTTON_WIDTH, HEIGHT), this, ICL_OK);
}
return ret;
}
If your constructor is in a method, then its destructor will be called at the end of the method. MFC CWnd derived windows classes usually call DestroyWindow() in their destructor and what this means is that the window is destroyed by the end of the call and that is the reason it is not visible.

Why does c++ send WM_DRAWITEM to the parent process?

I started recently learning C++ and WinAPI, I want to be able to create my own programs
I am subclassing a button (into separate file, because I like things clean and organized - is that a bad idea?), because I want to have few of the kind with same parameters. I also want to draw it, and the question that pops to my mind is: wouldn't it be better to have it all in the class file? Meaning all parameters including custom draw. It is annoying to go to main file to change the looks and set all other parameters in the class file. I use codeblocks.
EDIT (explanation):
#include <Windows.h>
#include <Winuser.h>
#include "CustomButton.h"
/*global vars*/
WNDPROC CustomButton::CustomButtonLongPtr;
/*functions*/
LRESULT CALLBACK CustomButtonProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
CustomButton * CustomButton::CreateCustomButton(HINSTANCE hInstance, HWND hwnd, int pos_x, int pos_y, int width, int height)
{
CustomButton * p_CustomButton = new CustomButton;
HWND customButton = CreateWindowEx(0, "BUTTON", "OK", WS_VISIBLE | WS_CHILD | BS_OWNERDRAW, pos_x, pos_y, width, height, hwnd, (HMENU)200, (HINSTANCE)GetWindowLong(hwnd, GWLP_HINSTANCE), p_CustomButton);
if(customButton == NULL)
{
delete p_CustomButton;
MessageBox(NULL, "Problem creating the Search box.", "Error", 0);
return 0;
}
CustomButton::CustomButtonLongPtr = (WNDPROC)SetWindowLongPtr(customButton, GWLP_WNDPROC, (LONG_PTR)&CustomButton::CustomButtonProc);
return p_CustomButton;
}
I want to use WM_DRAWITEM for this button in CustomButtonProc and I am wondering why did the developers think that it would be smarted to allow to use it only in parent WinProc.
This is a little complicated to explain.
You're probably coming from a background where you plug a function into an outlet to handle events, something like
extern void onClicked(void);
button->OnClicked = onClicked;
And while it's fully possible for Windows to have done this from the start, you have to remember that Windows was originally designed to run on systems with severely limited memory, so it was important that controls not waste memory. And there are a lot of events you can get from a button:
void (*OnClicked)(void);
void (*OnDoubleClicked)(void);
void (*OnDisabled)(void);
void (*OnHighlight)(void);
void (*OnKillFocus)(void);
void (*OnPaint)(void);
void (*OnSetFocus)(void);
void (*OnUnhighlight)(void);
void (*OnUnpushed)(void);
HBRUSH (*OnCtlColorButton)(void);
Having these for every button in your program — that is, push buttons, checkboxes, radio buttons, and groupboxes — with most of them likely to be unused would just be a massive waste of memory.
Because Windows needs a way to communicate between the system and a window and between windows, Microsoft decided to create a message-passing interface where each message had a 16-bit code and two pointer-sized (originally one 32-bit and one 16-bit) parameters and a pointer-sized return value. And there aren't a lot of messages that Windows needs for itself, giving both window classes and the application a lot of real estate for using messages to communicate. So why not use a message to signal the event?
Using a message avoids the waste of memory while still allowing the button to pass data to and return data from its target window. So the logic follows that all a button would need to do is
/* this is not the correct syntax but let's use it for expository purposes */
#define BN_CLICKED someNumberHere
case WM_LBUTTONUP:
SendMessage(GetParent(hwnd), BN_CLICKED, hwnd);
break;
and the parent would handle that:
case BN_CLICKED:
if (whichButton == button1)
doButton1Stuff();
break;
No wasted memory, but still flexible and extensible. And even more importantly, also binary-compatible: if more events were added later, the size of the function pointer table would need to change, and newer programs that tried to use newer events on older systems would clobber random memory. With messages, these programs would just have dead code.
Now why send the message to the parent? If we view windows as communication endpoints, then this is obvious: you want the button to tell its parent that it was clicked because you're communicating that the button was clicked!
But more important, you didn't write the button's window procedure. Microsoft did, and they provide the same one to every program. If you could handle the message in the button procedure, where would you put it? You can't change the button procedure, after all.
(Nowadays we have something called "subclassing" which allows you to override a single window's window procedure to do custom processing. It's not used for event handling because it's more work than just sending up to the parent.)
All of this extends to custom draw; just substitute "custom draw" for "clicked" and it should still make sense. Hopefully this explanation was clear, even with that mental substitution.
If you want, you can write your own facility to handle events in the function pointer way. Keep a map of window handles to event functions and call a global dispatch function in all your window procedures to handle the event messages WM_COMMAND, WM_NOTIFY, and (for trackbars) WM_HSCROLL and WM_VSCROLL. How you do this is up to you, but think about whether you really want to do it this way; sometimes it's necessary, but sometimes it isn't. If you do, remember to provide a way to pass arbitrary data to the event function that's decided at event connection time, so the event handler can do something reasonable without relying on global state.
Thanks to comments by RemyLebeau and IInspectable I was able to also find solution to my frustration, which I am going to explain here for anybody else who's scratching their heads over this very issue.
This solution does not require VCL nor any component from Visual Studio and such.
First define your own custom message, in the way that you can reach it inside WndProc:
#define MY_DRAWITEM (WM_APP+1)
UINT uDrawButtonMsg = RegisterWindowMessage(_T("MY_DRAWITEM"));
Then find out what number it is assigned:
std::cout << uDrawButtonMsg; //for example 49648
And send this message from WndProc to your subclassed control from any message you wish, for example WM_DRAWITEM:
case WM_DRAWITEM:
{
::SendMessage(p_CustomButton->customButton, uDrawButtonMsg, wParam, lParam);
break;
}
And then in your subclass just catch the message by the 5 digit number you looked for while ago:
if(49648 == uMsg)
{
//do your DRAWITEM stuff here
}
Thanks to everybody who contributed to this article helping with exaplanation, tips and also historical background!

FindWindow by class name not working?

To preface we have a strange requirement that all dialogs must be modeless for an MFC application. There is a particular dialog using region drawing and some custom controls to select dates and times for viewing past and future data per view. I need to be able to close this window when it loses focus, the main app gets a system command, etc.
I figured the easiest way to do this would be to register the class like so:
// for CWnd::FindWindow
WNDCLASS wndcls;
SecureZeroMemory(&wndcls, sizeof(WNDCLASS));
wndcls.lpszClassName = L"CTransactionDialog";
wndcls.style = CS_HREDRAW | CS_VREDRAW;
wndcls.lpfnWndProc = AfxWndProc;
wndcls.cbClsExtra = wndcls.cbWndExtra = 0;
wndcls.hInstance = AfxGetInstanceHandle();
wndcls.hIcon = NULL;
#ifndef _WIN32_WCE_NO_CURSOR
wndcls.hCursor = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
#else
wndcls.hCursor = 0;
#endif
wndcls.hbrBackground = (HBRUSH) (COLOR_BACKGROUND + 1);
wndcls.lpszMenuName = NULL;
BOOL retVal = AfxRegisterClass(&wndcls);
if (!retVal)
AfxMessageBox(L"AfxRegisterClass(CTransactionDialog) Failed");
Then later in response to various event handlers and messages where I would want these modeless window or windows to be closed to do something simple like this:
CWnd* pFound = NULL;
while ((pFound = CWnd::FindWindow(L"CTransactionDialog", NULL)) != NULL)
pFound->DestroyWindow();
However despite the registration of the class succeeding and looking at GetRuntimeClass of the dialog in question in debug and seeing that is matches up as expected the FindWindow never seems to find or close these modeless dialogs as expected.
What am I doing wrong or is there a better way to go about this?
Update: This is how the dialog is created via a static method on the dialog class. The dialog resource for the id specified in create has the Popup property set which should resolve to WS_POPUP style under the MFC covers. The dialog shouldn't and doesn't have a parent as far as I knew.
CTransactionDialog* CTransactionDialog::ShowTransactionDialog(const CRect& crCtrlToFloatAbove, UINT dialogID, Itime defaultTime, Itime initialTime)
{
CTransactionDialog* pCTDialog = new CTransactionDialog(crCtrlToFloatAbove, dialogID, defaultTime, initialTime);
pCTDialog->Create(CTransactionDialog::IDD);
pCTDialog->ShowWindow(SW_SHOW);
return pCTDialog;
}
Update: Doh! FindWindowEx isn't finding anything either.
CWnd::FindWindowEx(AfxGetMainWnd()->GetSafeHwnd(), NULL, L"CTransactionDialog", NULL);
However I have a new plan. I'm just going to make my own window message and handle it on the main frame. I think I can get away with passing a pointer to the dialog as the lParam of the message and then casting it to a CWnd* then calling DestroyWindow. It will work for most cases in a very round about way. I may run into trouble with minimizing and maximizing of the main frame window for dialogs that nothing is holding a pointer too but we'll see.
FindWindow doesn't work with child windows. To find a child window, you can use FindWindowEx, passing the HWND of the parent window as the first parameter.
Class name denotes NOT the c++ class name - it denotes the window class name. This name was used to register the window by the OS and has nothing to do with the c++ class.
May MSDN enlight you...
CWnd::FindWindow does not search child windows.
Is it possible that the mode less window that you are creating has a parent set and that is the reason why FindWindow doesn't find it ?
Below is the simplest and cleanest solution I could come up with:
BOOL CTransactionDialog::OnNcActivate(BOOL bActive)
{
if (!bActive)
PostMessage(WM_CLOSE);
return CDialog::OnNcActivate(bActive);
}
Not 100% sure about this but I think you need to set your class name in the resource file. You are defining a windows class and creating a dialog class but you aren't linking them. Setting a class name in the WNDCLASS struct won't help unless you actually have a way of linking it to the dialog. If you use the resource file and define the dialog, and class name in there then it should work.

create a control programmatically using MFC

I just wonder how to do it.
I write :
CEdit m_wndEdit;
and in the button event handler (dialog app),
I write :
m_wndEdit.Create(//with params);
but I still don't see the control appear in the UI.
I actually wrote this in the button handler :
CWnd* pWnd = GetDlgItem(IDC_LIST1);
CRect rect;
pWnd->GetClientRect(&rect);
//pWnd->CalcWindowRect(rect,CWnd::adjustBorder);
wnd_Edit.Create(ES_MULTILINE | ES_NOHIDESEL | ES_READONLY,rect,this,105);
wnd_Edit.ShowWindow(SW_SHOW);
this->Invalidate();
id 105 doesn't exist. (I used it in the Create member function of CEdit). I just put it in there. isn't it supposed to be the id you want to give to the new control ? Should it already exist ?
Check with the following set of flags as the example mentioned in MSDN:
pEdit->Create(ES_MULTILINE | WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER | ES_NOHIDESEL | ES_READONLY,
rect, this, 105);
The Invalidate() is not necessary
Add the WS_VISIBLE flag to your create flags, you don't need the ShowWindow
You are creating the button on the location where IDC_LIST1 is - you probably want to do pWdn->Destroy() after the GetClientRect()
The id you pass to Create() can be anything, of course if you want to handle messages from this button later you'll need to use the correct id. In that case it's easiest to manually add an entry to resource.h.
What do you mean with 'I put this code in the button event handler' - which button? A different one from the one you're trying to create, I may hope? Does your code get called at all, does it stop when you put a breakpoint in? What's the value of wnd_Edit->m_hWnd after the call to Create()?
wnd_Edit is a member of your dialog, right, and not a a function local variable?
What is wnd_Edit exactly? If it's a local variable in that function, that is likely the problem. The CWnd destructor destroys the window associated with the CWnd. So when wnd_Edit goes out of scope, the edit box is destroyed too.
If that's not it, check the return value of Create(). Is it NULL? If it is, check the value of GetLastError().

QWinWidget Inside MFC Dialog Not Repainting or Responding to Tab/Arrow keys

I am using a QWinWidget inside of an MFC dialog and the QWinWidget is not drawing itself correctly and it is not handling keyboard input correctly.
Repainting [Unsolved]
Within the QWinWidget, I have a QTableWidget. When I scroll the QTableWidget, it does not redraw itself until I stop scrolling, at which point it redraws everything. Similarly, I can type into cells in the QTableWidget and the control is not updated until I force it to re-update by scrolling up or down (it re-updates when the scrolling stops).
Since this QWinWidget is housed in an MFC CDialog, I tried overriding the CDialog's OnPaint method and only call the QWinWidget::repaint method, however this has the opposite problem where now only the QWinWidget is updated and the CDialog is never redrawn, resulting in artifacts. If I call QWinWidget::repaint and CDialog::OnPaint, the result is the same as not overriding the OnPaint method. Has anyone ever seen this problem or know how to resolve it?
Keyboard Input [Solved]
None of the controls within the QWinWidget respond to the tab key or arrow keys correctly. The tab/arrow keys simply skip over the entire QWinWidget (and all child controls). Even if I click inside the QWinWidget and select a control, the next time I press the tab key, it skips the focus completely out of the entire QWinWidget.
I noticed that the QWinWidget has two functions, QWinWidget::focusNextPrevChild and QWinWidget::focusInEvent and both of them have a comment header saying "\reimp". Am I supposed to override these functions in order to get correct tab functionality? If so, how can these functions be implemented for correct tab functionality.
I have fixed the keyboard input issue. The QWinWidget class needed some changes:
in the QWinWidget::init method, the WS_TABSTOP must be added to the window style:
SetWindowLong(winId(), GWL_STYLE, WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_TABSTOP);
Also, the QWinWidget::winEvent method needs to respond to the WM_GETDLGCODE to let Windows know that it is interested in receiving key/tab inputs. I had to add this if block:
if(msg->message == WM_GETDLGCODE)
{
*result = DLGC_WANTARROWS | DLGC_WANTTAB;
return(true);
}
I am still working on getting the widget to paint properly.
I don't know about whether you need to reimplement the focusNextPrevChild() and focusInEvent() functions, but I do know that the "\reimp" in the comment header is part of Qt's documentation generation, which merely specifies that the function was a reimplementation of another function in a parent class.
Thanks! It works for me! I have fixed an arrow keys navigation issue for a QTableView inside a QWinWidget.
I am using Qt5.3.0 and qtwinmigrate 2.8.
The QWinWidget::nativeEvent method needs to be modified.
#if QT_VERSION >= 0x050000
bool QWinWidget::nativeEvent(const QByteArray &, void *message, long *result)
#else
...
{
...
if (msg->message == WM_SETFOCUS) {
...
} else if (msg->message == WM_GETDLGCODE) {
*result = DLGC_WANTALLKEYS;
return true;
}
return false;
}
No idea about the keyboard input, but concerning the repainting: have you tried calling QWinWidget::repaint() in the CDialog's OnPaint method AFTER calling the CDialog::OnPaint()?