MFC: After applying a CSplitterWnd to my CChildFrame the main window title isn't updated any more - mfc

Still using VS 2008, I have split my MDI view class in my CChildFrame to facilitate a navigation sidebar (a CListCtrl) next to my old CScrollView using a static splitter (source code). This however implied two side effects: Beside the mouse wheel no longer working (where I found a workaround for), the application window is no longer updated on SetPathName(). What do I need to do to bridge the splitter so the framework updates the application window again based on my CDocument?

On your MainFrame class, just put a method
void CMyFrame::OnUpdateFrameTitle(BOOL bAddToTitle)
{
__super::OnUpdateFrameTitle(bAddToTitle);
}
and a breakpoint there, to see what happens. If needed, you will have to provide your own override instead of calling the __super implementation.

Related

MFC dialog-based app with tab control - Visual Studio bugs/restrictions make recommended path impossible?

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.

MFC Custom Control Not Appearing On Dialog

Using Visual Studio 2013, I created a dialog resource using the resource editor. It is a child control with no border and is just a collection of radio buttons, push buttons, and static text. I want to turn this into a custom control in order to place this in several different locations. Let's call this a "Panel".
I then created a regular dialog and using the Toolbox "Custom Control", defined an area for the Panel. The Panel registers itself and has a valid window handle.
I used the following example:
https://www.codeproject.com/Articles/521/Creating-Custom-Controls
The parent's DDX gets hit and the _panel is properly instantiated:
MyDialog::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX)
DDX_Control(pDX, IDC_CUSTOM_PANEL, _panel)
}
I read that I need to override the OnPaint() and OnEraseBkgnd(CDC* pDC) methods so the Panel class has these but they are empty. I do not have any custom painting to do as the Panel contains nothing but regular buttons.
What do I have to include in OnPaint()?
I also noticed that none of the member buttons are instantiated in the Panel like would normally happen in a dialog's DoDataExchange method. Instead, I've had to resort to dynamically creating each of the control's inside the Panel's PreSubclassWindow() method:
void MyPanel:PreSubclassWindow()
{
_groupBox.Create(_T("Options"), WS_CHILD|WS_VISIBLE|BS_GROUPBOX, CRect(11, 11, 112, 231), this, IDC_STATIC_GROUPBOX);
//... do this for every dialog element??? seems like overkill...
CWnd::PreSubclassWindow()
}
Why do I need to do this when I've already defined/designed the Panel and each of its controls in the resource editor?
If I do not do this in the PreSubclassWindow method, nothing will appear on the dialog.
Any help is appreciated. Thanks.
The article says override OnPaint and OnEraseBkgnd if you want to change the functionality. It doesn't say you have to override always.
Just remove ON_WM_PAINT and ON_WM_ERASEBKGND, remove OnPaint and OnEraseBkgnd if you don't need them. Or call the base class implementations if you are not making any changes:
void MyPanel::OnPaint() { CWnd::OnPaint(); }
BOOL MyPanel::OnEraseBkgnd(CDC* pDC) { return CWnd::OnEraseBkgnd(pDC); }
This will show a blank control with nothing in it, unless you add a child window to _panel as you have done in MyPanel:PreSubclassWindow
You are adding _groupBox to _panel. And you are adding _panel to the MyDialog.
MyDialog::DoDataExchange(...){DDX_Control(pDX, IDC_CUSTOM_PANEL, _panel)} is needed to invoke SubclassWindow for _panel. That in turn calls _groupBox.Create.
If MyPanel::OnPaint and MyPanel::PreSubclassWindow are not doing anything MyPanel appears as a blank control.
... do this for every dialog element??? seems like overkill...
You can directly add _groupBox to the main dialog. But if you want to add specific controls within MyPanel then you have to do it manually.
You can also create a child dialog within a main dialog. For example that's how a tab control works.

Create a property sheet using a dialog template

