In my MFC application I used CSplitterWnd to create two panes and each pane is a CFormView dialog box. When run this GUI application the splitter is working and both panes are showing but all controls (button, edit box, combo box...) are disabled. Both dialog boxes have property of 'child' and 'no border'.
Did I miss something to enable all those conntrols on the pane view?
Thanks a lot for help.
CK
/////////// Header file
class CParentSelectionDlg : public CFormView
{
protected:
CParentSelectionDlg(); // protected constructor used by dynamic creation
DECLARE_DYNCREATE(CParentSelectionDlg)
// Form Data
public:
//{{AFX_DATA(CParentSelectionDlg)
enum { IDD = IDD_PARENT_SELECTION };
// NOTE: the ClassWizard will add data members here
//}}AFX_DATA
// Attributes
public:
// Operations
public:
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CParentSelectionDlg)
public:
virtual void OnInitialUpdate();
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
virtual ~CParentSelectionDlg();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
// Generated message map functions
//{{AFX_MSG(CParentSelectionDlg)
afx_msg void OnButtonSave();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
/////////////////////////////// CPP
IMPLEMENT_DYNCREATE(CParentSelectionDlg, CFormView)
CParentSelectionDlg::CParentSelectionDlg()
: CFormView(CParentSelectionDlg::IDD)
{
//{{AFX_DATA_INIT(CParentSelectionDlg)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
}
CParentSelectionDlg::~CParentSelectionDlg()
{
}
void CParentSelectionDlg::DoDataExchange(CDataExchange* pDX)
{
CFormView::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CParentSelectionDlg)
// NOTE: the ClassWizard will add DDX and DDV calls here
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CParentSelectionDlg, CFormView)
//{{AFX_MSG_MAP(CParentSelectionDlg)
ON_BN_CLICKED(IDC_BUTTON_SAVE, OnButtonSave)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CParentSelectionDlg diagnostics
#ifdef _DEBUG
void CParentSelectionDlg::AssertValid() const
{
CFormView::AssertValid();
}
void CParentSelectionDlg::Dump(CDumpContext& dc) const
{
CFormView::Dump(dc);
}
#endif //_DEBUG
void CParentSelectionDlg::OnInitialUpdate()
{
CFormView::OnInitialUpdate();
}
/////////////////////////////////////////////////////////////////////////////
// CParentSelectionDlg message handlers
void CParentSelectionDlg::OnButtonSave()
{
// TODO: Add your control notification handler code here
}
/// Thanks a lot
I bet your message map isn't set up correctly.
Can you post your code?
Related
I am using RAD Studio 10 working on a Windows VCL application. I have two Forms, Form1 (the MainForm in Unit1.cpp) and a secondary Form2 (in Unit2.cpp). I managed to embed Form2 inside of Form1. This is just a setup to illustrate the problem. My real project has multiple Forms.
When closing Form2, the VCL triggers the Form2::OnClose() event. Knowing that Form2 was created dynamically in Form1 (the MainForm), is there a Form1 event that will fire upon Form2 being closed? Or something inside Form1 to know that Form2 is closing?
I was thinking of customizing an event handler like OnChildFormClose but I couldn't make it.
I tried to wrap the code that I wanted to execute on Form1 when Form2 is closed in a public function and call it in the Form2::OnClose() event, and it worked to some extent, but it is not a good approach if you have multiple Forms.
//FROM THE unit1.cpp
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
#include "Unit2.h"
//-----------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//-----------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//-----------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
TForm2 *form2 = new TForm2(this);
form2->ManualDock(container);
form2->Show();
}
//FROM unit2.cpp
#include <vcl.h>
#pragma hdrstop
#include "Unit2.h"
//-----------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm2 *Form2;
//-----------------------------------------------------------------------
__fastcall TForm2::TForm2(TComponent* Owner)
: TForm(Owner)
{
}
//-----------------------------------------------------------------------
void __fastcall TForm2::Button1Click(TObject *Sender)
{
Close();
}
//-----------------------------------------------------------------------
Can I implement something like an OtherFormsonClose(*Sender) event in Form1 with a Sender that we can dynamically cast to check if it is Form2, or maybe I am wrong? I would appreciate some guidance.
You can declare a common event handler of type TCloseEvent, e.g. OtherFormClose(TObject *Sender, TCloseAction &Action); in the main form:
private: // User declarations
void __fastcall TForm1::OtherFormClose(TObject *Sender, TCloseAction &Action);
implementation
void __fastcall TForm1::OtherFormClose(TObject *Sender, TCloseAction &Action)
{
Action = caFree;
TForm2 *f2 = dynamic_cast<TForm2 *>(Sender);
if (f2) {
ShowMessage(String("Form2 closing")); //Do stuff
}
}
(or use Sender to inspect which form)
Then when you create other forms in code, e.g. Form2, you assign
TForm2 *form2 = new TForm2(this);
form2->OnClose = OtherFormClose;
// etc
Ok I found something interesting after reading this, this, this, and this.
So basically, a VCL Delphi/C++Builder application uses Windows Form Messages for comunication, and we can override the virtual function WndProc to catch a specific message, but it must be some unique message because VCL uses a lot of messages and if you don't tread carefully things might blow up; This will translate to a custom event handler on the main form.
So what I did is :
Passed the MainForm handle to Form2 in the constructor to be saved on a Form2 private var and to be used only for the messaging.
Generate a specific ID that I use to tag the message to make it stand out
overriding the WndProc and filtering messages with a specific ID so we know that Form2 is closing.
Test it and it worked, maybe someone have a better idea.
//From unit2.h---------------------------------------------------------
class TForm2 : public TForm
{
__published: // IDE-managed Components
TButton *Button1;
void __fastcall Button1Click(TObject *Sender);
void __fastcall FormClose(TObject *Sender, TCloseAction &Action);
private: // User declarations
HWND mfhandle;
public: // User declarations
__fastcall TForm2(TComponent* Owner, HWND mainformhandle);
};
//From unit2.cpp---------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit2.h"
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm2 *Form2;
const UINT uiMyCopyDataID = RegisterWindowMessage(TEXT("MyCopyDataID"));
__fastcall TForm2::TForm2(TComponent* Owner,HWND mainformhandle)
: TForm(Owner)
{
mfhandle = mainformhandle;
}
void __fastcall TForm2::Button1Click(TObject *Sender)
{
Close();
}
void __fastcall TForm2::FormClose(TObject *Sender, TCloseAction &Action)
{
//Notify the mainForm and say Hey I am closing now
PostMessage(mfhandle, uiMyCopyDataID, 0, 0);
}
//From unit1.h---------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE-managed Components
TPanel *container;
void __fastcall FormCreate(TObject *Sender);
void __fastcall FormUnDock(TObject *Sender, TControl *Client, TWinControl *NewTarget,
bool &Allow);
private: // User declarations
protected:
void __fastcall TForm1::WndProc(TMessage &Message); //Added THIS
public: // User declarations
__fastcall TForm1(TComponent* Owner);
};
//From unit1.cpp-------------------------------------------------------
const UINT uiMyCopyDataID = RegisterWindowMessage(TEXT("MyCopyDataID"));
void __fastcall TForm1::WndProc(TMessage &Message)
{
if (Message.Msg == uiMyCopyDataID)
{
//Do SomeThing here
ShowMessage("Form2 is closing");
}
TForm::WndProc(Message);
}
Ok, So far it works and custom messages MUST be in the WM_USER (0x0400 - 0x7FFF) range.
I encountered a little problem with QListView. I wanted to add a header to this object. However, I found no method to do such a task. I have seen several posts on SO and Qt forums but did not found anything relevant to my needs. I remembered that, in the past, I have used something that could be adapted to QListView (I think it was inspired by a Qt example but do not remember which one).
For those who need to display a header on the top of the QListView, I will post the code here. If someone find something false, or unadapted, please let me know. Here is the code:
Header
class MainMenuListView : public QListView
{
Q_OBJECT
class Header : public QWidget
{
public:
Header(MainMenuListView* parent);
QSize sizeHint() const;
protected:
void paintEvent(QPaintEvent* event);
private:
MainMenuListView* menu;
};
public:
MainMenuListView(QWidget* parent = nullptr, const QString& header = QString("Header"));
void headerAreaPaintEvent(QPaintEvent *event);
int headerAreaWidth();
protected:
void resizeEvent(QResizeEvent* event);
private:
QWidget* headerArea;
QString headerText;
};
Implementation
MainMenuListView::Header::Header(MainMenuListView* parent) : QWidget(parent), menu(parent) {}
QSize MainMenuListView::Header::sizeHint() const
{
return QSize(menu->headerAreaWidth(), fontMetrics().height());
}
void MainMenuListView::Header::paintEvent(QPaintEvent* event)
{
menu->headerAreaPaintEvent(event);
}
MainMenuListView::MainMenuListView(QWidget* parent, const QString& header) : QListView(parent), headerText(header)
{
headerArea = new Header(this);
setViewportMargins(0, fontMetrics().height(), 0, 0);
}
void MainMenuListView::headerAreaPaintEvent(QPaintEvent* event)
{
QPainter painter(headerArea);
painter.fillRect(event->rect(), Qt::lightGray);
painter.setPen(Qt::black);
painter.drawText(0, 0, headerArea->width(), fontMetrics().height(), Qt::AlignCenter, headerText);
}
int MainMenuListView::headerAreaWidth()
{
return width();
}
void MainMenuListView::resizeEvent(QResizeEvent* event)
{
QListView::resizeEvent(event);
headerArea->adjustSize();
}
Tested and working under Debian Qt5.x.x
Result:
I have created MFC dialog form resource. After I ask Form Wizard to create class for this resource it generates header and cpp file correctly except one thing - my class does not recognize control ID that appears in class code like ID_BLABLABLA. After including resource.h - everything goes fine. But why wizard doesn't do it automatically?
This is dialog header:
#pragma once
// dlg4 dialog
class dlg4 : public CDialogEx
{
DECLARE_DYNAMIC(dlg4)
public:
dlg4(CWnd* pParent = NULL); // standard constructor
virtual ~dlg4();
// Dialog Data
enum { IDD = IDD_DIALOG2 };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
DECLARE_MESSAGE_MAP()
};
dialog cpp:
// dlg4.cpp : implementation file
//
#include "stdafx.h"
#include "dlg4.h"
#include "afxdialogex.h"
// dlg4 dialog
IMPLEMENT_DYNAMIC(dlg4, CDialogEx)
dlg4::dlg4(CWnd* pParent /*=NULL*/)
: CDialogEx(dlg4::IDD, pParent)
{
}
dlg4::~dlg4()
{
}
void dlg4::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(dlg4, CDialogEx)
END_MESSAGE_MAP()
// dlg4 message handlers
The wizard includes the app class h file. The app class h file includes resource.h
It is quite reasonable for you to remove the include of app.h and replace it with resource.h
I am desparately in search of MFC dll sample that updates an edit field on a dialog derived from CDialog with a string using only the callback function. I realize static functions are the ones that are used in the calback.
I guess you realise that the dll should have something (like a separate thread, or a certain third-party message handler for this model. So, this is out of scope for this question.
Back to the question:
In order to create a callback function you need to "typedef" its prototype and pass the address of this function from your calling app to the dll. In your dll "h" file specify this:
// This is the Mfcdll1.h header file
// You should have some code created by the wizard, similar to the following:
#ifdef MYDLL_EXPORTS
#define MYDLL_API __declspec(dllexport)
#else
#define MYDLL_API __declspec(dllimport)
#endif
// This is the typedef for your callback function type:
typedef void (CALLBACK* MY_DLL_CALLBACK)
(
LPVOID lpParam,
LPCTSTR lpszMessage
);
class CMyDllApp : public CWinApp
{
public:
CMyDllApp();
// etc.
void SetCallBack(MY_DLL_CALLBACK pCallback, LPVOID pCallbackParm){m_pCallback = pCallback; m_pCallbackParm = pCallbackParm;}
private:
MY_DLL_CALLBACK m_pCallback;
LPVOID m_pCallbackParm;
};
MYDLL_API void SetCallBack(MY_DLL_CALLBACK pCallback, LPVOID pCallbackParm);
In your dll's cpp file specify this:
CMyDllApp::CMyDllApp()
{
m_pCallback = NULL;
m_pCallbackParm = NULL;
}
MYDLL_API void SetCallBack(MY_DLL_CALLBACK pCallback, LPVOID pCallbackParm)
{
theApp.SetCallBack(pCallback, pCallbackParm);
}
Now, if your dll wants to call the callback function, all you need to do is:
CMyDllApp::SendCallbackToTheCaller(LPCTSTR lpszMessage)
{
if(m_pCallback) (*(m_pCallback))(m_pCallbackParm, lpszMessage);
}
At this stage you have done coding in the dll. Now, all you need to do is specify the static callback function in your dialog that will update you Edit control, similar to this:
In the h file:
// TestDlg.h - My Test dialog with the Edit Control
CTestDlg: public CDialog
{
public:
static void CALLBACK StatusCallback(LPVOID lpParam, LPCTSTR lpszMessage);
};
In the cpp file:
BOOL CTestDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Insert this line along your stuff
::SetCallback((MY_DLL_CALLBACK)StatusCallback, (LPVOID)this);
}
void CALLBACK CTestDlg::StatusCallback(LPVOID lpParam, LPCTSTR lpszMessage)
{
CTestDlg* pTestDlg = reinterpret_cast<CTestDlg*>(lpParam);
ASSERT(pTestDlg!=NULL);
pTestDlg->m_edStatus.SetWindowText(lpszMessage);
}
I make a interface class in C++ for voice recognition, i´m using the Julius API. http://julius.sourceforge.jp/en_index.php?q=index-en.html.
Well, my class has some events, these events will be triggered by the Julius API.
The Julius API has the function call callback_add with this signature:
int callback_add (Recog *recog, int code, void(*func)(Recog *recog, void *data), void data)
I using some 'proxy' functions to Invoke the events and passing this functions to callback_add.
If the property event is static, it works fine, but if is a non static, inside the proxy function the property not be recognized.
The difficult is because I have to use the callback_add function and can't modify this.
Here is a summary of the class with 2 events (static and non-static)
Header
#ifndef FALAENGINE_H_
#define FALAENGINE_H_
#pragma once
extern "C"{
#include <julius/julius.h>
}
namespace FalaAPI {
public ref class FalaEngine
{
public:
FalaEngine();
~FalaEngine();
// Events
delegate void OnRecognizedDele(FalaAPI::RecoResult^ result);
static property OnRecognizedDele^ OnRecognized;
delegate void OnEngineStartDele();
property OnEngineStartDele^ OnEngineStart;
private:
Recog *recog;
Jconf *jconf;
};
}
#endif /* FALAENGINE_H_*/
Source
#include "stdafx.h"
using System::String;
using System::Console;
#include "FalaEngine.h"
#include <windows.h>
namespace FalaAPI{
void StartOnEngineStart()(Recog *recog, void * dummy){
if(FalaEngine::OnEngineStart->GetInvocationList()->Length > 0)
FalaEngine::OnEngineStart->Invoke();
}
void StartOnRecognized()(Recog *recog, void * dummy){
if(FalaEngine::OnRecognized->GetInvocationList()->Length > 0)
FalaEngine::OnRecognized->Invoke();
}
FalaEngine::FalaEngine(){
recog = j_recog_new();
jconf = j_jconf_new();
//Julius callback Functions
callback_add(recog, CALLBACK_EVENT_PROCESS_ONLINE, StartOnEngineStart, NULL);
callback_add(recog, CALLBACK_RESULT, StartOnRecognized, NULL);
}
}
The problem occurs inside StartOnEngineStart function:
error C2227: left of '->GetInvocationList' must point to class/struct/union/generic type
A non-static member exists separately in each instance. You haven't specified which instance contains the delegate you want to inspect, you've only specified a class (and there may be many instances).
Try using the dummy parameter to pass your instance. But be careful, because the garbage collector will move objects around unless you have pinned them, so simply passing the address will not work. You need to create and pass a GCHandle instead. (Be careful not to leak the GCHandle, or your object will never be released)
Something like this should be effective:
ref class FalaEngine;
struct EngineHandle
{
gcroot<FalaEngine^> handle;
EngineHandle(FalaEngine^ engine) : handle(engine) {}
};
public ref class FalaEngine
{
clr_scoped_ptr<EngineHandle> callback_ptr;
public:
FalaEngine();
~FalaEngine();
// Events
delegate void OnRecognizedDele(FalaAPI::RecoResult^ result);
property OnRecognizedDele^ OnRecognized;
delegate void OnEngineStartDele();
property OnEngineStartDele^ OnEngineStart;
private:
Recog *recog;
Jconf *jconf;
};
void StartOnEngineStart(Recog *recog, void * dummy)
{
FalaEngine^ that = static_cast<EngineHandle*>(dummy)->handle;
that->OnEngineStart(); // C++/CLI already checks if the invocation list is empty
}
void StartOnRecognized(Recog *recog, void * dummy)
{
FalaEngine^ that = static_cast<EngineHandle*>(dummy)->handle;
that->OnRecognized(recog->get_result());
}
FalaEngine::FalaEngine()
: callback_ptr(new EngineHandle(this))
{
recog = j_recog_new();
jconf = j_jconf_new();
//Julius callback Functions
callback_add(recog, CALLBACK_EVENT_PROCESS_ONLINE, StartOnEngineStart, callback_ptr.get());
callback_add(recog, CALLBACK_RESULT, StartOnRecognized, callback_ptr.get());
}
The clr_scoped_ptr class is here. There are not many license requirements, make sure you follow them though if you use it.