cmfcpropertygridctrl in dialog can't be edited - c++

I create a dialog derived from CDialogEx, then add a CMFCPropertyGridCtrl on it, generate a control value of m_wndPropList binding to the CMFCPropertyGridCtrl, in OnInitDialog, I add the initial code:
BOOL CPropDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
InitPropList(); // here I add the initial function
return TRUE;
}
the codes in InitPropList() are just copy from the example code, generated by VS2015 when creating a MFC project with visual studio style.
when I have done all the work, the property ctrl display well, but it can't be edited, and doesn't respons to the mouse. please kindly help me, thanks!

Related

MFC's dialog-based app title bar highlighting visual artifacts on Windows 10 (i.e. bugs in CDialogEx)

I'm not sure why am I getting this visual artifact?
Here's how to repro:
I'm using Visual Studio 2017 Community. Create a new C++ -> MFC project:
Then specify "dialog based":
Then build as "Debug" x86 app and run it.
So I'm running it on Windows 10.
When this dialog-based process has focus, it looks as I would expect it:
but if I switch keyboard focus to some other app (by clicking on it), this dialog-based process still retains its title bar color:
I'm not sure if it's just a matter of a visual glitch or if there's a deeper mess-up with the window message handling. How do I correct it? (This wasn't an issue with older MFC projects.)
I managed to replicate your problem and found a quick fix for it.
You need to add the WM_ACTIVATE message handler to your main dialog, comment out the base class OnActivate and modify it like this:
void CMFCApplication1Dlg::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized)
{
//CDialogEx::OnActivate(nState, pWndOther, bMinimized);
// TODO: Add your message handler code here
this->Default();
}
CWnd::Default call is needed to keep the active/inactive visualization of the default button.
OK, as much as I appreciate #VuVirt's solution, it doesn't completely remove all the bugs that are shipped in the default Dialog-based solution in VS2017. It solves the title bar focus issue, but while continuing to develop my project I encountered another bug. So I'm copy-and-pasting it from my comment to his answer:
There's still some kinda screw-up there. I'm not sure if it's related to this fix or not. Ex: If you create a button and then in its handler try to do: CFileDialog d(TRUE, NULL, NULL, OFN_HIDEREADONLY | OFN_EXPLORER, NULL, this); d.DoModal(); to open a file picker dialog. When file picker opens up, close it and see if the title bar of the parent MFC dialog window goes back to being active. In my case it remains inactive until I click onto the Windows taskbar and then back onto that MFC app.
After banging my head against the wall trying to see what is going on there, I decided to try an earlier solution proposed by #zett42 in the comments to my original question (i.e. to replace CDialogEx with CDialog) and it worked! All the bugs are gone!
So here's my verdict: CDialogEx is buggy af.
The resolution is quite simple: When you create a new dialog-based project use project-wide find-and-replace (in the Edit menu) and replace all occurrences of CDialogEx with CDialog. And that is it. (I tried to use VS2017's refactoring tool for that but it messed it up and didn't replace it all. So simple search-and-replace does the job.)
And if you think that you'll be missing some functionality without CDialogEx, then you won't. All it does (besides introducing bugs) is that it adds background images and colors to the dialog.
So until MS fixes those glaring bugs in their templates I'm sticking with this approach.
This seems to be a bug in CDialogImpl::OnActivate and CDialogImpl::OnNcActivate:
void CDialogImpl::OnNcActivate(BOOL& bActive)
{
if (m_Dlg.m_nFlags & WF_STAYACTIVE)
bActive = TRUE;
if (!m_Dlg.IsWindowEnabled())
bActive = FALSE;
}
void CDialogImpl::OnActivate(UINT nState, CWnd* pWndOther)
{
m_Dlg.m_nFlags &= ~WF_STAYACTIVE;
CWnd* pWndActive = (nState == WA_INACTIVE) ? pWndOther : &m_Dlg;
if (pWndActive != NULL)
{
BOOL bStayActive = (pWndActive->GetSafeHwnd() == m_Dlg.GetSafeHwnd()
|| pWndActive->SendMessage(WM_FLOATSTATUS, FS_SYNCACTIVE));
if (bStayActive)
m_Dlg.m_nFlags |= WF_STAYACTIVE;
}
else
{
m_Dlg.SendMessage(WM_NCPAINT, 1);
}
}
This is meant to give CDialogEx the ability to stay active, for example, when CMFCPopupMenu is shown.
But m_Dlg.SendMessage(WM_NCPAINT, 1) is a suspicious call. The usage doesn't match the documentation for WM_NCPAINT:
Parameters
wParam
A handle to the update region of the window. The update region is clipped to the window frame.
lParam
This parameter is not used.
Additionally, OnNcActivate has an override based on IsWindowEnabled(). This seems to be a patch to fix the earlier problem in OnActivate. But it causes problems elsewhere, for example when using CFileDialog in CDialogEx
Suggested solution:
Modify CDialogEx::OnActivate so that it runs the default procedure. Or, change it such that it will force repaint.
BOOL CDialogEx::OnNcActivate(BOOL active)
{
if(m_nFlags & WF_STAYACTIVE)
active = TRUE;
return(BOOL)DefWindowProc(WM_NCACTIVATE, active, 0L);
}
void CDialogEx::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized)
{
Default();
}
or
void CDialogEx::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized)
{
Default();
//save the previous flag
UINT previous_flag = m_nFlags;
m_nFlags &= ~WF_STAYACTIVE;
// Determine if this window should be active or not:
CWnd* pWndActive = (nState == WA_INACTIVE) ? pWndOther : this;
if(pWndActive != NULL)
{
BOOL bStayActive = pWndActive->GetSafeHwnd() == GetSafeHwnd() ||
pWndActive->SendMessage(WM_FLOATSTATUS, FS_SYNCACTIVE);
if(bStayActive)
m_nFlags |= WF_STAYACTIVE;
}
if(previous_flag != m_nFlags && previous_flag & WF_STAYACTIVE)
{
//if the flag is changed,
//and if WF_STAYACTIVE was previously set,
//then OnNcActivate had handled it wrongly, do it again
SendMessage(WM_NCACTIVATE, FALSE); //<- less wrong!
}
}
This should work with CMFCPopupMenu for example. The MFC menu will open without deactivating the dialog.
I am not sure what SendMessage(WM_FLOATSTATUS, FS_SYNCACTIVE) is for, I haven't been able to test it... If it's necessary, it seems the code could be added on OnNcActivate, and then OnActivate is left alone.

