MFC Statusbar - Making text a link with button behavior - c++

Im trying to use a status bar in an MFC application as described here
I managed to add items to my status bar as described in the link.
There's nothing that I could post here as code reference as its currently 1:1 as in the link.
What I want is to make the text a link, so that when clicked, I'll catch this callback and do something.
I couldn't find how to do it, yet I've seen it in apps before.

I use something similar, but you need to derive your own class from CStatusBar.
a) place the following code at the top of your derived StatusBar.cpp
BEGIN_MESSAGE_MAP(CMyStatusBar, CStatusBar)
ON_WM_LBUTTONUP()
END_MESSAGE_MAP()
b) add the following declaration for CMyStatusBar in your derived StatusBar.h
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
c) place the following code later on in your derived StatusBar.cpp
void CMyStatusBar::OnLButtonUp(UINT nFlags, CPoint point)
{ CRect rc;
int nPaneIndex = CommandToIndex(ID_INDICATOR_TIME);
GetStatusBarCtrl().GetRect(nPaneIndex, &rc);
if (rc.PtInRect(point))
{ // do something
}
}

Related

MFC ribbon home button closes app with double click

I've faced with strange behaviour of a home ribbon button.
I've created standard MFC application in Visual Studio 2010 with Office template that has a ribbon control. But if I double click on the Home ribbon button at the upper position the application is closed.
Could you please tell me if it is standard MFC application handlers behaviour and how I can change it?
I've looked at Prevent double click on MFC-Dialog button but couldn't apply it to my case (more clearly - I don't know how to add double click handler to a ribbon home button).
CMFCRibbonApplicationButton is not derived from CWnd so cannot handle WM_LBUTTONDBLCLK message.
One solution is to derive from CMFCRibbonBar.
class CCustomRibbonBar : public CMFCRibbonBar
{
// ...
protected:
DECLARE_MESSAGE_MAP()
afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point);
};
BEGIN_MESSAGE_MAP(CCustomRibbonBar, CMFCRibbonBar)
ON_WM_LBUTTONDBLCLK()
END_MESSAGE_MAP()
void CCustomRibbonBar::OnLButtonDblClk(UINT nFlags, CPoint point)
{
CMFCRibbonBaseElement* pHit = HitTest(point);
if (pHit->IsKindOf(RUNTIME_CLASS(CMFCRibbonApplicationButton)))
{
// the user double-clicked in the application button
// do what you want here but do not call CMFCRibbonBar::OnLButtonDblClk
return;
}
CMFCRibbonBar::OnLButtonDblClk(nFlags, point);
}
Another solution: override PreTranslateMessage in CMainFrame class;
BOOL CMainFrame::PreTranslateMessage(MSG* pMsg)
{
if ((WM_LBUTTONDBLCLK == pMsg->message) && (pMsg->hwnd == m_wndRibbonBar))
{
CPoint point(pMsg->pt);
m_wndRibbonBar.ScreenToClient(&point);
CMFCRibbonBaseElement* pHit = m_wndRibbonBar.HitTest(point);
if (pHit && pHit->IsKindOf(RUNTIME_CLASS(CMFCRibbonApplicationButton)))
{
// do what you want but do not call CMDIFrameWndEx::PreTranslateMessage
return TRUE; // no further dispatch
}
}
return CMDIFrameWndEx::PreTranslateMessage(pMsg);
}
Derive your own derived class of CMFCRibbonApplicationButton.
Create a message handler for CMFCRibbonApplicationButton::OnLButtonDblClk
Provide your own implementation of what you want to do on the double click. If nothing should happen, just leave the body empty.
In your CMainFrame you find a definition of CMFCRibbonApplicationButton m_MainButton. Replace the class name with your implementation.

I am extending CTabCtrl but but cant insert any tabs

I am extending CTabCtrl but when I call InsertItem on my extended object none tab gets inserted. Who knows why is that. What do I do wrong?
class MyTabControl : public CTabCtrl
{
public:
MyListControl m_listCtrl;
void switchInterface(IDataProvider *provider);
public:
MyTabControl();
~MyTabControl();
afx_msg void OnGetDispInfo(NMHDR *pNMHDR, LRESULT *pResult);
protected:
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
DECLARE_MESSAGE_MAP()
};
If I remove ON_WM_CREATE() macro from message map then I can add tabs. Implementation of OnCreate function contains m_listCtrl.Create() function call and return 0 if list control is created successfully. What is wrong with this?
The CTabCtrl class is terribly old and poorly functional; you will have to do all the showing/hiding logic of controls when user is switching from one tab to another by your own hand. I recommend you to extend from CMFCTabCtrl instead.

