C++ MFC MDI data as Document not on disk [closed] - c++

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
I would like to use MFC MDI but my data (Document) is not on the disk.
I have functions that generate data according to the input.
Data of type A, type B etc.
I would like to open a new tab for every new data generated.
For example, if the input of the generated data is X, I would like to open a new tab with the name X on it. If the X exists, I would like to activate (bring front) the X tab as the MDI already does.
My idea is to have multiple CMultiDocTemplate. Each one generated by different CDocument/CView derived class.
I am looking for a solution to overload the open/new function so instead of opening a file from disk, ask from my functions to generate the data (CDocument).
Also, I don't want the user to choose between CMultiDocTemplate/Cview but I want to choose which one to use for the new tab.

I am not sure if this is what you might be looking for, but I used code like this to change the view to a different one based on the document being loaded:
BOOL CCommunityTalksDoc::SwitchToView(CRuntimeClass* pNewViewClass)
{
POSITION rPos;
CView *pOldActiveView;
CFrameWnd *pChild;
CCreateContext context;
BOOL bAutoDelete;
rPos = GetFirstViewPosition();
pOldActiveView = GetNextView(rPos);
pChild = pOldActiveView->GetParentFrame();
// If we're already displaying this kind of view, no need to go further.
if (pOldActiveView->IsKindOf(pNewViewClass))
return TRUE;
// Set flag so that document will not be deleted when view is destroyed.
bAutoDelete = m_bAutoDelete;
m_bAutoDelete = FALSE;
// Delete existing view
pOldActiveView->DestroyWindow();
// restore flag
m_bAutoDelete = bAutoDelete;
// Create new view.
m_pScriptView = (CScriptParseView*)pNewViewClass->CreateObject();
if (m_pScriptView == nullptr)
{
TRACE1("Warning: Dynamic create of view type %s failed\n", pNewViewClass->m_lpszClassName);
return FALSE;
}
// we must ensure the popup dialogues don't display
m_pScriptView->SetBuildMode(FALSE);
// Draw new view.
context.m_pNewViewClass = pNewViewClass;
context.m_pCurrentDoc = this;
context.m_pNewDocTemplate = nullptr;
context.m_pLastView = nullptr;
context.m_pCurrentFrame = pChild;
if (!m_pScriptView->Create(nullptr, nullptr, AFX_WS_DEFAULT_VIEW, CRect(0, 0, 0, 0),
pChild, AFX_IDW_PANE_FIRST, &context))
{
TRACE0("Warning: couldn't create view for frame\n");
delete m_pScriptView;
m_pScriptView = nullptr;
return FALSE;
}
m_pScriptView->SendMessage(WM_INITIALUPDATE, 0, 0); // WM_INITIALUPDATE is defined in afxpriv.h
pChild->RecalcLayout();
m_pScriptView->UpdateWindow();
pChild->SetActiveView(m_pScriptView);
return TRUE;
}
You use it like:
CRuntimeClass *pNewViewClass = RUNTIME_CLASS(CScriptParseView);
if (!SwitchToView(pNewViewClass))
{
// fail, don't know why it would fail
ASSERT(FALSE);
}

Related

Can't assign the landscape LayerInfo object to a landscape

I've been developing an automated system that requires me to generate the LayerInfo objects of a given Landscape. So far I've been able to generate the required LayerInfo object's, but I wasn't able to assign them, or to be more precise, when I try to assign the necessary layers in the ULandscapeInfo property they appear in the editor but are not assigned, unless I save them and restart the engine, they also appear weird in the editor, as you can see in the image. I think it's a similar problem to the one in this thread. The code bellow it's responsible for assignig the LayerInfo:
// This method creates a
// new layer info object
UWorld * currentWorld = GetWorld();
ULevel * level = currentWorld->GetLevel(0); // TODO : just for debuging
ULandscapeLayerInfoObject* layerInfo = Landscape->CreateLayerInfo(*(layerName), level);
bool isDirty = layerInfo->MarkPackageDirty();
// Get a reference for the landscape Info
ULandscapeInfo* landscapeInfo = Landscape->GetLandscapeInfo();
landscapeInfo->CreateLayerEditorSettingsFor(layerInfo);
FLandscapeInfoLayerSettings* layerSettings = &landscapeInfo->Layers[1];
landscapeInfo->Layers[0] = FLandscapeInfoLayerSettings(layerInfo, Landscape);
UEditorAssetLibrary::SaveAsset(assetName, false);
// Assign the respective values
if (!layerSettings->LayerInfoObj)
{
layerSettings->Owner = Landscape;
layerSettings->LayerInfoObj = layerInfo;
layerSettings->bValid = true;
}
Editor Screenshot

