MFC RibbonBar -- programmatically minimize? - mfc

The MFC ribbon bar has a menu item labelled 'Minimize the ribbon'. When you select it, only the headers of each category are shown, and the ribbon pops up when the headers are clicked. I'd like to programmatically force a ribbon into this state. Unfortunately, the only method I can find is ToggleMimimizeState() [sic], which will either put it into this state or take it out depending on its current state.
Looking at the MFC source code, the way the menu command works is through this code:
case idMinimize:
if (m_pActiveCategory != NULL)
{
ASSERT_VALID(m_pActiveCategory);
m_pActiveCategory->ShowElements(FALSE);
RedrawWindow();
}
m_pActiveCategory can be obtained from outside of the CMFCRibbonBar class through the GetActiveCategory() method, but unfortunately the category's ShowElements() method is protected and I cannot see a way of achieving the same effect with the public methods.
Neither does there seem to be an obvious way of determining whether the ribbon is currently minimized.
Is there something I'm missing, or do I just have to guess at the current state?

Derive two new classes from CMFCRibbonBar and CMFCRibbonCategory
class MyCMFCRibbonCategory: public CMFCRibbonCategory
{
public:
void force_ShowElements(BOOL todo)
{
ShowElements(todo);
}
};
class MyRibbonBar: public CMFCRibbonBar
{
public:
BOOL is_minimized()
{
return m_dwHideFlags == AFX_RIBBONBAR_HIDE_ELEMENTS;
}
void minimize_me(BOOL show_minimized)
{
MyCMFCRibbonCategory* cc = (MyCMFCRibbonCategory*)GetActiveCategory();
if (cc != NULL)
{
cc->force_ShowElements(!show_minimized);
RedrawWindow();
}
}
};
then change in your CMainframe from
CMFCRibbonBar m_wndRibbonBar;
to
MyRibbonBar m_wndRibbonBar;
Now in your code you can use the new two members:
BOOL MyRibbonBar::is_minimized()
void MyRibbonBar::minimize_me(BOOL show_minimized)
Basic example:
void CMainFrame::OnButton2()
{
if( m_wndRibbonBar.is_minimized() )
m_wndRibbonBar.minimize_me(FALSE);
else
m_wndRibbonBar.minimize_me(TRUE);
}
Hope it can help.

A combination of the above worked for me. That is, I wanted to use the Ribbon as a tabbed set of extra functions on a main menu. However, I didn't want the ribbon to have the ability to stay maximized. I only wanted the user to click, see a few actions and after that, disappear.
In short, prevent the ribbon from docking, or staying maximized. Whatever you want to call it. Click a tab, then and icon on the ribbon and disappear.
Instructions:
I derived my own CMyRibbon class by inheriting from CMFCRibbonBar. (Done using Class wizard and making an MFC class)
Create an event handler for WM_SIZE in our new CMyRibbon class (ClassWizard)
void CMyRibbon::OnSize(UINT nType, int cx, int cy)
{
CMFCRibbonBar::OnSize(nType, cx, cy);
if (!(GetHideFlags() & AFX_RIBBONBAR_HIDE_ELEMENTS))
ToggleMimimizeState();
}
In CMainFrm.h add this change:
CMyRibbon m_wndRibbonBar;

Use m_wndRibbonBar.ToggleMimimizeState();

Simply check (m_wndRibbonBar.GetHideFlags() & AFX_RIBBONBAR_HIDE_ELEMENTS) value.

Related

How to change MFC View by clicking a Button inside the MainFrame