MFC click and move/drag dialog window

I am currently working on finishing some code handed off to me. It was written in MFC in Visual Studio 2005 years ago, was put on hold, and now is brought to me.
While I know C++, I have spent the last ~2 months studying the code and learning MFC and it's starting to come together.
The GUI for the code is an SWF flash file embedded in an invisible dialog window. I do not have the source code for the SWF file so will probably, in the future, redo it in WPF or something. I have the WMMode set to "Window" because in Transparent/Opaque mode it doesn't display properly, where it flashes/blinks everytime a mouse event is captured.
Anyhow, in Win XP/Vista, clicking and dragging the flash control works. In windows 7/8.1, it won't move.
Happy to provide any and all info needed. I'm still a little overwhelmed by MFC dialogs so unsure what you'd all like to see.
I found this question: Moving window by click-drag on a control
Which looks like it solves a lot of the issue. However, I don't want the whole control to be clickable like this, only the top part. Unfortunately, in the MS Resource view, the ActiveX control is blank as the SWF is only loaded at runtime; I've tried to find resources for this kind of thing but it's very difficult as I am unsure of the technical terms to use.
EDIT
I have attempted this by creating a very simple MFC app that has a Static Text control and nothing else. I am trying to get it to work by clicking on the static text (though I may be painting myself into a corner as it does not have a built-in lButtonDown event).
Here is the relevant code:
class MyDialog : public CDialog
{
public:
MyDialog(CWnd* pParent = NULL) : CDialog(MyDialog::IDD, pParent)
{ }
// Dialog Data, name of dialog form
enum{ IDD = INTERFACE1 };
protected:
virtual void DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); }
//Called right after constructor. Initialize things here.
virtual BOOL OnInitDialog()
{
CDialog::OnInitDialog();
pText = (CStatic *)GetDlgItem(ID_TEXT);
pText->SetWindowTextW(_T("Hello World!"));
return true;
}
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
private:
CStatic * pText;
public:
DECLARE_MESSAGE_MAP()
};
BEGIN_MESSAGE_MAP(MyDialog, CDialog)
ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()
Overridden Method:
afx_msg void MyDialog::OnLButtonDown(UINT nFlags, CPoint point)
{
CWnd::OnNcLButtonDown(HTCAPTION, point);
}
I have also tried setting nFlags to 0x2, calling OnLButtonDown (as opposed to onNcLButtonDown), various other things. The message fires but the window does not move (it does move from the title bar, as normal). What am I missing?
Actually lets try this code instead with ON_WM_NCHITTEST(). This will drag the dialog if you click the mouse anywhere in client area (client area acts as caption). There is a line rc.bottom = rc.top + 100 if you uncomment it then it will only drag if you click the top section (I picked the number 100 at random).
//declare:
afx_msg LRESULT OnNcHitTest(CPoint point);
BEGIN_MESSAGE_MAP(MyDialog, CDialog)
ON_WM_NCHITTEST()
END_MESSAGE_MAP()
LRESULT MyDialog::OnNcHitTest(CPoint point)
{
ScreenToClient(&point);
CRect rc;
GetClientRect(&rc);
//rc.bottom = rc.top + 100;
if (rc.PtInRect(point))
return HTCAPTION;
return CDialog::OnNcHitTest(point);
}
Second option:
If we want to move the dialog by clicking on a child control, and if that control captures the mouse, then try this method instead. ***Note, test to make sure the control works properly after it is moved.
BOOL MyDialog::PreTranslateMessage(MSG *msg)
{
if (msg->message == WM_MOUSEMOVE && (msg->wParam & MK_LBUTTON))
{
CPoint p;
GetCursorPos(&p);
CRect r;
ActiveX->GetWindowRect(&r);
if (r.PtInRect(p))
{
ReleaseCapture();
SendMessage(WM_NCLBUTTONDOWN, HTCAPTION, 0);
SendMessage(WM_NCLBUTTONUP, HTCAPTION, 0);
return 1;
}
}
return CDialogEx::PreTranslateMessage(msg);
}

Drag CMDIChildWnd using client area