MFC MDI CtabView hidden / remove navigation controls that are not used

I have 4 buttons that have no direct application to my program. I would like to hide or remove them. I've searched to find out what they are called...they look like navigation, left/right arrows, next/previous. I can't seem to find what they are called. I have looked on the Microsoft site and looking at the CTabView members doesn't seem to jump out and say "hey, these are what you're looking for"....
I'm hoping it is a relatively easy task. Anyone know how to just "turn them off"?
Thanks.
UPDATE:
I was able to resolve the C4430 mentioned by moving the new function before the existing in OutputWnd.h and that solved the issue in the output pane area where the arrows are now gone. What I forgot to mention is that I have another mechanism AddView that is creating 2 runtime classes and putting those into a tabbed document view. This time, I've been using GetTabControl() to make it pretty, but this also suffers from the same issue, the scroll arrows are now present.
This is what the code looks like that creates the 2 new tabs:
Above this code is the constructor/destructor/headers, nothing else.
IMPLEMENT_DYNCREATE(CTrackView, CTabView)
void CTrackView::OnInitialUpdate()
{
// add document views
AddView(RUNTIME_CLASS(CTrainView), AfxStringID(IDS_TRAIN));
AddView(RUNTIME_CLASS(CStationView), AfxStringID(IDS_STATION));
GetTabControl().EnableTabSwap(TRUE);
GetTabControl().SetLocation(CMFCBaseTabCtrl::Location::LOCATION_BOTTOM);
GetTabControl().ModifyTabStyle(CMFCTabCtrl::STYLE_3D);
GetTabControl().SetActiveTabBoldFont(TRUE);
CTabView::OnInitialUpdate();
}
I have tried to comment out CTabView::OnInitialUpdate(); but I know it won't and didn't affect the arrows. I done some searching on the control and I don't see any examples to remove the arrows, I assume another override is in order. I will follow the method shown in the other example, I am looking to see if it can be fixed similar to what has been shown.
Do these share the same CMFCTabCtrl mechanism or is this something different?
Update 2:
The OutputWnd.cpp that the MFC wizard creates has the same issue now that I've modified the tab control, I can't find the right pointer to fix the issue where &tabCtrl()is an unknown identifier. EDIT: This was the issue, I found the fix, see update 3 to see the resolution.
int COutputWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CDockablePane::OnCreate(lpCreateStruct) == -1)
return -1;
CRect rectDummy;
rectDummy.SetRectEmpty();
// Create User Define tab style:
int UserTabStyle = AfxGetApp()->GetProfileInt(_T("Settings"), _T("UserTabStyle"), 0); //Get value from registry
// If the key doesn't exist, UserTableStyle will be 0 or FALSE;
if (UserTabStyle != FALSE && UserTabStyle <= 8) { // User selected tab style type
int EnumUserTabStyle = UserTabStyle - 1; // Fix enum if key doesn't exist.
if (!m_wndTabs.Create(static_cast<CMFCTabCtrl::Style>(EnumUserTabStyle), rectDummy, this, 1))
{
TRACE0("Failed to create output tab window\n");
return -1; // fail to create
}
}
else { // Default tabs style if Reg key does not exist i.e. new install/program reset
if (!m_wndTabs.Create(CMFCTabCtrl::STYLE_FLAT, rectDummy, this, 1))
{
TRACE0("Failed to create output tab window\n");
return -1; // fail to create
}
}
// Nicely hack to access protected member
class CMFCTabCtrlEx : public CMFCTabCtrl
{
public:
void SetDisableScroll() { m_bScroll = FALSE; }
};
// One-Liner to Disable navigation control
((CMFCTabCtrlEx*)&tabCtrl())->SetDisableScroll();
// Create output panes:
const DWORD dwStyle = LBS_NOINTEGRALHEIGHT | WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL | WS_BORDER;
if (!m_wndOutputBuild.Create(dwStyle, rectDummy, &m_wndTabs, 2) ||
!m_wndOutputDebug.Create(dwStyle, rectDummy, &m_wndTabs, 3))
{
TRACE0("Failed to create output windows\n");
return -1; // fail to create
}
UpdateFonts();
CString strTabName;
BOOL bNameValid;
//Attach list windows to tab:
bNameValid = strTabName.LoadString(IDS_STATUS_TAB);
ASSERT(bNameValid);
m_wndTabs.AddTab(&m_wndOutputBuild, strTabName, (UINT)0);
bNameValid = strTabName.LoadString(IDS_DEBUG_TAB);
ASSERT(bNameValid);
m_wndTabs.AddTab(&m_wndOutputDebug, strTabName, (UINT)1);
int EnableDebugTab = AfxGetApp()->GetProfileInt(_T("Settings"), _T("EnableDebugTab"), 0); //Get value from registry
if (EnableDebugTab == FALSE)
{
OnOutputtabsDebug(); //Check to see if it should be enabled
}
return 0;
}
Update 3:
I found the answer to my unknown identifier, I was trying to call a function that didn't exist. Reformed as such and it works as expected:
// Nicely hack to access protected member
class CMFCTabCtrlEx : public CMFCTabCtrl
{
public:
void SetDisableScroll() { m_bScroll = FALSE; }
};
// One-Liner to Disable navigation control
((CMFCTabCtrlEx*)&m_wndTabs)->SetDisableScroll();
There is no public, documented API to hide those navigation buttons. However, digging into the MFC sources finds that:
the arrows are CMFCTabButton m_btnScrollLeft, m_btnScrollRight, m_btnScrollFirst, m_btnScrollLast; in CMFCTabCtrl;
they are created in CMFCTabCtrl::OnCreate whenever CMFCTabCtrl::m_bScroll is TRUE;
CMFCTabCtrl::m_bScroll is set to TRUE in CMFCTabCtrl::Create if any of the styles STYLE_FLAT, STYLE_FLAT_SHARED_HORZ_SCROLL, STYLE_3D_SCROLLED, STYLE_3D_ONENOTE, STYLE_3D_VS2005, or STYLE_3D_ROUNDED_SCROLL is used.
With that insight, the navigation buttons can be turned off in a couple of ways.
By the book: create the CMFCTabCtrl without any of the styles that enable those buttons, which leaves only STYLE_3D available. That loses the navigation buttons, indeed, but the styling of the tab control also becomes different, and the tabs themselves display as small rectangles rather than trapezoids.
Undocumented: override the CMFCTabCtrl::m_bScroll and set it to FALSE before the tab window gets created. For example, derive a class CMyTabCtrl to clear m_bScroll.
class CMyTabCtrl : public CMFCTabCtrl
{
protected:
BOOL PreCreateWindow(CREATESTRUCT &cs) override
{
m_bScroll = FALSE;
return CMFCTabCtrl::PreCreateWindow(cs);
}
};
Then instantiate the control as CMyTabCtrl m_wndTabs; instead of CMFCTabCtrl m_wndTabs;.
I remembered I solved this in the past with an One-liner.
Here a solution (hack) to direct access the m_bScroll Variable in your CTrackView::::OnCreate() function. OnInitialUpdate() comes too late as dxiv explained.
int void CTrackView::::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CTabView::OnCreate(lpCreateStruct) == -1)
return -1;
// add document views
AddView(RUNTIME_CLASS(CTrainView), AfxStringID(IDS_TRAIN));
AddView(RUNTIME_CLASS(CStationView), AfxStringID(IDS_STATION));
:
:
// Nicely hack to access protected member
class CMFCTabCtrlEx : public CMFCTabCtrl
{
public:
void SetDisableScroll() { m_bScroll = FALSE; }
};
// One-Liner to Disable navigation control
((CMFCTabCtrlEx*)&GetTabControl())->SetDisableScroll();
}

