2017-09-01 102 views
0

你好,C++/CLI:嵌入到MFC的WinForm

由于几个星期,我们试图“改造”一个MFC对话框为“MFC形式”,它可以被嵌入到一个WinForm用户控件。

我们已经成功地做到这一点:

  • 我们做了一个WinForm用户控件,叫做Dlg_WU_MFC_Container
  • 在创建时,UC创建MFC形式称为CDlgEdgeType
  • 然后,每次UC调整大小或移动时,我们也会移动并调整MFC窗体的大小

下面是代码(I试图删除了许多不必要的东西..):

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很好地显示在我们的应用程序中,当应用程序调整大小或移动时,一切都很顺利。

这里是我的问题: CDlgEdgeTypeDlg_WU_MFC_Container父,伟大的。但是后者不知道MFC窗体是一个“小孩”..所以焦点有点丢失,箭头键和Tab键根本不起作用。

你应该知道的事情是,Dlg_WU_MFC_Container添加到的TabPages定制的TabControl。因此,如果用户试图通过MFC窗体的控件导航,或者他试图通过箭头键浏览TreeView,则TabControl似乎将接管焦点并将更改选项卡..这不方便D:

I我的同事们都不知道如何解决这个问题。也许我们整合MFC的方式是错误的,但是没有真正的关于这个的话题(我们看到更多的“将WinForm表单嵌入到MFC中......”)。另外,由于我们的软件历史悠久,我们不能简单地重新创建CDlgEdgeType。这是一个很大的对话框,事实上,我们有7个类似的对话框,代码实现了模板,但为了清晰的显示此信息,我删除了它们。

谢谢!

Sbizz。

+0

这只是一个非常基本的问题,只要你混合GUI框架就会发生。键盘快捷键处理由消息循环完成,并且您的程序中有错误的处理。 MFC循环不知道有关Winforms行为的任何信息。曾经有一篇关于它的知识库文章,但我找不到它。也许,没有人认为使用ShowDialog()是一个很好的解决方法。 –

回答

0

那么,我们找到了一条出路..这可能不是最好的方式,但它工作(或至少,它似乎工作!)。

起初,我已经成功地将钥匙发送到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