I'm trying to create a CMDIChildWnd without title bar draggable using the mouse on the client area.
into the message map I've added
ON_WM_LBUTTONDOWN()
and
void CChildFrame::OnLButtonDown(UINT nFlags, CPoint point)
{
SendMessage(WM_SYSCOMMAND, SC_MOVE | 0x0002);
}
the result is a child windows that moves using the mouse as required but only inside its area.
Any idea on how I can proceed?
I did it using a little trick. The class to implement the behaviour is the derived class of the CFormView and I'm able to move the window as expected sending the htcaption message to the parent
void derived_CFormView::OnLButtonDown(UINT nFlags, CPoint point)
{
CFormView::OnLButtonDown(nFlags, point);
GetParentFrame()->PostMessage(WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(point.x, point.y));
}
void derived_CFormView::OnLButtonUp(UINT nFlags, CPoint point)
{
CFormView::OnLButtonUp(nFlags, point);
GetParentFrame()->PostMessage(WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(point.x, point.y));
}

Cannot edit labels in a CListCtrl

I'm building a project with MFC Feature Pack. Is this project I have a window which includes a CView, which includes a CListCtrl-derived object. The object includes the LVS_EDITLABELS flag.
Somehow I cannot edit the CListCtrl icon labels by two-time clicking (not double-clicking) on the icon label. After I select the item with a single click, a second click just flashes the item (button down turns text background to white, button up turns it back to blue) and the edit control never appears.
I reduced this problem to the simplest form, and even with a plain CListCtrl object I cannot edit the labels.
I also found that:
This problem occurs in VS2008. It doesn't occur in a similar project built in VS2003.
I am able to edit the labels if I build a CListView instead of a CView+CListCtrl.
I am also able to edit the labels if I build a CFormView and put the CListCtrl inside the resource dialog.
Here's some code in the simplest form: the .h file:
// vwTerminaisTeste.h
//
#pragma once
// vwTerminaisTeste view
class vwTerminaisTeste : public CView
{
DECLARE_DYNCREATE(vwTerminaisTeste)
protected:
vwTerminaisTeste(); // protected constructor used by dynamic creation
virtual ~vwTerminaisTeste();
CListCtrl m_lstTerminais;
protected:
DECLARE_MESSAGE_MAP()
virtual void OnDraw(CDC* /*pDC*/);
public:
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnSize(UINT nType, int cx, int cy);
};
and the .cpp file:
// vwTerminaisTeste.cpp : implementation file
//
#include "stdafx.h"
#include "vwTerminaisTeste.h"
// vwTerminaisTeste
IMPLEMENT_DYNCREATE(vwTerminaisTeste, CView)
vwTerminaisTeste::vwTerminaisTeste()
{
}
vwTerminaisTeste::~vwTerminaisTeste()
{
}
BEGIN_MESSAGE_MAP(vwTerminaisTeste, CView)
ON_WM_CREATE()
ON_WM_SIZE()
END_MESSAGE_MAP()
// vwTerminaisTeste message handlers
void vwTerminaisTeste::OnDraw(CDC* /*pDC*/)
{
CDocument* pDoc = GetDocument();
}
int vwTerminaisTeste::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;
m_lstTerminais.Create(WS_CHILD | WS_VISIBLE | LVS_EDITLABELS, CRect(0,0,1,1), this, 0);
m_lstTerminais.InsertItem(0, "Teste", 0);
return 0;
}
void vwTerminaisTeste::OnSize(UINT nType, int cx, int cy)
{
CView::OnSize(nType, cx, cy);
if (IsWindow(m_lstTerminais.GetSafeHwnd()))
m_lstTerminais.MoveWindow(0, 0, cx, cy);
}
This way I cannot edit labels.
To change it to a CListView I simply replaced CView by CListView and m_lstTerminais by GetListCtrl(), and removed the OnCreate and OnSize implementations. That way it worked.
Note: the vwTerminaisTeste is created from a CSplitterWndEx within a CMDIChildWndEx-derived class.
Well nobody solved this problem but I managed to go around it by changing the CView to a CFormView and building a resource dialog with a ListView control, attaching it to the CListCtrl-derived class.
If anyone still has any suggestions on how could I solve this problem entirely, I'd appreciate them.
This sounds like it may be a focus or command routing issue, although that doesn't explain why it works OK in VS2003. You might try routing the command and/or focus messages from the splitter ctrl to vwTerminaisTeste, and/or from the MDIChild to the splitter. If you haven't already, you may need to derive your own splitter window. The command/focus forwarding would be something like...
BEGIN_MESSAGE_MAP(MySplitter, CSplitterWnd)
ON_WM_SETFOCUS()
END_MESSAGE_MAP(...)
void MySplitter::OnSetFocus(CWnd* pOldWnd)
{
// forward focus to the view window
m_vwTerminaisTeste.SetFocus();
}
BOOL MySplitter::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
{
// let the view have first crack at the command
if (m_vwTerminaisTeste.OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
return TRUE;
// otherwise, do default handling
return MySplitter::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
}