(My English can be strange o_o)
I work with MFC tabbed MDI, No Document/View
I delete auto-generated View Class, and use CSplitterWnd and my own View Class
"my own View Class" has two child: edit control and my custom control
(edit: I created new tabbed MDI, No Document/View MFC Project and tested that project by spy++. and the following problem occur AGAIN!)
(edit: Use Document/View: AGAIN!)
While I write this program, I found a strange problem:
When I validate window by clicking Aero Peek Thumbnail, my custom control is drawn so many times (WM_PAINT is sent many times)
I figured out CMDIFrameWndEx::OnAfterTaskbarActivate function calls RedrawWindow in that WM_PAINT, and OnAfterTaskbarActivate is callen so many times (31 times, yah)
Could you tell me what makes this problem and how to solve?
I had the same issue as this. My Solution is to override the OnAfterTaskbarActivate
add this to your MessageMap
ON_REGISTERED_MESSAGE(AFX_WM_AFTER_TASKBAR_ACTIVATE, OnAfterTaskbarActivate)
the function is as follows. please note this is exactly the same as the original function without the redraw.
LRESULT CMainFrame::OnAfterTaskbarActivate(WPARAM, LPARAM lp)
{
HWND hwndMDIChild = (HWND)lp;
if (hwndMDIChild != NULL && ::IsWindow(hwndMDIChild))
{
::SetFocus(hwndMDIChild);
}
return 0;
}
I hope this helps. it has resolved my issues.
Related
Create a dialog-based application in Visual Studio 2019. Insert a new dialog in the resources. Place a control (or two) on that dialog that you will later (try to) hook up to a variable (e.g. an edit box to enter your name).
Prepare to add that control variable by creating a class for this second dialog. If you inherit from CDialogEx (or perhaps other classes too - not tested), you can go on to add a variable for the control you added - easy, normal.
But if you had the "tab control" context from the title above in your mind, and chose to inherit from CMFCPropertyPage instead, can you add a variable subsequently? No you cannot - the class added makes no reference to the ID of the dialog resource, and so the Add Variable process has no basis to find a class to add the variable to.
I think this is a bug in Visual Studio... but I'm not 100% sure because I have always found the documentation around property sheets and property pages somewhat confusing. Specifically:
Do the classes implementing the tabs contents on a tab control "contain" the controls "on" the tab... or is the tab control really just a way to provide visual cues to show/hide sets of controls, and all of those controls and associated variables reside in one class?
I believe it's intended to be the former, but I can imagine that one uses a tab control because there are strong similarities between tab contents, and therefore potential benefits in implementing all the control variables in one place so as to avoid duplication. I just wish it was stated explicitly somewhere what the intention was.
Wider context: I'm trying to implement a dialog-based app with a tab control dominating that dialog. MS documentation says to implement a tab control using CPropertySheet and CPropertyPage to implement the tabbed dialog and tab contents. However, there is no (direct) way to create a dialog-based application whose main dialog inherits from CPropertySheet. When you look for examples of tab controls at the application level, you readily find things that deviate from the documented path considerably - using neither CPropertySheet nor CPropertyPage and using the WM_LBUTTONDOWN event instead of TCN_SELCHANGE, both without obvious reasons.
Any tips on (a) how to repair the apparent Visual Studio bug and/or (b) how to inherit from CPropertySheet for my application dialog and/or (c) where to find a clearer and more conventional example of tab control use at the top level would be greatly appreciated.
Given that there appears to be no clear and consistent way of approaching this, if (like me) you think "how hard can it be?", you might naively set out to see if you can create a new dialog-based application based on CDialog and manually convert it to CPropertyPage. You might fare better; I quickly hit a weird problem with the app compiling but not running - it could not load the window caption from the resource ID, despite the caption literally being there in black and white. Due to the weirdness, this was a red flag, and so to my mind problem (b) seems to be not worth the effort.
Without a CPropertySheet at the top level, there is no point having any kind of CPropertyPage or CMFCPropertyPage involved, making problem (a) pointless. That said, if you manually convert from (default) CDialogEx to CPropertyPage for your tab classes, it seems to have no problem compiling and running - the tabs just don't have any functionality that integrates with the main dialog.
Instead, I can now recommend working through the details of the video example even though it does strange things at first glance. You can achieve the desired result in more or less the same way as follows:
Use plain CDialogEx throughout, and so provide all the tab switching/showing/hiding/control work yourself (which the example demonstrates).
Create your main dialog and all tabs in the resource editor, adding the CDialog-based classes from there.
If you create your tab dialog resources via Insert Dialog (generic) instead of Add Resource (uses specific templates) then you will have to manually set certain dialog properties for each tab:
Border: Thin (or I preferred None)
Style: Child
System Menu: false
Title Bar: false
You can simplify the repositioning of the tabs (see below; it barely justifies the separate SetRectangle() function)
You should (probably) drive the tab changing from the TCN_SELCHANGE event instead of WM_LBUTTONDOWN
As noted, repositioning the tabs can be simplified to something like:
CRect tabRect;
m_tabControl.GetWindowRect( &tabRect );
for ( int i = 0; i < m_totalTabs; i++ )
{
m_pTabPage[ i ]->SetWindowPos( &wndTop, tabRect.left, tabRect.top, 0, 0,
i == m_currentTab? SWP_NOSIZE|SWP_SHOWWINDOW : SWP_NOSIZE|SWP_HIDEWINDOW );
}
Additional calls to ShowWindow() for each tab shown in the example are redundant.
One time, I had made on application as such. I don't remember if I used the Visual Studio new project wizard. Most probably, I did. But I will assist you on how to do it if you can not reach how to do it via the assistant.
The essential things:
Define the main dialog in the resources and the respective class in its H and CPP files.
In the YourAppClass::InitInstance you will need to have something like:
YourMainDlgClass dlg;
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: Place code here to handle when the dialog is
// dismissed with OK
}
else if (nResponse == IDCANCEL)
{
// TODO: Place code here to handle when the dialog is
// dismissed with Cancel
}
else if (nResponse == -1)
{
TRACE(traceAppMsg, 0, "Warning: dialog creation failed, so application is terminating unexpectedly.\n");
TRACE(traceAppMsg, 0, "Warning: if you are using MFC controls on the dialog, you cannot #define _AFX_NO_MFC_CONTROLS_IN_DIALOGS.\n");
}
Insert a CMFCTabCtrl element in your dialog for tabs, instead of using a CPropertySheet.
I added a navigation sidebar in my CChildFrame by creating a CSplitterWnd in it and by adding a CScrollView and a CListView as splitter panes based on this answer. Is there a textbook way to do this? I'm asking because I ran into multiple side-effects like the document name no longer showing up in the applications titlebar or mousewheel no longer working for the CScrollView.
I guess, I need to redirect messages arriving in my CChildFrame somehow to make my CScrollView the default receiver for CView-related messages as the CSplitterWnd breaks the flow, because it's not derived from CView. Can someone point me in the right direction?
(Note: This is still done with VS 2008.)
Update: I just created a separate question for the application window title update issue: MFC: After applying a CSplitterWnd to my CChildFrame the main window title isn't updated any more
These code samples are not the most suitable ones, because they create CWnd-derived panes, not CView-derived ones. The views are not in some way associated to the document. You need to call the CDocument::AddView() method, to add every view you created to the document's list of views. The pContext parameter contains a pointer to the document, among other members.
Put the code below in your document class, to verify that your views have been created and associated to the document correctly:
POSITION pos = GetFirstViewPosition();
while (pos)
{
CView *pVw = GetNextView(pos);
AfxMessageBox(typeid(*pVw).name(), MB_OK | MB_ICONINFORMATION);
}
Also in each view class:
CDocument *pDoc = GetDocument();
if (pDoc) AfxMessageBox(pDoc->GetTitle(), MB_OK | MB_ICONINFORMATION);
else AfxMessageBox("The view has no associated Document!");
Note: The default implementation of the CChildFrame class calls CSplitterWnd::Create() rather than CSplitterWnd::CreateStatic() to create the splitter window, and if you move the splitter window to the end it destroys the panes that come off-view. Then if you move it back it creates them again, using the information in the document template or the existing views. You may need to override this behavior too, if you finally make a dynamic splitter window. Better check the MFC source of the version you are using.
There seem to be historical interferences between splitter and view concerning mouse wheel: https://forums.codeguru.com/showthread.php?42826-BUG-CSplitterWnd-Mouse-Wheel
I wont't mark this as solution, as I don't comprehend the backgrounds completely, but it's a workaround for the moment:
BOOL CMyScrollViewDerivedClassInTheStaticSplitter::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
// CSplitterWnd prohibits mousewheel somehow. Doing it explicitely here:
CPoint pos = GetScrollPosition();
pos.y -= zDelta;
ScrollToPosition(pos);
return CScrollView::OnMouseWheel(nFlags, zDelta, pt);
}
I have a CFormView SDI which calls and opens up a CDialog.
In the CDialog I have a button which has a bitmap displayed.
All works well, until I close the CDialog.
When I open up the same CDialog (using create function), the button appears, and the button's functionality is there, however the bitmap disappears.
Can someone please tell me why the bitmap on the button disappears on subsequent calls to CDialog?
Thank you in advance.
Here is the code:
In the CFormView I have a button that creates the CDialog:
void CTest4View::OnButton1()
{
m_dialog_calculator.Create(IDD_DIALOG1, GetDesktopWindow());
m_dialog_calculator.ShowWindow(SW_SHOW);
}
In the CDialog I have the bitmap put on the button in the InitDialog:
BOOL CCalculator::OnInitDialog()
{
CDialog::OnInitDialog();
if(!bitmapNew.LoadBitmap(IDB_BITMAP_NEW)){
MessageBox("problem in loadbitmap");
}
if(!m_button.SetBitmap(bitmapNew)){
MessageBox("problem in SetBitmap");
}
bitmapOpen.LoadBitmap(IDB_BITMAP_OPEN);
m_buttonOpen.SetBitmap(bitmapOpen);
//==========================
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
Upon further investigation, the problem seems to be in: m_button.SetBitmap(bitmapNew) since this returns FALSE. Can someone please help me?
Quick fix:
void CTest4View::OnButton1()
{
// only create the dialog once
if (m_dialog_calculator.m_hWnd==NULL)
m_dialog_calculator.Create(IDD_DIALOG1, GetDesktopWindow());
m_dialog_calculator.ShowWindow(SW_SHOW);
}
Additional information 1:
The information that OP provided in his question/code is quite little, so I actually have to recreate a similar test project to guess what is wrong with the missing bitmap. What I found is that the CDialog and CBitmap is being created multiple times when the button is pressed, this causes subsequent creation api call to fail, other than the first creation call. The result is some unexpected behavior as you can see now.
The program is supposed to generate some assertion errors when run in debug mode due to the creation failure but I guess the OP compiled it in release mode so all warnings are being suppressed.
The problem occurred because the calculator dialog is created as a modeless dialog as compared to a normal DoModal way of activation. The usual way of doing such modeless dialog is to create the dialog only once, by monitoring the m_hWnd member variable. When the user want the dialog to be dismissed, it is simply being hidden instead of being destroyed. This will avoid all the multiple creation problems altogether.
I guess presently, the calculator dialog is assumed to be closed and destroy by clicking the "X" button on the top right of dialog, well, actually it is only hidden but not actually being destroyed by the default handling of CDialog. The correct way to dismiss the modeless calculator dialog is thus to overide the OnClose event to hide it using ShowWindow(SW_HIDE). To activate it again, use ShowWindow(SW_SHOWNORMAL).
I have "n" dialogs which have the same base dialog. Each dialog has its own controls
edit boxes
combo boxes
list controls
etc.
In base dialog, how do I set focus messages of each control and,for example, give a Message box with
text("Hello I got focus, my ID is %d")?
The easiest way is using the classical subclassing method. The problem is that WM_SETFOCUS is not pumped through the message Loop, so PreTranslateMessage will not help.
Thee are some nice classes that help to do additional subclassing without disturbing the MFC stuff.
Paul Di Lascia wrote CSubclassWnd. PJ Naughter wrote CHookWnd. And with the ATL has CWindowsImpl.
All this classes allow easy additional subclassing even if a window is already subclassed by the MFC.
You can use "standard subclassing" GetWindowLong/SetWindowLong too.
According to this SO article, you can hook the WM_SETFOCUS message.
You can get the Control ID by using GetDlgCtrlID with the hwnd returned by the hook.
But beware of popping up a MessageBox, that will change the focus and trigger your hook proc, making it go into a loop!
As Jerry already said make a hook, get parent window handler via GetParent() and SendMessage(hParentWND, WM_MESSAGE, lParam, wParam).
Of course, you should handle WM_MESSAGE in your parent window.
Btw, framework calls OnSetFocus function when window gained focus.
Updated: please see this other thread instead, all this COM stuff is not part of the problem.
One of our apps has a COM interface which will launch a dialog, e.g:
STDMETHODIMP CSomeClass::LaunchDialog(BSTR TextToDisplay)
{
CDialog *pDlg = new CSomeDialog(TextToDisplay);
pDlg->BringWindowToTop();
}
For some reason when the COM method is called several times at once by the server, we get odd behaviour:
We get multiple dialogs, but only one entry in the taskbar
Dialog Z-order is based on order created and can't be changed... the first dialog created is always shown under the 2nd one, 2nd under 3rd, etc, even when you drag them around
if N dialogs were created, closing one of them closes it and all the others created afterwards. e.g if 5 dialogsa re created and you close the 3rd one, #3,#4,#5 all get closed.
It's somehow like the dialogs are siblings but I don't see anything weird going on. Is it perhaps due to COM, or is this a weird MFC/Win32 issue?
EDIT: If the interface method is called several times separately, it works as expected. Only when the server component sends several through at once does it seem to mess up. Could threading/timings be to blame?
EDIT2:
I put this logging in:
std::stringstream ss;
HWND self = dlg->m_hWnd;
HWND parent = dlg->GetParent() ? dlg->GetParent()->m_hWnd : 0;
ss<<"Dlg created'. HWND = "<<self<<", Parent = "<<parent<<std::endl;
OutputDebugString(ss.str().c_str());
It gave:
Dlg created. HWND = 0013014A, Parent = 00000000
Dlg created. HWND = 001B0390, Parent = 0013014A
Dlg created. HWND = 000B03B0, Parent = 001B0390
So clearly the problem is the dialogs are being made children of each other. But the question is, WHY?! It seems Windows is doing this automatically...
This question seems to be slightly away from the main issue of parenting, so I have tried to separate out the main issue into a new question.
It sounds like the first dialog has been set as the owner of the second, and the second as the owner of the third. Can you change the dialog initialization to explicitly specify the owner window? Is there a window that makes sense to assign? Perhaps the Desktop window, if they're all intended to be top-level?
If you want to be able to access all three (or more), then they would need to be modeless. Try using Create(CSomeClass::IDD, CWnd::GetDesktopWindow()), and you ought to see sibling dialogs, all of which show up on the taskbar.