I want to change the presented View by clicking a button inside the Window
like this.
My Project settings:
I made an MFC Project (SDI) without Doc/View support.
I made two more Views in the Designer and added Classes to them. The new View Classes are derived from CFormView. I changed the Constructor and Destructor of the new View Classes to public.
Added them as pointers to MainFrm.h:
CMainView* m_pMainView;
CSecondView* m_pSecondView;
I changed the OnCreate(),OnSetFocus() and OnCmdMsg() Method of MainFrm.cpp like this:
(That allows to present the FormView I made with the Designer)
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
// First, build the view context structure
CCreateContext ccx;
// Designate the class from which to build the view
ccx.m_pNewViewClass = RUNTIME_CLASS(CMainView);
// Using the structure, create a view
m_pMainView = DYNAMIC_DOWNCAST(CMainView, this->CreateView(&ccx));
if (!m_pMainView)
{
TRACE0("creation of view failed");
}
// Do layout recalc
RecalcLayout();
// Show the view and do an initial update
m_pMainView->ShowWindow(SW_SHOW);
m_pMainView->OnInitialUpdate();
// Set this view active
SetActiveView(m_pMainView);
// Order it to resize the parent window to fit
m_pMainView->ResizeParentToFit(FALSE);
return 0;
}
...
void CMainFrame::OnSetFocus(CWnd* /*pOldWnd*/)
{
m_pMainView->SetFocus();
}
BOOL CMainFrame::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
{
if (m_pMainView->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
return TRUE;
return CFrameWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
}
Now here comes my problem! I have a button on the First presented View and if you click on it, the view should change. I made the following function with the event handler in the Designer:
void CMainView::OnBnClickedButton1()
{
// What to do here? I want to change the current view to another View by clicking the button
}
If i handle it in the MainFrm.cpp class for example with menue buttons it is no problem... that works fine:
void CMainFrame::OnViewNextview()
{
CCreateContext ccx2;
ccx2.m_pNewViewClass = RUNTIME_CLASS(CSecondView);
m_pSecondView = DYNAMIC_DOWNCAST(CSecondView, this->CreateView(&ccx2));
RecalcLayout();
m_pMainView->ShowWindow(SW_SHOW);
m_pMainView->OnInitialUpdate();
SetActiveView(m_pMainView);
m_pMainView->ResizeParentToFit(FALSE);
}
I tried to write a function in CMainFrame and call this function in CMainView::OnBnClickedButton1() but I don't know how to get the current MainFrm Object. A pointer on MainFrm or a member of it in CMainView did not work.
I searched and red tutorials for days to solve my problem. I also tried it with Doc/View support like shown here:
https://learn.microsoft.com/en-us/cpp/mfc/adding-multiple-views-to-a-single-document?view=vs-2019 but i dont know where to call switchView() correctly.
Maybe anyone can help...
First, you shouldn't really be overriding OnCmdMsg - instead, use DECLARE_MESSAGE_MAP in your header file and BEGIN_MESSAGE_MAP/END_MESSAGE_MAP in your implementation file, and insert handler messages between those two macros.
I see that you already have a handler in your CMainView class for the button click! From here, you should call the CMainFrame function to change to the next view - just as you do when the menu command is given (which you say works). Make that function public and give the MainView class access to a pointer to the main frame (or use AfxGetMainWnd() and cast it to a pointer of your class). Something like this:
void CMainView::OnBnClickedButton1()
{
AfxGetMainWnd()->PostMessage(WM_COMMAND, menuID); // ID of menu command that works!
}
Big hugs for Adrian, i got it to work!
I also added a third view successfully :)
It is very IMPORTANT , to HIDE the last shown Window if you want to implement more views. you can do it like:
void CMainFrame::OnView3()
{
CCreateContext ccx3;
ccx3.m_pNewViewClass = RUNTIME_CLASS(CThirdView);
m_pThirdView = DYNAMIC_DOWNCAST(CThirdView, this->CreateView(&ccx3));
RecalcLayout();
m_pSecondView->ShowWindow(SW_HIDE); // Hide the last Window
m_pThirdView->ShowWindow(SW_SHOW); // Show the new Window
m_pThirdView->OnInitialUpdate();
SetActiveView(m_pThirdView);
//m_pThirdView->ResizeParentToFit(FALSE); //if you call this, the size of the window is the same like in the Designer
}

MFC Edit control message handle access Document object

I create a new class extend CEdit to override some of the message handles.
My ultimate goal is when edit control is on focus, some of the toolbar buttons become available.
I created a bool variable in doc. then the pCmdUI->enable() set to this bool. The onfocus is overridden in new edit control class. I'm having trouble to update this bool vairbale from the onfocus message handle.
void CMFCDoc::OnUpdateTextColor(CCmdUI *pCmdUI)
{
// TODO: Add your command update UI handler code here
pCmdUI->Enable(shape_onfocus_);
}
class CMFCDoc : public COleServerDoc
{
...
bool shape_onfocus_;
}
//edit control
#include <afxwin.h>
class CEditControl :
public CEdit
{
public:
CEditControl();
~CEditControl();
DECLARE_MESSAGE_MAP()
afx_msg void OnEnSetfocus();
};
void CEditControl::OnEnSetfocus()
{
//----- I want to update shape_onfocus_ here. -----
this->SetWindowTextA(_T("Hello world"));
}
Assuming your CEditControl instance is a child of some sort of CView, you could go about it like this:
void CEditControl::OnEnSetfocus()
{
CView *view = static_cast<CView *>(GetParent());
CMFCDoc *doc = static_cast<CMFCDoc *>(view->GetDocument());
doc->shape_onfocus_ = true;
...
}
Assuming the edit-control is a child of a CView-derived class, you should better put the OnUpdateUI() handler in the view class, not the document one.
For example, if the view-class is CFormView-derived (dialog), you could simply write:
void CMyView::OnUpdateTextColor(CCmdUI *pCmdUI)
{
pCmdUI->Enable(GetFocus()==GetDlgItem(IDC_MYEDIT));
}
This piece of code works for both SDI and MDI applications.
If the view class is not CFormView-derived (the edit-box was created programmatically), the code above could be modified slightly, and instead of calling GetDlgItem() you should enumerate the view's children list (search your edit-box there).
If the only reason to override the edit-control was to capture the EN_SET/KILLFOCUS messages, sorry this wasn't worth the effort, as you could simply capture these in the view's code. Then the view's message-map would contain:
ON_EN_SETFOCUS(IDC_MYEDIT, &CMyView::OnEnSetfocusMyEdit)
ON_EN_KILLFOCUS(IDC_MYEDIT, &CMyView::OnEnKillfocusMyEdit)
and the view-class code:
void CMyView::OnEnSetfocusMyEdit()
{
// TODO: Add your control notification handler code here
}
void CMyView::OnEnKillfocusMyEdit()
{
// TODO: Add your control notification handler code here
}
These are generated by the wizard. Go to the Class View tab, select your class and then go to the Events page; in the Controls subtree you can find your control and add handlers for its events. But all this is not needed, as you can just use GetFocus()/GetDlgItem() as suggested above.
And as other members said, you can access the document class from any of its views by calling the GetDocument() function.

