C++/CLI:将 MFC 嵌入 WinForm
Posted
技术标签:
【中文标题】C++/CLI:将 MFC 嵌入 WinForm【英文标题】:C++/CLI: Embedding MFC into WinForm 【发布时间】:2017-09-01 15:03:33 【问题描述】:你好,
几周以来,我们一直在尝试将 MFC 对话框“转换”为可以嵌入到 WinForm 用户控件中的“MFC 表单”。
我们已经成功地做到了:
我们制作了一个 WinForm 用户控件,名为 Dlg_WU_MFC_Container 创建时,UC 会创建名为 CDlgEdgeType 的 MFC 表单 然后,每次调整 UC 的大小或移动时,我们也会移动和调整 MFC 窗体的大小这是代码(我试图删除很多不必要的东西..):
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);
这些东西“非常好”:CDlgEdgeType 在我们的应用程序中得到了很好的显示,当应用程序调整大小或移动时,一切正常。
这是我的问题: CDlgEdgeType 有 Dlg_WU_MFC_Container 作为父级,太好了。但后者不知道 MFC 表单是“子”.. 所以焦点有点丢失,箭头键和 Tab 键根本不起作用。
您应该知道的是 Dlg_WU_MFC_Container 被添加到自定义 TabControl 的 TabPages 中。因此,如果用户尝试浏览 MFC 表单的控件,或者他尝试使用箭头键浏览 TreeView,TabControl 似乎会接管焦点并会更改选项卡。这不方便 D:
我不知道如何解决这个问题,我的同事也不知道。
也许我们集成 MFC 的方式是错误的,但并没有真正的主题(我们看到更多“将 WinForm 表单嵌入 MFC”..)。此外,由于我们的软件历史悠久,我们不能简单地重新创建 CDlgEdgeType。这是一个很大的对话框,事实上,我们有 7 个这样的对话框,代码实现了模板,但为了清楚起见,我删除了它们..
谢谢!
Sbizz。
【问题讨论】:
这只是一个非常基本的问题,每当您混合 GUI 框架时都会发生。键盘快捷键处理是由消息循环完成的,您在程序中激活了错误的快捷键。 MFC 循环对 Winforms 的行为一无所知。曾经有一篇关于它的知识库文章,但我找不到它。投票离岛,也许没有人认为使用 ShowDialog() 是一个很好的解决方法。 【参考方案1】:好吧,我们找到了出路.. 这可能不是最好的方法,但它正在工作(或者至少,它似乎工作!)。
起初,我设法将密钥发送到 MFC 表单:
bool Dlg_WU_MFC_Container::ProcessDialogKey(Keys keyData)
::SendMessage(CWnd::GetFocus()->GetSafeHwnd(), WM_KEYDOWN, (WPARAM)keyData, (LPARAM)0);
return true;
由于 TabControl 通过 WM_ACTIVATE 获取控件,我们尝试通过将 WM_ACTIVATE 发送到 MFC 表单来“覆盖”它,因此结果如下:
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;
唯一的问题是“Tab”键似乎不起作用,但经过调查,我们的用户不需要它,所以... :D 但我认为它只是与 WM_ACTIVATE 的第三个参数有关(以前的控制)。它必须用于知道按下 Tab 后必须聚焦哪个控件。
Sbizz
【讨论】:
以上是关于C++/CLI:将 MFC 嵌入 WinForm的主要内容,如果未能解决你的问题,请参考以下文章
MFC C++/CLI 项目:VS2012 中的 /CLR 开关导致调试问题
将窗口(glfwCreateWindow)作为子嵌入到 C++ MFC 父窗体
MFC dll 中的访问冲突(用 C++/CLI 包装)从 C# 程序开始