click event of WindowsFormsControlLibrary button in MFC Dialog based app

I'm using some Windows Forms Control Library elements in my app.
My question is:
How to perform button click event which this element comes from Windows Forms Control Library?
So, I can get *library* textbox value in ProgramDlg.cpp file like this:
void CMFCApplication1Dlg::OnBnClickedButton1()
{
// TODO: Add your control notification handler code here
AfxMessageBox(CString(m_ctrl1.GetControl()->textBox1->Text));
// m_ctrl1.GetControl()->button1->Click();
// how can I write this above line to perform click event?
}
I defined m_ctrl1 in ProgramDlg.h:
// ....
public:
CMFCApplication1Dlg(CWnd* pParent = NULL); // standard constructor
// Data member for the .NET User Control:
CWinFormsControl<WindowsFormsControlLibrary1::UserControl1> m_ctrl1;
// ....
p.s sorry for my bad english.
Thanks.
I solved my problem by visiting this link.
Hope to be useful for other developers.

MFC application title

I am creating a simple clock application using MFC. My application title appears as follows : "CLOCK - [CLOCK1]". How do I reset it to simply "CLOCK"?
FYI, I have enabled the Document-View architecture.
Put in this override of the MFC title:
void CMainFrame::OnUpdateFrameTitle(BOOL bAddToTitle)
{
SetWindowText(L"CLOCK");
}
There's an answer here, but I feel that the following solution is more "proper".
In addition to overriding CMainFrame::OnUpdateFrameTitle(), you also need to override CMainFrame::PreCreateWindow() as below:
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{ cs.style &= ~FWS_ADDTOTITLE;
return CFrameWndEx::PreCreateWindow(cs); // replace CFrameWndEx by CFrameWnd if
} // your CMainFrame is based on CFrameWnd
A note: it is better to use AfxSetWindowText(m_hWnd, _T("foo")) instead of SetWindowText(_T("foo")) to avoid excessive flicker, it checks that the text is different before setting the window text.
You can change it in visual editor by clicking on a your window and typing a title.
Or you can add this code in function OnInitDialog
this->SetWindowText(L"CLOCK");

How to create a MFC CSliderCtrl?