C++/MFC is there a way to click a button while clicking another one?

I have 2 buttons in my projects :
-Button A
-Button B
Is there a way i can push the buttonA and perform the click of ButtonB aswell.
Call the callback for ButtonB explicitly from ButtonA click callback.
BOOL MyDialog::OnButton_A_Clicked()
{
...
OnButton_B_Clicked();
}
As mentioned in this comment, calling a handler directly may cause subtle problems, I would propose the following solution from the same comment:
Send button click message to the button B.
BOOL MyDialog::OnButton_A_Clicked()
{
...
CWnd *pBtnB = GetDlgItem(IDC_BUTTONB);
ASSERT(pBtnB != NULL); // You can use MFC
pBtnB->SendMessage(BN_CLICKED);
}
Although the answer was accepted, I would like to offer another solution.
First, the proposed linking of buttons will break as soon as you decide that clicking on ButtonB should cause click on ButtonA too.
I would separate all that clicking from the underlying functionality, and invoke it as needed:
void MyDialog::Button_A_Action()
{
}
void MyDialog::Button_B_Action()
{
}
void MyDialog::OnButton_A_Clicked()
{
Button_A_Action();
Button_B_Action();
}
void MyDialog::OnButton_B_Clicked()
{
Button_B_Action();
}

Change Expand/Collapse icon in wxDataViewCtrl

I want to replace the Expand/Collapse icon with my icons in wxDataViewCtrl.
I am not able to find a way to set my expand/collapse icons.
Is there any API available for the same?
or
Do I need to modify the wxDataViewCtrl source directly? your prior experience can help me.
The main point of having wxDataViewCtrl in the first place is that it wraps the corresponding native control, so changing its look and feel is not supported.
This being said, if you're using its generic implementation, as is always the case under MSW, you can actually change it by defining a custom wxRendererNative-derived class and overriding its DrawTreeItemButton() method. But this would affect all the other controls drawing tree-like buttons, including, obviously, generic wxTreeCtrl itself but also wxPropGrid. Generally speaking I wouldn't recommend doing this.
I do not know whether it is right approach or wrong, Here I found a way to achieve my thing based on VZ.suggestion.
class MyRenderer : public wxDelegateRendererNative
{
public:
MyRenderer() : wxDelegateRendererNative(wxRendererNative::GetDefault()) { }
virtual void DrawTreeItemButton(wxWindow *win,
wxDC& dc,
const wxRect& rect,
int flags = 0)
{
MyCustomCtrl* pWin = dynamic_cast<MyCustomCtrl*>(win->GetParent());
if (pWin != nullptr)
{
// Draw my choice of expand/collapse button.
}
else
{ // Do not affect other controls that are using this drawing.
wxRendererNative::GetDefault().DrawTreeItemButton(win, dc, rect, flags);
}
}
};
This way I can override the
virtual void DrawItemSelectionRect(wxWindow *win,
wxDC& dc,
const wxRect& rect,
int flags)
and many more native drawings.

How to solve a specific circular dependency?

so my problem isn't as much about the code, but about the way to do it.
I'm working on a GUI and I want my buttons to know who there parent is. And of course, the window knows which buttons it has.
This creates a circular dependency, since both need to be able to access the others methods and attributes, at least that's what I would prefer.
I found a solution that works, but I'm not very happy with it:
I created a third object to which button writes, what it wants the window to do. And the window checks this third object for commands.
I wanted to ask you, if you know of a better way to this, since I couldn't find any other way, that works for me.
Thanks!
I'd suggest create a window interface. Provide a back pointer to the window interface in the constructor of the button. The window that owns the button depends on the button and the button depends on the window interface. No circular dependency.
struct IWindow {
};
struct Button {
IWindow* window_;
Button(IWindow* window) : window_(window){}
};
struct WindowWithButton : IWindow {
Button button_;
WindowWithButton() : button_(this) {}
};
Then add virtual methods to IWindow that are implemented by WindowWithButton so that Button can get the information it needs from the WindowWithButton.
It's a standard pattern:
struct Window; // Forward-declare the parent
struct Button {
void pingParent(); // Only declare members which need
// more than superficial knowledge of Window
Window* parent; // Ok, we know there is a Window, somewhere
};
struct Window {
unique_ptr<Button> child;
// Other functions using the button
void pingpong() {child->pingParent();}
void ping(){}
};
/*optional inline*/ void Button::pingParent() {
parent->ping();
}