When exactly are published properties assigned at run time?

I have a custom control which needs to do some things when loading at run time, based on a published property. However, I am running into a problem where, whenever I check the published property, it has not been set yet, and is always the default value.
I first attempted checking the property in the constructor, of the control, but quickly found they were not loaded yet. I know that when the control is shown on the screen the properties are set correctly, thus its not an issue with the properties not being loaded at all.
I next attempted overriding the Loaded Method but am still having the same problem, so I don't think this is exactly what I am looking for.
void __fastcall TFmSearchBar::Loaded()
{
TEdit::Loaded(); //call base class loaded
if( MyProperty )
{
//do stuff
}
}
At what point are these published properties actually getting set?
What method can/should I hook into in order to execute some logic in my control based on these properties, as soon as the properties are set correctly?
If I check the property in the constructor of the control, the property is always the default value even if I have specified otherwise in the designer.
Correct, because its design-time values have not been assigned yet.
At what point are these published properties actually getting set?
When the Owner (Form, Frame, or DataModule) is being constructed. It loads its own DFM resource and parses it, constructing stored child components and reading their property values.
For example, say you have the following DFM:
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
...
object Edit1: TEdit
Left = 136
Top = 64
Width = 121
Height = 21
TabOrder = 0
end
object Button1: TButton
Left = 263
Top = 62
Width = 75
Height = 25
Caption = 'Button1'
TabOrder = 1
end
end
The DFM streaming process roughly translates to the following equivalent code (I'm leaving a lot of internal details out for simplicity):
__fastcall TCustomForm::TCustomForm(TComponent *Owner)
: TScrollingWinControl(Owner)
{
this->FFormState << fsCreating;
try
{
// locate, load, and parse the "Form1" DFM resource ...
this->FComponentState << csLoading;
this->Parent = ...;
this->Name = L"Form1":
this->FComponentState << csReading;
this->Left = 0;
this->Top = 0;
this->Caption = L"Form1";
...
TEdit *e = new TEdit(this);
try
{
e->FComponentState << csLoading;
e->Parent = this;
e->Name = L"Edit1"; // <-- sets the derived Form's 'Edit1' member to this object
e->FComponentState << csReading;
e->Left = 136;
e->Top = 64;
e->Width = 121;
e->Height = 21;
e->TabOrder = 0;
e->FComponentState >> csReading;
}
catch (...)
{
delete e;
throw;
}
TButton *b = new TButton(this);
try
{
b->FComponentState << csLoading;
b->Parent = this;
b->Name = L"Button1"; // <-- sets the derived Form's 'Button1' member to this object
b->FComponentState << csReading;
b->Left = 263;
b->Top = 62;
b->Width = 75;
b->Height = 25;
b->Caption = L"Button1";
b->TabOrder = 1;
b->FComponentState >> csReading;
}
catch (...)
{
delete b;
throw;
}
this->FComponentState >> csReading;
...
e->Loaded();
b->Loaded();
this->Loaded();
}
__finally
{
this->FFormState >> fsCreating;
}
}
So, as you can see, a component's property values are not available yet when its constructor is called.
What method can/should I hook into in order to execute some logic in my control based on these properties, as soon as the properties are set correctly?
That depends on what the properties need to do. If they need to perform operations immediately, you can do that directly in their property setters. But if they need to wait until other properties have been loaded first (if one property is dependent on the value of another property), then override the virtual Loaded() method instead, which is automatically called after DFM streaming is finished. Property setters can check the flags of the ComponentState property to know whether or not the component is currently running in the Form Designer at design-time, whether or not a DFM is currently being streamed, etc and then act accordingly as needed.
I attempted overriding the Loaded Method but am still having the same problem
Which is what exactly? You did not explain what your actual problem is. Please edit your question to provide those details.
so I don't think this is exactly what I am looking for.
It most likely is, you probably are just not using it correctly.

How to persist document tab order when using EnableMDITabbedGroups in C++ MFC

VS2010 with an MDI document layout using tabs along the top to switch between documents. Each document is a "live" view into a database, where the persistent data per document is a group of configuration settings.
We would like to allow the user to rearrange the tabs (this functionality is built in), but need to persist this new order. Right now it appears the document z-order is not affected by moving the tabs around. when closing the app, the documents close in the order they were opened so this is not helpful in determining the final tab order on close.
We are using the EnableMDITabbedGroups(TRUE, mdiTabParams) with m_bEnableTabSwap = TRUE which is the default.
Thanks! Ended up with the following solution in the MainFrame::OnClose() method.
Note that this code example uses two custom classes of 1) CSpectraAnalysisUtilityView which inherits from CView and 2) CReviewDataFolder which is our object that we needed to update the recent Tab Order.
This code solution also implements the GetMDITabGroups in case there are multiple group windows open.
void CMainFrame::OnClose()
{
iReviewDataFolderOrder = 1;
const CObList& tabGroups =m_wndClientArea.GetMDITabGroups();
if (0 < tabGroups.GetCount())
{
POSITION pos = tabGroups.GetHeadPosition();
CMFCTabCtrl* pCrtTabCtrl;
while(pos != NULL)
{
pCrtTabCtrl=DYNAMIC_DOWNCAST(CMFCTabCtrl, tabGroups.GetNext(pos));
int count = pCrtTabCtrl->GetTabsNum();
for(int i = 0; i < count; i++)
{
CWnd* pWnd = pCrtTabCtrl->GetTabWndNoWrapper(i);
CMDIChildWnd *pChild = ((CMDIChildWnd*)(pWnd));
if (pChild)
{
CView *pView = pChild->GetActiveView();
if (pView)
{
if (pView->IsKindOf(RUNTIME_CLASS(CSpectraAnalysisUtilityView)))
{
CSpectraAnalysisUtilityView* specUtilView;
specUtilView = (CSpectraAnalysisUtilityView*)pView;
CReviewDataFolder* pDataFolder = specUtilView->GetSpecReviewDataFolder();
if(pDataFolder)
{
pDataFolder->SetRecentOrder(iReviewDataFolderOrder);
iReviewDataFolderOrder++;
}
}
}
}
}
}
}
CMDIFrameWnd::OnClose();
}
Upon destruction of the outer main frame (OnDestroy) you can access the the CMFCTabCtrl members and can loop over each tab and determine the current sequence stored in the tab. GetTabWnd will allow you to access each tab by its index.
To access the tab control use CMDIClientAreaWnd::GetMDITab.