I created a new MFC project with MS VS 2010 using their wizard (I chose single document if it matters). Then, I created a dialog box and from the toolbox I dragged a new slider. Using VS's wizard (right click on dialog-->add class) I created a class for my dialog called MyDialog. Next, I used VS's wizard to add a variable to to slider.
So now, in my MyDialog class I have a CSliderCtrl and I can't get it to work.
I tried to use SetRange() and\or SetPos() in OnInitDialog() but they crash to program and throws an exception.
If I create a new CSliderCtrl in OnInitDialog() and set it's range with SetRange() like so, it does works.
BOOL MyDialog::OnInitDialog()
{
CSliderCtrl *TrackBar = new CSliderCtrl;
TrackBar->Create(WS_CHILD | WS_VISIBLE,CRect(20, 20, 60, 280),this, IDC_SLIDER1);
int min,max;
TrackBar->GetRange(min,max);
TrackBar->SetPos(10);
return TRUE;
}
But this doesn't work:
BOOL MyDialog::OnInitDialog()
{
m_mySlider.SetRange(1,100); //sending true\false doesn't matter
return TRUE;
}
Can anyone help me please?
From the code you shown, you missed this important line,
CDialogEx::OnInitDialog();
You should add SetRange and SetPos calls after the above line.

Clicking in an MFC edit box

I've created a read-only edit box in an MFC dialog box. I'm trying to have it so a user clicks in the edit box, which is read-only, it opens a file dialog, and then puts this value into the text box using UpdateData. I'm catching the ON_EN_SETFOCUS message but pressing OK on the file dialog respawns it, so I get caught in an infinite loop.
UpdateData(TRUE);
CFileDialog fileDialog(TRUE,NULL, NULL,OFN_HIDEREADONLY|OFN_FILEMUSTEXIST, _T("Text Files(*.txt)|*.txt||"));
if( fileDialog.DoModal() == IDOK )
{
configFile=fileDialog.GetPathName(); //Note to self, this includes filename, getPathName includes filename and path.
}
else
{
return;
}
UpdateData(FALSE);
If you've got any ideas on how this should be done, I would be very grateful.
Alright Mr. Lister I guess I'll add an answer.
First off I would preface this with I would probably simply add a button name "..." to launch the file dialog to the right of the edit box for opening the file dialog as that's the simplest solution and what most windows users will expect.
Another option however is to extend an MFC control. When deciding to extend a control you want to pick one that mostly has the desired behavior and that has a virtual destructor which lends itself to being a subclass. Since you want button like behavior CButton may be a good choice.
Your class interface might look something like this:
class CPathButton : public CButton
{
public:
enum { ID /*= IDC_BUTTON1*/ };
const CString GetPath() const;
const CString GetFileName() const;
const CString GetDirectory() const;
const CString GetExtension() const;
// other useful methods for setting file filters etc
protected:
// add ON_CONTROL(BN_CLICKED, ID, &OnClick) or ON_BN_CLICKED(ID, &OnClick)
DECLARE_MESSAGE_MAP()
// CFileDialog fdlg.DoModal(), m_path = fdlg.GetPathName(), SetWindowText(fdlg.GetFileTitle()), etc
afx_msg void OnClick();
// additional message handlers etc
private:
CString m_path; // save full path for after dialog is closed
};
You can add as much or as little customization as you want depending on if the control will be created dynamically, via the resource file, or whatever. The basic idea being that you display the currently selected file name on the button while storing the full path for other uses as a member so the user doesn't need to see the clutter of a long path with nested directories.
If you don't like the way it looks by default you can override OnPaint and handle WM_PAINT messages and use a custom font, size, or add ellipsis for a long file title. You could also handle re-sizing the button to fit the file title by using text metrics and GetTextExtent to ensure the name fits or simply display a CToolTipCtrl when they hover the mouse over the button so they can see the full name. The CMFCButton from the MFC feature pack in VS2008+ has tool tip functionality built in so if you inherit from that instead of CButton displaying a tool tip would be as simple as calling SetTooltip(m_path)
If you want to get really fancy you could use some of the uxtheme API or new windows animation API.
You can override PreTranslateMessage() in your dialog class, and determine if the edit control was clicked that way:
CEdit m_CEditCtrl;
// ...
BOOL YourDialogClass::PreTranslateMessage(MSG *pMsg)
{
if((pMsg->wParam == VK_LBUTTON) && (m_CEditCtrl.m_hWnd == pMsg->hwnd))
{
// open your file dialog
return TRUE; // Return that the message was translated and doesn't need to be dispatched
}
return CDialog::PreTranslateMessage(pMsg);
}
Update: You can also (and it may be a better idea) to override your CEdit control's CWnd::PreTranslateMessage() function. This would require deriving a class from CEdit.
If you are using VS2008 SP1 or above, the easiest way to ask for a path is with CMFCEditBrowseCtrl. It displays an edit control with a button. The steps to use it are:
Change your edit control's class to CMFCEditBrowseCtrl
Call EnableFileBrowseButton to tell it that you want to browse for files, not folders (you can set a filter and default extension)
When the user clicks the button, a file dialog appears, and when you click OK in it, the selected path is written in the edit control.