CTabView how to prevent switching - mfc

I use CTabView thats hold different FormViews.
I can switch to another view without any problem.
From a ParamView I don't want to allow switch to another view until the changed value are confirmed.
I have tried it with OnActivateView(..), OnShowWindow(..). But they are too late, the view are already moved to a another view, then these handlers are fired. :(
void CParamView::OnActivateView(BOOL bActivate, CView* pActivateView, CView* pDeactiveView)
{
/* // did not work, next View already shown before
if (!bActivate && pActivateView != pDeactiveView)
{
if (ParamValueChanged())
{
if (!ConfirmChangedValues())
return;
}
}
*/
CFormView::OnActivateView(bActivate, pActivateView, pDeactiveView);
}

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

Changing the enabling to disabling content by giving condition to combo box

I have a certain selection in combo box. Based on that selection some items need to be enabled/disabled. However I am unable to do so. And also another problem is once a single option is selected I cant change it to another selection without backspace and typing that selection again.
m_d_lvlayers is the variable of IDC of combo box. Its type is CString.
void CThermalToolDlg::OnCbnSelchangeLvLayers()
{
// TODO: Add your control notification handler code here
if (m_d_lvlayers == "2" )
{
UpdateData();
GetDlgItem(IDC_LV3_CU)->EnableWindow(0);
GetDlgItem(IDC_LV3_ICI)->EnableWindow(0);
//etc etc
UpdateData(0);
}
else if (m_d_lvlayers == "3")
{
UpdateData();
GetDlgItem(IDC_LV3_CU)->EnableWindow(1);
GetDlgItem(IDC_LV3_ICI)->EnableWindow(1);
//etc etc
UpdateData(0);
}
else
{
UpdateData();
GetDlgItem(IDC_LV3_CU)->EnableWindow(1);
GetDlgItem(IDC_LV3_ICI)->EnableWindow(1);
//etc etc
UpdateData(0);
}
}
I expect to get proper selections in combobox and corresponding enabling and disabling.
You need to call UpdateData(TRUE); first.
bSaveAndValidate
Flag that indicates whether dialog box is being initialized (FALSE) or data is being retrieved (TRUE)
void CThermalToolDlg::OnCbnSelchangeLvLayers()
{
UpdateData(TRUE); // Controls to Variables
if (m_d_lvlayers == "2" )
{
GetDlgItem(IDC_LV3_CU)->EnableWindow(FALSE);
GetDlgItem(IDC_LV3_ICI)->EnableWindow(FALSE);
//etc etc
UpdateData(FALSE);
}
else if (m_d_lvlayers == "3")
{
GetDlgItem(IDC_LV3_CU)->EnableWindow(TRUE);
GetDlgItem(IDC_LV3_ICI)->EnableWindow(TRUE);
//etc etc
UpdateData(FALSE);
}
else
{
GetDlgItem(IDC_LV3_CU)->EnableWindow(TRUE);
GetDlgItem(IDC_LV3_ICI)->EnableWindow(TRUE);
//etc etc
UpdateData(FALSE);
}
}
Although in your code it makes no sense to call UpdateData(FALSE); because all you are doing is setting the control window state to enabled.

Canceling an accepted dialog in Qt (aka user error checking)

I feel like I'm missing something obvious, but I want to be able to error check what the user inputted in a modal dialog when they hit 'OK', and allow them to go back and fix it without closing the dialog.
Is there something I can set within the 'OK' button's slot callback that will tell the dialog not to close?
You can use done method which you could know return value is rejected or not.
for example:
void ExDialog::done(int res)
{
if (res == QDialog::Accepted)
{
// check if it is ok or not
if(not)
{
ShowErrPopUp();
return;
}
}
QDialog::done(res);
}
// when ok button is clicked
void ExDialog::action_ok_bt_clicked()
{
this->accept();
}

Delete Key is not triggering KeyUp & KeyDown Event

I am currently dealing with a multi-form application and am having issue registering a del key press, the application that requires the del key is a form with a frame on it with objects painted on it that can be selected, upon pressing the del key the selected objects are to be deleted via a deleteObjects method. The code I am currently using is as follows
void __fastcall TF_Image::KeyUpKbd( WORD &Key )
{
if(Key == VK_DELETE || Key == VK_DKEY)
{
deleteSelectedObjects();
}
}
(Note: There are other paramenters in the function call but they aren't used)
TF_Image inherits from TFrame
I have tried mapping other keys other than the del key ie the D key and have found that the method is called with no problem. I have discovered that when pressing (physically) the del key the methods associated with KeyUp & KeyDown are not called.
Edit: So i've attempted to add the DeleteSelectedOb() method to my WndProc method without much luck either.
void __fastcall TF_ImgBrowserOA::WndProc(TMessage &Message)
{
if (Message.Msg == WM_KEYDOWN)
{
if (Message.WParam == VK_DELETE)
{
F_Image->DeleteSelectedOb();
}
}
//code that manages window resize
TForm::WndProc(Message);
}
The WndProc method doent appear to respond to keystrokes
So after cleaning up some code in some other modules and removing unneccessary menu's I decided to go back and look at this section again after I found a similar piece of code implementing a similar function, I couldn't see much difference between them and so I recompiled and attempted to run my Delete function from the KeyDown event and for some reason it just worked, I suspect it came down to an issue of another element holding focus in the Application. As a precaution I also called a SetFocus() to the frame in which I required this code to operate in. Its still a mystery to me why this didn't work intially though.
Here is a snippet for my TRichEdit control (Script_Edit).
TWndMethod *PrevWndProc = Script_Edit->WindowProc;
Script_Edit->WindowProc = MyWndProc;
void __fastcall My_Form::MyWndProc(TMessage &Message) {
switch (Message.Msg) {
case WM_KEYDOWN: {
// Check for DELETE and BACKSPACE keys
if( Message.WParam == VK_BACK ||
Message.WParam == VK_DELETE
) {
// Do whatever you need
}
break;
default:
// call default handler if not processed
PrevWndProc(Message);
}
}
You can't get much closer to the message core than this with VCL...

How does MFC's "Update Command UI" system work?

I'd like to know more about how this system works, specifically when and how the framework actually decides to update a UI element.
My application has a 'tools' system where a single tool can be active at a time. I used the "ON_UPDATE_COMMAND_UI" message to 'check' the tool's icon/button in the UI, which affected both the application menu and the toolbars. Anyway, this was all working great until some point in the last couple of days, when the toolbar icons stopped getting highlighted properly.
I investigated a little and found that the update command was only being received when the icon was actually clicked. What's strange is this is only affecting the toolbars, not the menu, which is still working fine. Even when the buttons in the menu are updated the toolbar icon stays the same.
Obviously I've done something to break it - any ideas?
EDIT:
Never mind. I'd overwritten the Application's OnIdle() method and hadn't called the original base class method - that is, CWinApp::OnIdle() - which I guess is where the update gets called most of the time. This code snippet from https://msdn.microsoft.com/en-us/library/3e077sxt.aspx illustrates:
BOOL CMyApp::OnIdle(LONG lCount)
{
// CWinApp's original method is involved in the update message handling!
// Removing this call will break things
BOOL bMore = CWinApp::OnIdle(lCount);
if (lCount == 0)
{
TRACE(_T("App idle for short period of time\n"));
bMore = TRUE;
}
// ... do work
return bMore;
// return TRUE as long as there are any more idle tasks
}
Here's a good article that kinda explains how to do it. Don't use his code example with WM_KICKIDLE though, instead scroll down to the comments section. There are two code samples that explain how to do it better. I quote:
//Override WM_INITMENUPOPUP
void CDialog::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu)
{
CDialog::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu);
// TODO: Add your message handler code here
if(pPopupMenu &&
!bSysMenu)
{
CCmdUI CmdUI;
CmdUI.m_nIndexMax = pPopupMenu->GetMenuItemCount();
for(UINT i = 0; i < CmdUI.m_nIndexMax; i++)
{
CmdUI.m_nIndex = i;
CmdUI.m_nID = pPopupMenu->GetMenuItemID(i);
CmdUI.m_pMenu = pPopupMenu;
// There are two options:
// Option 1. All handlers are in dialog
CmdUI.DoUpdate(this, FALSE);
// Option 2. There are handlers in dialog and controls
/*
CmdUI.DoUpdate( this, FALSE );
// If dialog handler doesn't change state route update
// request to child controls. The last DoUpdate will
// disable menu item with no handler
if( FALSE == CmdUI.m_bEnableChanged )
CmdUI.DoUpdate( m_pControl_1, FALSE );
...
if( FALSE == CmdUI.m_bEnableChanged )
CmdUI.DoUpdate( m_pControl_Last, TRUE );
*/
}
}
}
See if this helps - http://msdn.microsoft.com/en-us/library/essk9ab2(v=vs.80).aspx