问题
Hello,
Since a few weeks, we are trying to "transform" a MFC dialog into a "MFC form" which can be embedded into a WinForm User Control.
We've succeeded to do that:
- We made a WinForm User Control, called Dlg_WU_MFC_Container
- When created, the UC creates the MFC form called CDlgEdgeType
- Then, every time the UC is resized or moved, we also move and resize the MFC form
Here is the code (I tried to remove a lot of unnecessary stuff..):
Dlg_WU_MFC_Container.h:
#pragma once
public ref class Dlg_WU_MFC_Container : public System::Windows::Forms::UserControl
{
private:
CDlgEdgeType* _dialog;
CWnd *_wnd;
private: //---Local Controls
System::ComponentModel::IContainer^ components;
public:
Dlg_WU_MFC_Container();
~Dlg_WU_MFC_Container();
!Dlg_WU_MFC_Container();
template<class T, class HP>
void InitializeContainer() {
CDlgEdgeType =
}
private:
void RefreshEmbeddedSize();
#pragma region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
void InitializeComponent(void)
{
this->SuspendLayout();
//
// Dlg_WU_MFC_Container
//
this->AutoScaleDimensions = System::Drawing::SizeF(96, 96);
this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Dpi;
this->ForeColor = System::Drawing::SystemColors::WindowText;
this->Margin = System::Windows::Forms::Padding(0);
this->Name = L"Dlg_WU_MFC_Container";
this->Size = System::Drawing::Size(816, 480);
this->SizeChanged += gcnew System::EventHandler(this, &Dlg_WU_MFC_Container::evSizeChanged);
this->VisibleChanged += gcnew System::EventHandler(this, &Dlg_WU_MFC_Container::evVisibleChanged);
this->ResumeLayout(false);
}
#pragma endregion
private: System::Void evSizeChanged(System::Object^ sender, System::EventArgs^ e);
private: System::Void evVisibleChanged(System::Object^ sender, System::EventArgs^ e);
};
Dlg_WU_MFC_Container.cpp:
#include "Dlg_WU_MFC_Container.h"
#include "DlgEdgeType.h"
Dlg_WU_MFC_Container::Dlg_WU_MFC_Container()
{
InitializeComponent();
_wnd = NULL;
_dialog = new CDlgEdgeType();
}
Dlg_WU_MFC_Container::~Dlg_WU_MFC_Container()
{
if (components)
{
delete components;
}
this->!Dlg_WU_MFC_Container();
}
Dlg_WU_MFC_Container::!Dlg_WU_MFC_Container()
{
// We need to detach the handle to free it for other usage
if (_wnd) {
_wnd->Detach();
delete _wnd;
_wnd = NULL;
}
if (_dialog) {
delete _dialog;
_dialog = NULL;
}
}
System::Void Dlg_WU_MFC_Container::evSizeChanged(System::Object^ sender, System::EventArgs^ e) {
RefreshEmbeddedSize();
}
// Inform the embedded form to adapt to its new size
void Dlg_WU_MFC_Container::RefreshEmbeddedSize() {
if (_dialog && _isShown) {
CRect containerWnd;
containerWnd.left = this->Left;
containerWnd.right = this->Right;
containerWnd.top = this->Top;
containerWnd.bottom = this->Bottom;
_dialog->ReplaceEmbeddedForm(containerWnd);
}
}
System::Void Dlg_WU_MFC_Container::evVisibleChanged(System::Object^ sender, System::EventArgs^ e) {
// _isShown basically useless.. !
if (Visible != _isShown) {
_isShown = Visible;
if (_dialog) {
if (Visible) {
void *handle = Handle.ToPointer();
if (handle) {
// We need to create a new CWnd which will contain
// the handle of the current WinForm control where
// the embedded MFC will be contained
_wnd = new CWnd();
_wnd->Attach((HWND)handle);
_dialog->Create(_wnd);
RefreshEmbeddedSize();
}
} else {
// When the control is not shown anymore, we need to free
// the handle so another control can use it (the handle
// is stored in the MFC permanent map)
_wnd->Detach();
_dialog->DestroyWindow();
delete _wnd;
_wnd = NULL;
}
}
}
}
CDlgEdgeType.cpp:
void CDlgEdgeType::ReplaceEmbeddedForm(CRect &rect) {
if (!GetSafeHwnd()) {
return;
}
SetWindowPos(NULL, rect.left, rect.top, rect.Width(), rect.Height(), SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
}
This stuff is working "great" : CDlgEdgeType is well shown in our application and when the application is resized or moved, everything goes well.
Here is my issue: CDlgEdgeType has Dlg_WU_MFC_Container as parent, great. But the latter doesn't know that the MFC form is a "child".. so the focus is kind of lost and the Arrow keys and the Tab keys simply do not work.
A thing you should know is that Dlg_WU_MFC_Container is added to TabPages of a custom TabControl. So, if the user tries to navigate through MFC form's controls or he tries to navigate through a TreeView with the Arrow keys, the TabControl seems to take over the focus and will change tab.. which is not convenient D:
I have no idea how to solve that issue, neither my colleagues.
Maybe the way we integrate the MFC is wrong, but there are no really topics about that (we see more "Embedding WinForm forms into MFC"..). Also, as our software has a big history, we cannot simply recreate CDlgEdgeType. It's a big dialog and, in fact, we have 7 dialogs like that, the code implements template but I removed them for the clarity of this message..
Thank you !
Sbizz.
回答1:
Well, we find a way out.. this may not be the best way to do it, but it is working (or at least, it seems to work !).
At first, I've managed to send the keys to the MFC form:
bool Dlg_WU_MFC_Container::ProcessDialogKey(Keys keyData) {
::SendMessage(CWnd::GetFocus()->GetSafeHwnd(), WM_KEYDOWN, (WPARAM)keyData, (LPARAM)0);
return true;
}
Since the TabControl is taking the control through an WM_ACTIVATE, we tried to "override" it by sending also WM_ACTIVATE to the MFC form, so the result is the following:
bool Dlg_WU_MFC_Container::ProcessDialogKey(Keys keyData) {
::SendMessage(CWnd::GetFocus()->GetSafeHwnd(), WM_ACTIVATE, MAKEWPARAM(WA_ACTIVE, 0), (LPARAM)0);
::SendMessage(CWnd::GetFocus()->GetSafeHwnd(), WM_KEYDOWN, (WPARAM)keyData, (LPARAM)0);
return true;
}
The only thing is that the "Tab" key doesn't seems to work but after investigation, it is not needed by our users, so... :D But I think it's just related to the third parameter of WM_ACTIVATE (previous control). It must be used to know which control must be focused after Tab is pressed.
Sbizz
来源:https://stackoverflow.com/questions/46003104/c-cli-embedding-mfc-into-winform