Set the size of a window in MFC application - c++

This question may be considered as naive for many users but I am new to MFC and I need an indication. I want to set a specific size to a SDI window but I fail to do it. I try the code in the CMainFrame class:
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if( !CFrameWndEx::PreCreateWindow(cs) )
return FALSE;
cs.style = WS_OVERLAPPED | WS_SYSMENU | WS_BORDER;
cs.cy = 600;
cs.cx = 600;
cs.y = ((cs.cy * 3) - cs.cy) / 2;
cs.x = ((cs.cx * 3) - cs.cx) / 2;
return CFrameWnd::PreCreateWindow(cs);
}
The window still keeps its size though without any change.
As far as I am concerned, I can override the respective function in CWnd class to get to the same result. Given that I have not used the CWnd class till now in my program, how can I do that?

Move your code above the call of CFrameWndEx::PreCreateWindow

Is your class really derived from CFrameWndEx? And is that the class you have in the IMPLEMENT_DYNANIC / IMPLEMENT_DYNCREATE line? And in the BEGIN_MESSSAGE_MAP line?
Think also on adding a ON_WM_CREATE to your message map and its OnCreatemethod for allowing other possibilities:
Do a similar thing to what you are doing int this new method.
Use SetWindowPos here

Related

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();
}

MFC CListCtrl - how to move item text to the left

