I have a MFC class derived from CWnd directly, not from CFrameWnd, And I use a CSplitterWnd as a member variable to create a Splitter and two views in OnCreate message handler.
But it shows nothing in the client area.
What's wrong with my approach? Do I have to use a CFrameWnd derived class?
Thanks
Code Snippet:
// CMyWnd.h
class CMyWnd : public: CWnd
{
DECLARE_DYNCREATE(CMyWnd)
public:
CMyWnd();
virtual ~CMyWnd();
// blahblah
protected:
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnPaint();
DECLARE_MESSAGE_MAP()
private:
CXTPSplitterWnd m_WndSplitter;
}
// CMyWnd.cpp
int CMyWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CWnd::OnCreate(lpCreateStruct) == -1)
{
return -1;
}
if (!m_WndSplitter.CreateStatic(this, 2, 1))
return -1;
if (
!m_WndSplitter.CreateView(0, 0, RUNTIME_CLASS(CEditView),
CSize(100, 100), NULL) ||
!m_WndSplitter.CreateView(1, 0, RUNTIME_CLASS(CEditView),
CSize(100, 200), NULL)
)
{
m_WndSplitter.DestroyWindow();
return -1;
}
return 0;
}
The problem is that you create views. A element derived from a CView class must reside in a CFrameWnd derived class.
You can create a splitter in a CWnd derived class, but than the splitter window must host CWnd derived windows too.
Related
Needing several variations of a simple modal dialog with an MFC project, I wrote a simple class, CDialogDlg, which extends the standard MFC CDialog class and contains hooks for implementation specific callbacks that are normally implemented as methods in the class extending CDialog. These callbacks, if provided, are used when processing some events such as dialog initialization, OK button processing, and the DoDataExchange() function which provides the method for interfacing the dialog class variables and the actual dialog controls.
My first version of this class used standard functions for the callbacks. So the callback setting methods of the extended class set a function pointer with the address of the standard function.
void SetDataExchangeCallBack(void(*f)(CDataExchange* pDX)) { funcDX = f; };
void SetInitCallBack(void(*f)(CWnd *dlgWnd)) { funcInit = f; }
void SetOnOkCallBack(void(*f)(CWnd *dlgWnd, CDocument *pDoc)) { funcOK = f; }
Then I realized I should be able to use a lambda instead of a standard function. The first version of the lambdas, which used the same parameters as the standard functions, that did not capture any variables compiled fine and worked fine with the existing methods and function pointers in the extended class.
However when I tried to capture a variable in the lambda specified in the callback setter method, I had compile errors.
Reviewing Passing capturing lambda as function pointer and C++ lambda with captures as a function pointer I get that trying to do a lambda with capture will not compile with the definitions of function pointer and callback setting function that I am using.
So I decided that I should add an additional lambda specific function pointer within the class along with an override of the existing methods with an additional method setting the callbacks with the lambda specific function pointer within the class.
Question: What should the function pointer definition and the parameter definition in the function that sets the callback for accepting a lambda with captured variables look like? I would like to capture the local variable CPCSampleDoc *pDoc in the method CPCSampleView::OnDisplayReportList (). This variable contains a pointer to the CDocument derived object for the view that I would like to capture rather than using the method of storing it and doing the static_cast to get it back in the current version of the lambdas that do not capture.
Source Code
The CDialogDlg class which extends CDialog looks like the following:
class CDialogDlg : public CDialog
{
// Construction
void(*funcDX)(CDataExchange* pDX);
void(*funcDXpDoc)(CDataExchange* pDX, CDocument *pDoc);
void(*funcInit)(CWnd *dlgWnd);
void(*funcOK)(CWnd *dlgWnd, CDocument *pDoc);
CDocument *m_pDoc;
public:
CDialogDlg(UINT nIDTemplate, CWnd* pParentWnd = NULL, void(*f)(CDataExchange* pDX) = NULL) : CDialog(nIDTemplate, pParentWnd) { funcDX = f; funcInit = NULL; }
CDialogDlg(UINT nIDTemplate, CDocument *pDoc, CWnd* pParentWnd = NULL, void(*f)(CDataExchange* pDX, CDocument *pDoc) = NULL) : CDialog(nIDTemplate, pParentWnd) {
funcDX = NULL; funcDXpDoc = f; funcInit = NULL; m_pDoc = pDoc;
}
void SetDataExchangeCallBack(void(*f)(CDataExchange* pDX)) { funcDX = f; };
void SetInitCallBack(void(*f)(CWnd *dlgWnd)) { funcInit = f; }
void SetOnOkCallBack(void(*f)(CWnd *dlgWnd, CDocument *pDoc)) { funcOK = f; }
// Dialog Data
//{{AFX_DATA(CCashierNoDlg)
//}}AFX_DATA
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CCashierNoDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
protected:
// Generated message map functions
//{{AFX_MSG(CCashierNoDlg)
virtual BOOL OnInitDialog();
virtual void OnOK();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
BEGIN_MESSAGE_MAP(CDialogDlg, CDialog)
//{{AFX_MSG_MAP(CCashierNoDlg)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
void CDialogDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
if (funcDX) funcDX(pDX);
if (funcDXpDoc) funcDXpDoc(pDX, m_pDoc);
//{{AFX_DATA_MAP(CCashierNoDlg)
//}}AFX_DATA_MAP
}
BOOL CDialogDlg::OnInitDialog()
{
CDialog::OnInitDialog();
if (funcInit) funcInit(this);
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
void CDialogDlg::OnOK()
{
if (funcOK) funcOK(this, m_pDoc);
CDialog::OnOK();
}
The CView method which is triggered by a menu selection displays a dialog with a list of items to choose from. The dialog presented is a CDialogDlg object with a specific dialog template ID. For special processing, I am using two different callbacks which are currently using a lambda that does not capture. The result is the following:
void CPCSampleView::OnDisplayReportList ()
{
CPCSampleDoc *pDoc = GetDocument();
CDialogDlg myDialog(IDD_DIALOG_REPORTLIST, pDoc, this, [](CDataExchange* pDX, CDocument *pDoc) {
if (pDX->m_bSaveAndValidate) {
}
else {
CPCSampleDoc *pDocDoc = static_cast<CPCSampleDoc *>(pDoc);
POSITION pos = NULL;
do {
CPCSampleDoc::ListReportList sectionHeader;
CListBox *x = static_cast<CListBox *>(pDX->m_pDlgWnd->GetDlgItem(IDC_LIST1));
pos = pDocDoc->GetReportSectionHeader(pos, sectionHeader);
x->AddString(sectionHeader.m_SectionTitle);
} while (pos);
}
});
myDialog.SetOnOkCallBack([](CWnd *dlgWnd, CDocument *pDoc) {
CPCSampleDoc *pDocDoc = static_cast<CPCSampleDoc *>(pDoc);
CListBox *x = static_cast<CListBox *>(dlgWnd->GetDlgItem(IDC_LIST1));
int iPtr = x->GetCurSel();
POSITION pos = NULL;
do {
CPCSampleDoc::ListReportList sectionHeader;
pos = pDocDoc->GetReportSectionHeader(pos, sectionHeader);
if (iPtr < 1) {
pDocDoc->MoveToReportSectionHeader(sectionHeader.m_ListOffset);
break;
}
iPtr--;
} while (pos);
});
myDialog.DoModal();
}
You might use std::function to allow capturing lambdas and other functors and regular function pointers:
void(*funcDX)(CDataExchange* pDX);
void(*funcDXpDoc)(CDataExchange* pDX, CDocument *pDoc);
void(*funcInit)(CWnd *dlgWnd);
void(*funcOK)(CWnd *dlgWnd, CDocument *pDoc);
becomes
std::function<void(CDataExchange*)> funcDX;
std::function<void(CDataExchange*, CDocument*)> funcDXpDoc;
std::function<void(CWnd*)> funcInit;
std::function<void(CWnd*, CDocument*)> funcOK;
You setter/constructor should change pointer function to std::function too:
void SetInitCallBack(std::function<void(CWnd*)> f) { funcInit = f; }
Else your usage is identical:
if (funcDX) funcDX(pDX);
or
funcInit = nullptr;
I have make a view in dialog in MFC.
and I want to call a function from dialog to view.
How can I call each other function in the different view?
Here is code Also I have attached as link
void Cmfc_test5Dlg::OnDropFiles(HDROP hDropInfo)
{
int nFiles;
char szPathName[MAX_PATH];
CString strFileName;
nFiles = ::DragQueryFile( hDropInfo, 0xFFFFFFFF, szPathName, MAX_PATH );
{
::DragQueryFile(hDropInfo, 0, szPathName, MAX_PATH);
}
::DragFinish(hDropInfo);
CDialog::OnDropFiles(hDropInfo);
DoDisplayImage(); <---Here is My call function.
CDialogEx::OnDropFiles(hDropInfo);
}
and Here is another function
void CTestview::DoDisplayImage()
{
CDC *pDC = GetDC();
if (pDC != NULL && m_Image.isValid() )
{
CRect rectClient;
GetClientRect(rectClient);
pDC->FillSolidRect(rectClient,pDC->GetBkColor());
// Set up the Windows bitmap header
BITMAPINFOHEADER bmi;
bmi.biSize = sizeof(BITMAPINFOHEADER); // Size of structure
bmi.biWidth = m_Image.columns(); // Bitmaps width in pixels
bmi.biHeight = (-1)*m_Image.rows(); // Bitmaps height n pixels
bmi.biPlanes = 1; // Number of planes in the image
bmi.biBitCount = 32; // The number of bits per pixel
bmi.biCompression = BI_RGB; // The type of
...
and the DoDisplayImage function called from here
void CTestview::OnDraw(CDC* pDC)
{
CDocument* pDoc = GetDocument();
// TODO: add draw code here
DoDisplayImage();
}
But, as you know, My problem is that I can't call the DoDisplayImage() in void Cmfc_test5Dlg::OnDropFiles(HDROP hDropInfo) function also I want to get szPathName of OnDropFiles function in DoDisplayImage.
What should I do for solving for this problems?
update 1
There are error when I make as following.
1>d:\work\mfc_test5\mfc_test5\Testview.h(29): error C2143: syntax error : missing ';' before '*'
1>d:\work\mfc_test5\mfc_test5\Testview.h(29): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
1>d:\work\mfc_test5\mfc_test5\Testview.h(29): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
1>
1>Build FAILED.
In TestView.h,
#pragma once
// CTestview view
class CTestview : public CScrollView
{
DECLARE_DYNCREATE(CTestview)
protected:
CTestview(); // protected constructor used by dynamic creation
virtual ~CTestview();
public:
#ifdef _DEBUG
virtual void AssertValid() const;
#ifndef _WIN32_WCE
virtual void Dump(CDumpContext& dc) const;
#endif
#endif
protected:
virtual void OnDraw(CDC* pDC); // overridden to draw this view
virtual void OnInitialUpdate(); // first time after construct
DECLARE_MESSAGE_MAP()
public:
CTestView* pTestView; <---- is this right?
};
Here is code Also I have attached as link
update2
I have done as following.
// mfc_test5Dlg.h : header file
//
#pragma once
#include "afxcmn.h"
// Cmfc_test5Dlg dialog
class CTestview;//adding
class Cmfc_test5Dlg : public CDialogEx
{
// Construction
public:
Cmfc_test5Dlg(CWnd* pParent = NULL); // standard constructor
CTestview* pTestView;//adding
// Dialog Data
enum { IDD = IDD_MFC_TEST5_DIALOG };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
Cmfc_test5Dlg::Cmfc_test5Dlg(CWnd* pParent /*=NULL*/)
: CDialogEx(Cmfc_test5Dlg::IDD, pParent)
, m_CString(_T(""))
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
pTestView = NULL; //adding
}
But I can't understand at this part how to implement it in my case.
You have to set pTestView each time the dialog is created. For example:
void CTestview::foo()
{
Cmfc_test5Dlg dlg(...);
dlg.pTestView = this;
dlg.DoModal();
}
In MFC's Document/View architecture you do NOT call View's method from outside.
The way to get the View to draw something is through its Document by updating the content and invoking UpdateAllViews(), possibly with hint (for optimization).
This is basic c++. You have class A and B which are not related. How do you access A from B? Try this console program:
class TA {
public:
void foo() {
cout << "TA\n";
}
};
class TB {
public:
TA *A;
TB() {
A = nullptr;
}
void foo() {
if (A)
A->foo();
}
};
int main()
{
TA *a = new TA;
TB *b = new TB;
b->A = a;
b->foo(); //prints "TA"
delete a;
delete b;
return 0;
}
It's the same in MFC. You can declare a pointer to CTestView and use that pointer in the dialog class:
class CTestview;
class Cmfc_test5Dlg : public CDialogEx
{
public:
CTestView* pTestView;
...
};
include "TestView.h"
Cmfc_test5Dlg::Cmfc_test5Dlg()
{
pTestView = NULL;
}
You have to set pTestView each time the dialog is created. For example:
void CTestview::foo()
{
Cmfc_test5Dlg dlg(...);
dlg.pTestView = this;
dlg.DoModal();
}
Then use pTestView->DoDisplayImage() anywhere in Cmfc_test5Dlg. For example:
void Cmfc_test5Dlg::OnDropFiles(HDROP hDropInfo)
{
...
if (pTestView != NULL)
pTestView->DoDisplayImage();
}
Another way is to use global variables:
CTestview *global_TestView;
CTestview::CTestview()
{
global_TestView = this;
...
}
//---- another cpp file:
extern CTestview *global_TestView;
...
void Cmfc_test5Dlg::OnDropFiles(HDROP hDropInfo)
{
...
if (global_TestView != NULL)
global_TestView->DoDisplayImage();
}
But this method can fail for a number of reason. For example if there could be more than one CTestview.
ps, don't call CDialogEx::OnDropFiles(hDropInfo); and CDialogEx::OnDropFiles(hDropInfo); consecutively, it doesn't make sense.
So you want to display an image in the view whenever a file is dropped on a child dialog?
Supply a pointer to the view as additional parameter in the constructor of the dialog and store it in the dialogs member variable m_view. Then call it:
m_view->DoDisplayImage();
I'm trying to access a dialog item from a function that is not in the same class as the dialog class. How can I do that?
Example:
class AnotherClass : CClas
{
AnotherClass();
public:
void MyFunction();
};
void AnotherClass::MyFunction() //Message overwriting, can't change parameters
{
CClass* temp = (CClass*)GetDlgItem(IDC_ID); //Reference to dialog item IDC_ID
temp->DoSomething(); //This gives me an assertion error
}
I know I can use "this" if it is the same dialog item than the message, but I want to access another dialog item.
Thanks for your attention.
Solution:
As suggested by Moo-Juice, you can simply pass the dialog when you instantiate the class. In my case, I couldn't do that. For some reason subclassing didn't work that way. If you face the same issue when doing an application in MFC , you can create a pointer to a CDialog and pass it your main dialog at OnInitDialog():
Example (Class):
class AnotherClass : CClass
{
AnotherClass();
public:
void MyFunction();
CDialog * mainDialog;
};
void AnotherClass::MyFunction() //Message overwriting, can't change parameters
{
CClass* temp = (CClass*)mainDialog->GetDlgItem(IDC_ID); //Reference to dialog item IDC_ID
temp->DoSomething(); //This gives me an assertion error
}
Example (OnInitDialog()):
MyMainDialog::OnInitDialog()
{
...
AnotherClass obj; //Instantiate class
obj->mainDialog = this;
return true;
}
In this example simply passing it as a parameter when creating the object makes more sense. It just didn't work with me for what I was doing.
Hope it helps anyone with a similar question.
When you instantiate AnotherClass, pass it the dialog class:
class AnotherClass
{
private:
CDialog& dialog_;
public:
AnotherClass(CDialog& dialog) : dialog_(dialog) { }
void MyFunction();
};
void AnotherClass::MyFunction()
{
CClass* temp = (CClass*)dialog_.GetDigItem(IDC_ID);
temp->doSOmething();
}
I know a Moblet object can subclass a timerlistener class but can a Widget also subclass the TimerListener?
With my code below I am getting an error `addTimer' was not declared in this scope:
class MyWidget : public QAButton, public TimerListener
// The class QAButton inherits from Label & PointerListener
{
MyWidget( MAUI::Widget *nParent, QAScreen *nDestinationScreen )
: Widget( 0, 0, 0, 0, nParent )
{
// Constructor:
}
void initAnimationTimer()
{
// Post:
addTimer( this, 20, 0 ); // Error here "`addTimer' was not declared in this scope"
}
void runTimerEvent()
{
// Post:
}
};
TimerListener is an interface (it has the pure virtual function runTimerEvent) which any class can implement.
You get an error when trying to access addTimer in MyWidget because it is a function of the MAUtil::Environment class. You can access it through the singleton MAUtil::Environment::getDefault().
MFC Library Reference
CWnd::OnLButtonDown
void CMyCla::OnLButtonDown(UINT nFlags, CPoint point)
{
CWnd::OnLButtonDown(nFlags, point);
}
void CMyTreeCla::OnLButtonDown(UINT nFlags, CPoint point)
{
CTreeCtrl::OnLButtonDown(nFlags, point);
}
I know the inheritance.
class CTreeCtrl : public CWnd
{
......
}
Is there any clear rule to follow when i want to call OnLButtonDown()?
Thank you.
I think this is what you want.
In your class header, you will need to declare the message map, and also write the function header.
Class myCWnd : public CWnd
{
DECLARE_MESSAGE_MAP() //note, no semi colon
afx_msg void OnLButtonDown( UINT nFlags, CPoint pt );
};
in the cpp file:
BEGIN_MESSAGE_MAP(myCWnd, CWnd)
ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()
void myCWnd::OnLButtonDown( UINT nFlags, CPoint pt )
{
//do what you want here
CWnd::OnLButtonDown(nFlags, pt); //call base class method
}
If you want the parent class implementation to be called first then you call the parent class's OnLButtonDown() and then add your implementation.
Usually, you do what you want to do with the event in your implementation and then you call the implementation of the parent class. This codeguru post shows a nice example in step 2 of the tutorial. But this depends on what exactly you want to do with the OnLButtonDown event, so it might be the other way around in your case.
I assume the inheritance in your example is as follows:
class CMyCla : public CWnd
{
}
class CMyTreeCla : public CTreeCtrl
{
......
}
So indeed, as you do, you do your thing in either OnLButtonDown and then call the parent implementation:
void CMyCla::OnLButtonDown(UINT nFlags, CPoint point)
{
// Your stuff here
// blah
// end your stuff
CWnd::OnLButtonDown(nFlags, point);
}
void CMyTreeCla::OnLButtonDown(UINT nFlags, CPoint point)
{
// Your stuff here
// blah
// end your stuff
CTreeCtrl::OnLButtonDown(nFlags, point);
}