2017-09-01 100 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