I'm trying to implement single-column CListCtrl (or CMFCListCtrl, doesn't matter) in a way, that some rows might have checkboxes and some might not (I don't want to use neither CListBox, nor CCheckListBox, because in the future I'm planning to use multiple columns). I'm using LVS_EX_CHECKBOXES style, but that forces every item to have a checkbox. Then I manually delete the checkbox with a custom draw handler, but then I'm having trouble moving the item's text to the left side so that it takes place of the erased checkbox.
This is what my list control looks like:
But I need it to look like this (item2 is aligned to the left border, taking place of the erased checkbox):
I create my list control dynamically like this:
list->Create(WS_CHILD | WS_VISIBLE | WS_BORDER | LVS_REPORT | LVS_NOCOLUMNHEADER
rect, this, SOME_ID);
list->SetExtendedStyle(list->GetExtendedStyle() | LVS_EX_CHECKBOXES);
And my custom draw handler function looks like this:
I create my list control dynamically like this:
void MyCListCtrl::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult)
{
*pResult = CDRF_DODEFAULT;
LPNMLVCUSTOMDRAW lpn = (LPNMLVCUSTOMDRAW)pNMHDR;
if (CDDS_PREPAINT == lpn->nmcd.dwDrawStage)
{
*pResult = CDRF_NOTIFYITEMDRAW; // get notification for every row (item)
}
else if (CDDS_ITEMPREPAINT == lpn->nmcd.dwDrawStage)
{
int row = lpn->nmcd.dwItemSpec;
if (row == 1) { // we are in the first row (item2)
lpn->nmcd.rc.left -= 20; // doesn't do anything
lpn->rcText.left -= 20; // doesn't do anything
// this erases checkbox from the current row
SetItemState(row, INDEXTOSTATEIMAGEMASK(0), LVIS_STATEIMAGEMASK);
}
}
}
Is there any way to achieve the desired result? Am I doing it the right way, or is it better to use CListCtrl without the LVS_EX_CHECKBOXES and draw the checkboxes myself where I want to? If so, how? Thanks in advance.
You could try indentation with minus value (I didn't tried though):
LV_ITEM lvItem;
lvItem.iItem = nYourItem;
lvItem.iSubItem = 0;
lvItem.mask = LVIF_INDENT;
lvItem.iIndent = -1;
VERIFY(SetItem(&lvItem));
If it would be an option for you, why don't you just leave it as it is?
The first version looks much more uncluttered, anyway.
The second version breaks guidance of the eyes.

No LVIF_TEXT in LVN_GETDISPINFO message using LVS_OWNERDATA mode

I've encountered strange behavior in MFC when using list control (CListCtrl) with LVS_REPORT style in "virtual mode" i.e. with LVS_OWNERDATA style. List view itself is placed on a modeless dialog.
In dialog's OnInitDialog method I add two columns -- "Column 1" and "Column 2" in two different ways -- using LVCOLUMN and ListView_InsertColumn macro (1) and using CHeaderCtrl class with HDITEM structure (2).
When using first method (1), in LVN_GETDISPINFO message handler (message is handled by dialog, simple ON_NOTIFY) I receive a mask (NMLVDISPINFO.item.mask member) with different bits set (LVIF_TEXT, LVIF_IMAGE, LVIF_STATE, LVIF_INDENT etc.), after filling appropriate fields in NMLVDISPINFO.item structure, everything works fine.
But, when using second method (2) involving CHeaderCtrl class, the only bit in mask that is set is LVIF_INDENT, I never receive mask with anything else set.
Here are pieces of code that I use to add those columns:
Method one (1):
LVCOLUMN col;
col.mask = LVCF_TEXT | LVCF_WIDTH;
col.pszText = _T("Column 1");
col.cx = 100;
ListView_InsertColumn(m_MyList, 0, &col); //m_MyList is of CListCtrl type
col.pszText = _T("Column 2");
ListView_InsertColumn(m_MyList, 1, &col);
And method two (2):
HDITEM hdItem;
hdItem.mask = HDI_TEXT | HDI_FORMAT | HDI_WIDTH;
hdItem.fmt = HDF_STRING;
hdItem.cxy = 100;
hdItem.pszText = _T("Column 1");
hdItem.cchTextMax = _tcslen(hdItem.pszText);
m_MyList.GetHeaderCtrl()->InsertItem(0, &hdItem);
others_info.pszText = _T("Column 2");
others_info.cchTextMax = _tcslen(hdItem.pszText);
m_MyList.GetHeaderCtrl()->InsertItem(1, &hdItem);
What could possibly cause such behavior?
Method 2 is incorrectly using others_info for some operations but not others. It looks like the first InsertItem is using a text length that is possibly 0. The second call to InsertItem reuses the same hdItem structure which hasn't been changed since the first call.

MFC VC++ Custom Checkbox Image

How do I get a 3-state checkbox to use a different bitmap for the Indeterminate state?
I want to change the image used by my 3-state checkboxes to use a different one; the controls are in Win98-style, and the indeterminate state of such checkboxes is difficult to distinguish from disabled checkboxes (this is presumably why they changed this for the WinXP-style controls, but I cannot use those because of other details in my project).
I'm using Visual C++ 2010, and I've defined an 8x8 bitmap in VS's Resource Editor. The bitmap's ID is IDB_INDET_CHECK.
I'm not entirely sure what the standard "technique" for something like this is; I've only really just started getting into manipulating Windows controls and MFC.
My first attempt was to create a class, CTriButton, that derives from CButton, override the DrawItem function, and try to draw it myself. I then used SubclassDlgItem to turn one of the checkboxes in my window into this class (I think?). This... sort of works? The checkbox no longer appears, and if I click on where it should be, an empty checkbox frame appears, but nothing else happens (and the debug message in my code is not being sent).
Here's the relevant code, though I'm not sure any of this is right. First, code from my window's OnInitDialog.
BOOL CAffixFilterDlg::OnInitDialog() // CAffixFilterDlg is my CDialog-derived window
{
CDialog::OnInitDialog(); // call basic version
// subclass a CButton-derived control with CTriButton
if ( CBipedHead.SubclassDlgItem(IDC_HEAD, this) ) // CBipedHead is a CTriButton member of CAffixFilterDlg, IDC_HEAD is a checkbox
SetWindowLong(CBipedHead.m_hWnd, GWL_STYLE, CBipedHead.GetStyle() | BS_OWNERDRAW); // set the ownerdraw style
else // subclassing didn't work
_ERROR("Subclassing failed."); // I do not see this error message, so SubclassDlgItem worked?
// initialization continues, but is not relevant...
UpdateWindow();
Invalidate();
return TRUE;
}
Next, the code for my custom button's DrawItem.
void CTriButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
_DMESSAGE("Drawing TriButton"); // never see this message
CDC dc;
dc.Attach(lpDrawItemStruct->hDC); //Get device context object
int nWidth = GetSystemMetrics(SM_CXMENUCHECK);
int nMargin = ( nWidth - 8 ) / 2;
CRect textRt = lpDrawItemStruct->rcItem;
textRt.right = textRt.right - nWidth - nMargin;
CString text;
GetWindowText(text);
UINT textDrawState = DST_TEXT;
if ( lpDrawItemStruct->itemState & ODS_DISABLED )
textDrawState |= DSS_DISABLED;
dc.DrawState(CPoint(textRt.left, textRt.top), textRt.Size(), text, textDrawState, TRUE, 0, (CBrush*)NULL);
CRect rt = lpDrawItemStruct->rcItem; // initial rect is for entire button
rt.left = rt.right - nWidth; // set left margin
LONG center = ( rt.bottom + rt.top ) / 2;
rt.top = center - nWidth/2;
rt.bottom = center + nWidth/2;
UINT checkDrawState = DFCS_BUTTONCHECK;
if ( lpDrawItemStruct->itemState & ODS_DISABLED )
checkDrawState |= DFCS_INACTIVE;
if ( lpDrawItemStruct->itemState & ODS_CHECKED )
checkDrawState |= DFCS_CHECKED;
else if ( GetCheck() == BST_INDETERMINATE ) {
_VMESSAGE("Indeterminate; custom draw.");
CBitmap indet_check = CBitmap();
indet_check.LoadBitmap(IDB_INDET_CHECK);
CPoint pt = CPoint(rt.left + nMargin, rt.top + nMargin);
CSize sz = CSize(8, 8);
dc.DrawState(pt, sz, &indet_check, DST_BITMAP|DSS_NORMAL);
}
dc.DrawFrameControl(rt, DFC_BUTTON, checkDrawState);
}
In OnInitDialog() you need to call InvalidateRect() after changing the window style otherwise it doesn't know it needs to be redrawn. It's also a good idea to call UpdateWindow() after changing window styles. Some information is usually cached by the common controls and won't acknowledge the change until UpdateWindow() has been called.
In DrawItem() you are responsible for rendering all states of the control. You should not call CButton::DrawItem() as it does nothing. Try something like the following:
void CTriButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
CBitmap indet_check
_DMESSAGE("Drawing TriButton"); // I never see this message
int checkState = GetCheck();
if ( checkState == BST_CHECKED )
{
indet_check.LoadBitmap(IDB_INDET_CHECK);
}
else if ( checkState == BST_UNCHECKED )
{
indet_check.LoadBitmap(IDB_INDET_UNCHECKED);
}
else if ( checkState == BST_INDETERMINATE )
{
indet_check.LoadBitmap(IDB_INDET_INDETERMINATE);
}
// ... rest of your drawing code here ...
// don't forget to draw focus and push states too ;)
}
Addendum:
I can't believe I missed this first time around but your call to SubclassDlgItem is probably not having the desired effect. This call causes messages intended for the button to be processed by the controls parent window first. Because the default implementation of DrawItem in CWnd (the superclass of CDialog) does nothing the message never gets passed to the control.
Replace this with the following snippet and everything should be ok:
HWND hWndButton;
GetDlgItem(IDC_HEAD, &hWndButton);
CBipedHead.SubclassWindow(hWndButton);
Two side notes here:
It's usually not a good idea to use the same naming convention for both classes and class members. It makes for a confusing read.
I'm guessing you are always compiling and running in release mode. If you are - don't. This prevents assertions from being thrown and letting you know something is wrong.
Not the answer, but an answer: this custom CCheckBox I found more-or-less enables what I want. It doesn't, by default, allow 3 states, but I fixed that up with some of my own tweaks. I'm not 100% sure it works out of the box (I've had some issues, that don't seem to be due to my edits, but I can't be sure), but it was the solution I've used. I'm not going to call this the answer, though, in case someone can spy what was wrong with my code and wants to illuminate me.

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.