MFC - Printing - Changing page orientation from a custom pagesetup dialog

I am developing a custom print dialog and page setup using MFC and VS2008 for my Win32 program. Since the code is legacy, I can't take much advantage from MFC view/doc architecture. As a result, I wrote a printing code completely from scratch.
I setup CPrintInfo, instantiate my custom print dialog box and hook this dialog box to the CPrintInfo I just created. When my custom print dialog is up, I have a radio button to let a user toggles the page orientation. For some reasons, I couldn't modify the current DEVMODE at the run-time. As a result, every page I print will end up as a portrait.
Even if I manually set pDevMode->dmOrientation to DMORIENT_LANDSCAPE from the event handler of the custom print dialog, the printing result is still ended up as portrait. I am really not sure why this is happening and how to modify the DevMode after the print dialog is up.
Thank you in advance for any help.
Here is the code I have:
void PrintSomething(CWnd* currentWnd) {
// Create CPrintInfo
CPrintInfo* pPrintInfo = new CPrintInfo;
SetupPrintInfo(pPrintInfo); // simply setup some member variables of CPrintInfo
// Create a custom print dialog
CustomPrintDlg* pCustomPrtDlg = new CustomPrintDlg(FALSE, PD_ALLPAGES | PD_USEDEVMODECOPIES | PD_NOPAGENUMS
| PD_HIDEPRINTTOFILE | PD_NOSELECTION, pPrintInfo, currentWnd);
SetupPrintDialog(pPrintInfo,pCustomPrtDlg);
if ( AfxGetApp()->DoPrintDialog(pCustomPrtDlg) == IDOK ) {
... // proceed a print loop
}
}
Code for setting up the custom print dialog:
void SetupPrintDialog(CPrintInfo* pPrintInfo,CustomPrintDlg* pCustomPrtDlg) {
delete pInfo->m_pPD;
pInfo->m_pPD = pCustomPrtDlg;
pInfo->m_pPD->m_pd.hInstance = AfxGetInstanceHandle();
pInfo->m_pPD->m_pd.lpPrintTemplateName = MAKEINTRESOURCE(IDD_CUSTOM_PRTDLG);
// Set the Flags of the PRINTDLG structure as shown, else the
// changes will have no effect.
pInfo>m_pPD->m_pd.Flags |= PD_ENABLEPRINTTEMPLATE;
// Set the page range.
pInfo>m_pPD->m_pd.nMinPage = 1; // one based page numbers.
pInfo>m_pPD->m_pd.nMaxPage = 0xffff; // how many pages is unknown.
}
When a user toggles the radio button to Landscape, this function will be invoked:
void CustomPrintDlg::OnLandscapeChecked() {
// set the current Devmode to landscape
LPDEVMODE pDevMode = GetDevMode();
GlobalUnlock(pDevMode);
pDevMode->dmOrientation = DMORIENT_LANDSCAPE;
}
A pseucode for the custom print dialog class:
class CustomPrintDlg: public CPrintDialog {
... // just override some methods from CPrintDialog
};
Thanks again,
Unagi
I figured out the solution:
All I need is to call GlobalLock to obtain a pointer to the Devmode before changing the current DevMode.
void CustomPrintDlg::OnLandscapeChecked()
{
// set the current Devmode to landscape
LPDEVMODE pDevMode = GetDevMode();
GlobalLock(pDevMode);
pDevMode->dmOrientation = DMORIENT_LANDSCAPE;
GlobalUnlock(pDevMode)
}
Thanks again for helping me.
Nowhere in your example code do you show how you're creating the DC for printing. When you call CreateDC, you must pass a pointer to a DEVMODE structure; this defines whether the printing will be portrait or landscape.