I am trying to change cursor of a button in MFC dialog. i have used
BOOL CStartDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
if ( m_changeCursor )
{
::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_HAND));
return TRUE;
}
return CDialog::OnSetCursor(pWnd, nHitTest, message);
}
but it is changing cursor for whole dialog box. m_button is object of CButton class.
Please tell me how to change cursor of a button.I have tryed this also but not working
m_button1.SetCursor(::LoadCursor(NULL, IDC_HAND));
Call the LoadCursor() function and pass its returned value to the CMFCButton::SetMouseCursor() member function. Here is an example:
BOOL CExerciseDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
m_Calculate.SetMouseCursor(LoadCursor(AfxGetInstanceHandle(),
MAKEINTRESOURCE(IDC_CURSOR1)));
return TRUE;
}
refer http://www.functionx.com/visualc/controls/mfcbtn.htm#subtitle
also refer Api CWinApp::LoadCursor
Related
What I'm trying to achieve
Well, the title might not have explained the problem very well, so here goes:
I am trying to create a Win32 app using MFC that lets you edit and inspect other windows.
I want the user to be able to select other windows.
I got inspired by the "Find Window Process" tool on the toolbar on sysinternals applications such as ProcessExplorer.
The way it works is you click, then the window disappears, and then you drag it over the window you want to select. A border pops up around it and when you let go, it selects the window the mouse is over.
My problem
The problem I was facing is that I don't know how to detect when the user lets go of the mouse on another window.
I detect mouse down using OnClick in CMFCToolBarButton
I tried using SetCapture() but that did nothing.
I tried using OnNcLButtonUp and OnLButtonUp but neither of them worked. (alongside SetCapture)
Here's my code so far (ChildView.cpp):
BEGIN_MESSAGE_MAP(CChildView, CWnd)
ON_WM_PAINT()
ON_UPDATE_COMMAND_UI(ID_TB_LOCATEWINDOW, &CChildView::EnableToolbarButton)
ON_UPDATE_COMMAND_UI(ID_TOOLS_MESSAGELAUNCHER, &CChildView::EnableToolbarButton)
ON_WM_XBUTTONUP()
// ON_WM_LBUTTONUP()
ON_WM_NCLBUTTONUP()
END_MESSAGE_MAP()
....
void CChildView::LocateWindow()
{
GetParentFrame()->ShowWindow(SW_MINIMIZE);
SetCapture();
}
void CChildView::OnNcLButtonUp(UINT nHitTest, CPoint point)
{
ReleaseCapture();
GetParentFrame()->ShowWindow(SW_NORMAL);
MessageBox(L"Stuff", L"");
CWnd::OnNcLButtonUp(nHitTest, point);
}
I want to mention that the LocateWindow function gets called when the toolbar button is clicked (as in mouse down, not mouse down AND up)
It is called from the OnClick function.
Here's the code for that:
(I replace the button with OnToolbarReset)
// CLocateWindowButton.cpp : implementation file
//
#include "pch.h"
#include "WindowHacker.h"
#include "MainFrm.h"
#include "CLocateWindowButton.h"
// CLocateWindowButton
IMPLEMENT_SERIAL(CLocateWindowButton, CMFCToolBarButton, 1)
// CLocateWindowButton member functions
CLocateWindowButton::CLocateWindowButton()
{
}
CLocateWindowButton::CLocateWindowButton(CMainFrame* mainFrame, UINT uiCmdID, LPCTSTR lpszText) : CMFCToolBarButton(uiCmdID, NULL, lpszText)
{
this->mainFrame = mainFrame;
}
BOOL CLocateWindowButton::OnClick(CWnd* pWnd, BOOL bDelay = TRUE) {
//(CMainFrame*)m_pWndParent->LocateWindow();
mainFrame->LocateWindow();
return FALSE;
}
void CLocateWindowButton::CopyFrom(const CMFCToolBarButton& src)
{
CMFCToolBarButton::CopyFrom(src);
mainFrame = ((CLocateWindowButton&)src).mainFrame;
}
//void CLocateWindowButton::AssertValid() const
//{
// CMFCToolBarButton::AssertValid();
//
// // TODO: Add your specialized code here and/or call the base class
//}
UPDATE:
It seems to work when I put it inside an LButtonDown event, it just seems to not work when it is being detected from OnClick in CMFCToolBarButton
I found that in CMFCToolBar::OnLButtonUp, after calling OnClick in the button, it recaptures the cursor, invalidating our SetCapture.
BUT if I return TRUE instead of FALSE in OnClick, the mouse is not recaptured.
So changing this:
BOOL CLocateWindowButton::OnClick(CWnd* pWnd, BOOL bDelay = TRUE) {
//(CMainFrame*)m_pWndParent->LocateWindow();
mainFrame->LocateWindow();
//ReleaseCapture();
this->mainFrame->SetCapture();
return FALSE;
}
To this:
BOOL CLocateWindowButton::OnClick(CWnd* pWnd, BOOL bDelay = TRUE) {
//(CMainFrame*)m_pWndParent->LocateWindow();
mainFrame->LocateWindow();
//ReleaseCapture();
this->mainFrame->SetCapture();
return TRUE; // The line is changed here
}
The message gets sent to CMainFrame instead.
I have this code :
m_pBtnCom = new CButton();
m_pBtnCom->Create(_T("Push"), WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON|BS_TEXT|BS_VCENTER|BS_CENTER, rc, this, BTN_CMT);
Where:
this = my derived CWnd class
rc = CRect button position
BTN_CMT = button id
Current context:
If I disable the parent CWnd by calling EnableWindow(FALSE), even if I call the function EnableWindow(TRUE) on the button (m_pBtnCom->EnableWindow(TRUE)), the latter remains disabled; Therefore, nothing works on it: click, tooltip, ...
I tried to remove WS_CHILD, without success
Question:
Is it possible to activate the button when the window (argument this in my code) is disabled?
Child window can't be independently enabled when parent window is disabled. You can instead enable all children, then go back and enable the particular button.
Note, if you have IDCANCEL button, and you disable it, then the dialog's close button is not functioning either and it gets confusing. You may want to avoid disabling the cancel button and override OnCancel
void CMyDialog::enable_children(bool enable)
{
auto wnd = GetWindow(GW_CHILD);
while (wnd)
{
wnd->EnableWindow(enable);
wnd = wnd->GetWindow(GW_HWNDNEXT);
}
}
BOOL CMyDialog::OnInitDialog()
{
CDialog::OnInitDialog();
enable_children(FALSE);
//re-enable one button
if(GetDlgItem(IDCANCEL)) GetDlgItem(IDCANCEL)->EnableWindow(TRUE);
return TRUE;
}
void OnCancel()
{
MessageBox(L"cancel...");
CDialog::OnCancel();
}
I thought it be nice to add a Status Bar with percentage and other information to a CDialogEx that is used for viewing an image. But it doesn't seem that you can simply use a CMFCStatusBar or a CStatusBar and have it just work.
I found various samples, but none of them have the statusbar outside the client area and moves as resized? The different methods simply create a statusbar and it ends up hidden under a horizontal scrollbar and if you resize the window, the statusbar is sitting there in the middle of the dialog.
Is there an easy way or full example of having a statusbar on a CDialogEx that can be resized like a normal window?
Is there an easy way or full example of having a statusbar on a CDialogEx that can be resized like a normal window?
Yes! Once you have created the status bar you can add it to the dynamic layout for resizing:
//This is where we actually draw it on the screen
RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST,
ID_INDICATOR_MEETING_TYPE);
GetDynamicLayout()->AddItem(m_StatusBar.GetSafeHwnd(),
CMFCDynamicLayout::MoveVertical(100), CMFCDynamicLayout::SizeHorizontal(100));
I have a status bar (not CMFCStatusBar as it will not work, but CStatusBar is OK) on two dialogs in my application.
When Dynamic Layout is not automatically enabled
Here is an updated example for when Dynamic Layout is not automatically enabled for you (CDialogEx with no controls):
BOOL CMyDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
if (!m_StatusBar.Create(this)) {
TRACE0("Failed to create status bar\n");
return -1;
}
m_StatusBar.SetIndicators(indicators, _countof(indicators));
RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0);
EnableDynamicLayout();
auto pdlmanager=GetDynamicLayout();
if (pdlmanager) {
if (pdlmanager->Create(this)) {
pdlmanager->AddItem(m_StatusBar.GetSafeHwnd(), CMFCDynamicLayout::MoveVertical(100), CMFCDynamicLayout::SizeHorizontal(100));
}
}
// return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
return TRUE;
}
Catering for horizontal scroll bars
NIf you have a horizontal scrollbar the StatusBar will end up above it; therefore you may have to create separate CWnd and add it to the dynamic layout (it would also be the nIDLeftOver of the RepositionBars()).
Here's how you can add the a "view" window for the contents so scrollbars can be contained within the view area:
BOOL CMyDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
if (!m_StatusBar.Create(this)) {
TRACE0("Failed to create status bar\n");
return -1;
}
m_StatusBar.SetIndicators(indicators, _countof(indicators));
CRect rc;
GetClientRect(&rc);
CString clsname=AfxRegisterWndClass(0);
m_ImageView.Create(clsname, _T(""), WS_CHILD | WS_VISIBLE, rc, this, IDC_MY_VIEW);
RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, IDC_MY_VIEW);
EnableDynamicLayout();
auto pdlmanager=GetDynamicLayout();
if (pdlmanager) {
if (pdlmanager->Create(this)) {
pdlmanager->AddItem(m_StatusBar.GetSafeHwnd(), CMFCDynamicLayout::MoveVertical(100), CMFCDynamicLayout::SizeHorizontal(100));
pdlmanager->AddItem(m_ImageView.GetSafeHwnd(), CMFCDynamicLayout::MoveNone(), CMFCDynamicLayout::SizeHorizontalAndVertical(100, 100));
}
}
// return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
return TRUE;
}
I need to create a caption bar button for a CDockablePane which will call up a menu with various options. I tried to use CMFCCaptionMenuButton and the button and menu show up but the message map methods for the menu ids don't fire. The MFC documentation states that CMFCCaptionMenuButton is meant for internal infrastructure and not really for your code.
So assuming that is what my problem is should I be using a CMFCCaptionBarButton and then making a separate popup menu? Has anyone made a similar caption bar based menu in MFC before?
Here's some slimmed down code snippets in case I just made a stupid mistake in hooking up the events:
BEGIN_MESSAGE_MAP(CDockPane, CDockablePane)
ON_COMMAND(ID_MORPH_BROWSER, OnMorphBrowser)
END_MESSAGE_MAP()
void CDockPane::OnPressButtons(UINT nHit)
{
// only for custom button handling don't call base
// close, maximize, and pin will be handled by default
switch (nHit)
{
case ID_MORPHTEST:
{
CMorphMenuButton* pButton = dynamic_cast<CMorphMenuButton*>(m_arrButtons.GetAt(m_morphIndex));
pButton->ShowMenu(this);
break;
}
}
}
void CDockPane::SetCaptionButtons()
{
CDockablePane::SetCaptionButtons(); // for close, pin etc
m_morphIndex = m_arrButtons.Add(new CMorphMenuButton(ID_MORPHTEST));
}
void CDockPane::OnMorphBrowser()
{
// do stuff on menu item click
}
Edit: Removed previous code no longer in use
Now that the sound of crickets chirping has dwindled in the background I guess I'll post the workaround I currently have in place:
Instead of inheriting and extending CMFCCaptionMenuButton I build my class by extending CMFCCaptionButton. I then create a menu and provide a ShowMenu method to be explicitly called when handling the custom button events as well as overriding GetIconID to return a particular system icon for the button for each menu added to the caption bar ending up with something like this for the example outlined in the question:
#pragma once
// CMorphMenuButton command target
class CMorphMenuButton : public CMFCCaptionButton
{
public:
CMorphMenuButton(UINT nHit);
virtual ~CMorphMenuButton();
virtual CMenuImages::IMAGES_IDS GetIconID (BOOL bHorz, BOOL bMaximized) const;
void ShowMenu(CWnd* pWnd);
private:
CMenu m_dockMenu;
CMenu* m_subMenu;
};
// MorphMenuButton.cpp : implementation file
//
#include "stdafx.h"
#include "MorphMenuButton.h"
// CMorphMenuButton
CMorphMenuButton::CMorphMenuButton(UINT nHit)
: CMFCCaptionButton(nHit)
{
SetMiniFrameButton(); // already defaulted?
m_dockMenu.LoadMenu(IDR_DOCKPANE); // resource ID for dock pane menus
}
CMorphMenuButton::~CMorphMenuButton()
{
m_dockMenu.DestroyMenu();
}
CMenuImages::IMAGES_IDS CMorphMenuButton::GetIconID(BOOL bHorz, BOOL bMaximized) const
{
return CMenuImages::IdArrowForward;
}
void CMorphMenuButton::ShowMenu(CWnd* pWnd)
{
CRect windowRect, buttonRect;
pWnd->GetWindowRect(&windowRect);
buttonRect = GetRect();
CPoint menuPos(windowRect.left + buttonRect.right, windowRect.top + buttonRect.bottom);
m_subMenu = m_dockMenu.GetSubMenu(0);
if (!m_subMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, menuPos.x, menuPos.y, pWnd))
{
DWORD id = GetLastError();
wchar_t errMsg[256];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, id, 0, errMsg, sizeof(errMsg), 0);
MessageBox(0, errMsg, L"Error", MB_OK);
}
}
The setting of caption bar buttons and handling of click events for both buttons and menus are the same as defined in the question and this works.
I'm having the following code:
CMainFrame* pFrame = new CMainFrame;
if (!pFrame)
return FALSE;
m_pMainWnd = pFrame;
// create and load the frame with its resources
pFrame->LoadFrame(IDR_APP_MAINFRAME,
WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE, NULL,
NULL);
// The one and only window has been initialized, so show and update it
pFrame->ShowWindow(SW_SHOWMAXIMIZED);
The problem is, when I press <ALT>, the menu(IDR_APP_MAINFRAME) will popup.
How can I always hide the menu and do not response to presss?
I had heard this is due to an accelerator control in MFC, but I couldn't see the control in my project solution which is using VS2008..
In your CMainFrame override PreCreateWindow and destroy the menu. Try something like this:
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if(cs.hMenu!=NULL)
{
::DestroyMenu(cs.hMenu);
cs.hMenu = NULL;
}
return CFrameWnd::PreCreateWindow(cs);
}