I need to extend an existing MFC app with a UI that will end up very cluttered unless I use a tab control. However, the nature of the UI is that there are some controls that are global, and only some that can be localised to a particular tab.
The standard use of tab controls (CPropertySheet + CPropertyPage) more or less expects there only to be CPropertyPage instances (tabs) visible on the CPropertySheet object, and nothing else. There is a Microsoft Example Project that shows a single additional window painted outside the area occupied by the tab control... but it's not immediately clear how it is created/drawn/handled, and it is only one single additional window that generates few events (I guess it is painted, so there must be a WM_PAINT event handler lurking somewhere).
Is it possible to lay out a bunch of controls with the MS Dialog Editor, including a tab control, and create the CPropertySheet using that template, hook up event handlers in a nice way, etc... or some equivalent way of getting the MFC framework to do as much of the creating, drawing and event handling as possible when it comes to a situation like this?
Yes it is possible to create dialog templates and use them in a CPropertyPage.
Each CPropertyPage behaves nearly like a dialog and handles all events for the controls on it.
Also there are features like OnApply that help you to manage the data exchange between the controls and your internal storage.
The central CPropertySheet only creates the dialog that get active. So OnInintDialog for a page is called the first time when the page gets active.
In the MFC since 2010 are more possibilities than a CPropertySheet. You can create tabbed views, that again may be CFormViews. I don't like CDialog based applications so I would prefer a tabbed view in a standard frame with toolbar and menus if appropriate for the application. So another method to unclutter your UI is to choose the MDI interface with tabbed documents... but multiple documents maybe isn't what you want.
Here is a sample of an SDI application with multiple tabbed views.
Also Coeproject shows some more samples here and with splitters and tabs here.
There are at least three solutions paths:
Try to squeeze the situation into the CPropertySheet + CPropertyPage framework which does not naturally allow for additional dialog controls on the CPropertySheet object, and so you will get no framework support this
Place a tab control on an ordinary dialog, and then use the TCN_SELCHANGE messages to fire code that manually hides & shows individual dialog controls subject to the tab control (again no framework support, but this time "within" the tab control instead of outside it)
Follow Mark Ransom's observation that if you can embed one kind of CWnd-based control on a CPropertySheet then you can probably embed any such object, including a CDialog-based object that has been developed in the MFC Dialog Editor
All of these approaches are going to encounter challenges, and it will depend on the specifics of the situation as to which is better. But first you should really consider whether there is a cleaner UI design which would lend itself to a simpler solution.
In my specific case, I saw no cleaner design alternatives, and found it easiest to take the second approach. It left me with some reasonably simple calls to ShowWindow() to show/hide the controls inside the tab control.

Is it possible to change the location of the main top-level menu in an MFC MDI app?

I have an app with a customized frame (i.e., caption/titlebar, borders). I customized the frame by removing the WS_CAPTION style, and overriding OnNcCalcSize to reserve a custom-sized area for the caption, which the app paints in OnNcPaint.
A side effect is that the menu bar no longer displays, which is OK because I want to customize the appearance & location of the main menu. Unfortunately, I don't know how to do that. I would like the "File" menu to be further from the left edge than it is with the standard menu bar.
An acceptable alternative would be to remove the menu entirely, and use OnNcHitTest, OnNcPaint and OnNcMouseMove to manage a hand-coded replacement for the top-level menu.
I've never seen a way to modify the looks of the standard menu. You can remove it (remove the creation code from your InitInstance) and then code a replacement. I wouldn't do that in OnNcXXX though, rather make a new custom control that you position at the top. Or you could have a look at the MFC Next themed menus and write a custom UI renderer. It may be enough to override a few functions left and ride to set the alignment of the menu.
It's an MFC MDI app -- InitInstance is not where the menu is being created. I have tried to get rid of the menu (and succeeded, temporarily) but the MFC framework seems to be "putting it back" -- I think the MDI model has the child windows modify the menu, but the code that does that seems to be buried in the framework somewhere (or else I just haven't been able to find exactly where it happens in the application's code, but I suspect it's happening inside the MFC framework code). If I knew where, I might be able to override the methods(s) and take control... I think.
What would be the reason to write a custom control vs. handling OnNcMouseMove & OnNcLeftButtonDown, e.g.? I don't expect to need the functionality in any other app (a new app would be coded in C#, probably), so I'm looking for ease of implementation, not code reuse.

How to create and add a custom made component to a Dialog based app (MFC)?

I want to make a custom made component (a line chart), that would be used in other applications.
I don't know 2 things:
Where should I use (within component class!) the methods for drawing, like FillRect
or PolyLine? In OnPaint handler that I should define and map it in MESSAGE MAP? Will
it (OnPaint handler) be called from OnPaint handler of the dialog of the application
or where from?
How to connect the component, once it is made, to the test application, which will
for example be dialog based? Where should I instantiate that component? From an
OnCreate method of the MyAppDialog.cpp?
I started coding in MFC few days ago and I'm so confused about it.
Thanks in advance,
Cheers.
Painting the control is handled exactly like it would be if it wasn't a control. Given that you're using MFC, that (at least normally) means you do the drawing in the View class' OnDraw (MFC normally handles OnPaint internally, so you rarely touch it).
Inserting the resulting ActiveX control in the host application will be done like inserting any other ActiveX control. Assuming you're doing your development in Visual Studio, you'll normally do that by opening the dialog, right clicking inside the dialog box, and clicking "Insert ActiveX Control..." in the menu that pops up. Pick your control from the list, and it'll generate a wrapper class for the control and code to create an object of that class as needed. From the viewpoint of the dialog code, it's just there, and you can use it about like any other control.
For create new component in MFC, you must create a class from the window class (CWND),
after that you can have your MessageMap for the component and your methods and also can override CWND::OnDraw method to draw the thing you want.
Before that I suggest you to take a look to device context
http://msdn.microsoft.com/en-us/library/azz5wt61(VS.80).aspx
Good Luck friend.