国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

經典與現代的結合:在MFC中集成RAD .NET框架

2019-11-17 05:02:00
字體:
來源:轉載
供稿:網友
概述   MFC已經有十幾年的歷史了,然而直到今天,他仍然是Visual C++的要害組成部分。從1996年的Visual C++ 4.2至今將近8年的時間,MFC的主體特征沒有出現明顯的變化,依舊是“古老”的面孔,因此關于這個類庫的種種評論自然是情理之中的事情了。從我個人的觀點上看,MFC功能依舊健壯、強大,而且是業界少有的、穩定的、經過足夠長歷史考驗的開發框架。深入研究這個類庫,你會找到酒越釀越醇的感覺。MFC的一個成功因素之一就是提供了一套完整的Document/View框架,然而這一點也是許多針對MFC批評的矛頭指向。也許是由于這個框架太經典,使人們看上去MFC不再“婷婷玉立”,而是“人老珠黃”,以至于打開今天的Visual Studio IDE的時候,多少會有點不協調的感覺:比起其它基于.NET框架的開發語言,用MFC開發會顯得很“土氣”、“孤獨”——沒有RAD機制,明顯的缺乏與其它時髦對象的“互操作”能力,而且嚴格恪守自己的領地。每當生成一個基于文檔的MFC程序,我們總是看到一張滄桑的面孔,似乎劉姥姥進入大觀園,與四周時髦的C#、VB.NET等存在明顯的“代溝”與“不相容”。曾經有很多人問我MFC還有前途嗎?是否已經行將就木?關于是MFC還是.NET的討論時隱時現,不絕于耳。CLR是個布滿魅力的世界,這種魅力,使得C#、VB.NET等變得光彩奪目。然而,MFC并沒有衰老,假如你深入的了解MFC,你會發現,MFC完全可以與C#、VB.NET爭奇斗艷……


在MFC項目中使用托管擴展
支持托管擴展

  .NET FrameWork提供的托管擴展支持確保了在MFC項目支持托管擴展(CLR),開發者可以使MFC工程(本文我們將使用Test作為工程名稱)通過打開項目的托管擴展屬性開關,來增加編譯器的托管支持(如圖1)。
經典與現代的結合:在MFC中集成RAD .NET框架
(圖1:打開托管編譯支持開關)對偶.NET對象

  在打開托管擴展編譯開關以后,您就可以在MFC項目中使用托管對象了,通常的做法是:為每個重要的MFC對象匹配一個托管對象以形成一個對偶對,彼此匹配的對象包含指向對方的指針,這樣,其他.NET對象可以通過對偶對中的.NET對象操作MFC對象;而其他MFC對象可以通過對偶對中的MFC對象操作.NET對象(如圖2)。
經典與現代的結合:在MFC中集成RAD .NET框架
(圖2:對偶托管對象)  在Visual Studio .NET 中,沒有提供關于添加托管C++類對象的向導,因此,你可以先添加一個基于托管C++的Component對象(如圖3)。
經典與現代的結合:在MFC中集成RAD .NET框架

(圖3. Add Class向導:增加托管C++ Component對象)  添加了該testDocObject托管組件對象之后,將該對象的基類改為Object,并刪除一些代碼得到一個最小托管類:
namespace test
{
__gc public class testDocObject
: public Object
{
public:
testDocObject(void)
{
}
};
}
  經過以上步驟,Visual Studio.NET生成的代碼被裝進了MFC程序,當然完全可以手動創建.h文件和.cpp文件,輸入相應的代碼,然后把它們添加到當前工程。由于以上步驟在托管擴展編程中經常碰到,因此,將上述過程自動化是必要的,有鑒于此,我們在附贈的光盤中提供了完整的添加.NET對象的Wizard。

在MFC非托管類中定義托管成員變量

  在MFC類中使用托管對象,提供對象的聲明和初始化方法與傳統的方法略有不同。以在文檔類CtestDoc中添加一個托管成員變量為例,聲明托管對象的代碼如下:
public:
gcroot m_ptestDocObj;
  gcroot類型安全包裝模板可以將托管參考類型指針作為成員變量嵌入到非托管類中,該變量就可以像其他類型的變量一樣使用了。在CtestDoc的成員函數InitialDocument中創建這個對象,代碼如下:
BOOL CtestDoc::InitialDocument()
{
#PRagma push_macro("new")
#undef new
m_ptestDocObj = new test::testDocObject();

#pragma pop_macro("new")
}
  由于testDocObject是一個托管參考類型,它總被分配在CLR堆上,所以自然不能使用在afx.h中定義的new操作符來直接初始化該對象以避免該托管對象在非托管的本地C++堆上創建導致的錯誤。在托管對象中聲明MFC對象,與常規方法一致。

  把握了上述的基本托管類和對象在傳統MFC項目中的對偶使用方法,就可以保證您充分使用.NET框架所提供的豐富的類庫支持(引用相關的動態鏈接庫并聲明名稱空間是必要的)。假如希望更多地了解托管和非托管C++代碼混用的技術,可以參考Tom Archer與Nishant Sivakumar合著的《Extending MFC applications with the .NET Framework》一書,相信會很有幫助。


宿主.NET控件
宿主.NET控件的理論基礎 在.NET Framework的世界里,功能豐富的.NET控件無疑是光彩奪目的明珠,MFC程序自然想聯姻這些華麗的事物。由于MFC框架不提供對.NET控件的直接支持,從而導致MFC后天的失落(缺乏類似C#、VB.NET特有的可視化設計機制以及自由的控件組織功能),這一點多少成為MFC遠離.NET世界的一種合理的客觀原因。但是,我們注重到:.NET控件本質上就是ActiveX控件,二者之間的重要區別是注冊方式不同——ActiveX控件是全局的,在系統注冊表中注冊;而.NET控件既可以在全局AssemblyCache中注冊,也可以放在局部的目錄中,相應的,在程序中獲取它們相關信息的方式是不同的。但是,一旦.NET控件的基本信息被我們“捕捉”,我們就可以使用與創建ActiveX控件一致的方法將.NET控件創建到MFC項目中。
經典與現代的結合:在MFC中集成RAD .NET框架(圖4:MFC框架中ActiveX控件的創建)  我們知道,MFC是通過COleControlSite類創建ActiveX控件的,由于針對用于ActiveX控件的COleControlSite類不適用于.NET控件,因此必須重新派生一個新類CWFControlSite來提供必要的支持,通過一個CWFControlWrapper類將一個.NET控件包裝在一個CWnd窗體中,并將包裝后的窗體“安置”在CWFControlSite內。CWFControlWrapper類代碼如下:
class CWFControlWrapper : public CWnd
{
public:
CWFControlWrapper();
virtual ~CWFControlWrapper(void);
IUnknown *pUnkControl;
IUnknown *GetManagedControl()
{
return pUnkControl;
}
void SetControlSite(COleControlSite *pSite)
{
m_pCtrlSite = pSite;
}
};
  下一步,要設計一個通用的CUserCtrlView類(從CView類派生),使得在CWFControlSite中指定的.NET控件可以像在COleControlSite中指定的ActiveX控件一樣顯示給用戶。正象每個ActiveX控件必需用一個CWnd對象進行創建一樣,一個支持.NET控件的CView類需要一個對應的CWnd對象,CWFControlWrapper就是針對這個目的設計的,通過CWFControlWrapper對象,MFC程序可以得到.NET對象對應的IUnknow、IDispatch。稍后我們介紹CUserCtrlView類的具體設計和使用方法。
經典與現代的結合:在MFC中集成RAD .NET框架(圖5:MFC框架中.NET控件的創建)
  一般而言,控件的對話框消息處理是一個極為要害的問題,在網上能找到的MFC中宿主控件的解決方法中,均沒有實現.NET控件的對話框消息處理,一個明顯的特征是不能處理“Tab”鍵消息為此,我們重載了CUserCtrlView的PreTranslateMessage函數:
BOOL CUserCtrlView::PreTranslateMessage(MSG *pMsg)
{
BOOL bRet = FALSE;
if(m_Control.pUnkControl != NULL)
{
CComQiptr
spInPlace(m_Control.pUnkControl);
if(spInPlace)
bRet =(S_OK == spInPlace->
TranslateAccelerator(pMsg)) ?
TRUE : FALSE;
}
if(CView::PreTranslateMessage(pMsg))
return TRUE;
CFrameWnd *pFrameWnd = GetTopLevelFrame();
if(pFrameWnd != NULL
&& pFrameWnd->m_bHelpMode)
return FALSE;
// start with first parent frame
pFrameWnd = GetParentFrame();
while(pFrameWnd != NULL)
{
if(pFrameWnd->PreTranslateMessage(pMsg))
return TRUE;
pFrameWnd = pFrameWnd->GetParentFrame();
}
return bRet;
}
  這樣可以使得CUserCtrlView可以正確的處理.NET Control的對話框消息。


回歸RAD世界

  接下來我們看看如何在工程中插入一個.NET用戶自定義控件。我們增加一個新的托管類testControl,代碼如下:
#pragma once

...

namespace test
{
public __gc class testControl :
public System::Windows::Forms::UserControl
{
public:
testControl(void)
{
InitializeComponent();
}
protected:
void Dispose(Boolean disposing)
{
if(disposing && components)
components->Dispose();
__super::Dispose(disposing);
}
private:
System::Windows::Forms::Label *label1;
System::ComponentModel::Container
*components;
void InitializeComponent(void)
{
this->label1 = new
System::Windows::Forms::Label();
this->SuspendLayout();
this->label1->Location =
System::Drawing::Point(16, 24);
this->label1->Name = S"label1";
this->label1->Size =
System::Drawing::Size(208, 16);
this->label1->TabIndex = 0;
this->label1->Text =
S"Welcome to TZ MFC.NET!";
this->Controls->Add(this->label1);
this->Name = S"testControl";
this->Size =
System::Drawing::Size(240, 160);
this->ResumeLayout(false);
}
};
}
  注重,testControl類繼續自UserControl類,用戶控件是開發者創建的任何控件,您可以將多個.NET控件組織在一起,添加功能代碼,然后把它作為一個更綜合一些的控件來使用,使用每一個用戶控件和使用其他的.NET標準控件的步驟都是沒有區別的在上面的代碼中,我們自定義的用戶控件僅包含了一個.NET Label控件。

  到目前為止,我們已經可以在原生MFC項目中成功插入.NET控件。然而,因為上面的.NET控件的插入是純手工方式的,不直觀且很難駕馭,一個聰明的辦法是實現一個集成在Visual Studio .NET IDE中的Wizard,以使得MFC工程中可以直接使用可視設計器,在隨機光盤中,我們提供了相關的Wizard,安裝后您就可以直接在MFC項目中插入并可視化設計.NET用戶控件了。

  通過集成的Wizard,傳統的MFC可以與現代的.NET RAD機制完美的結合在一起,使得你既可以得到傳統C++的優雅,又可以享有現代RAD機制的風韻,對資源的整合力度也極大地擴展了。

使用CUserCtrlView類創建、顯示.NET控件

  我們為每個MFC文檔類增加一個與之對偶的托管對象類,從而得到了一對對偶對。這個與MFC文檔對偶的托管對象維護一個托管對象字典,每一個需要在文檔中創建的托管控件會根據一個別名添加到這個字典中備查。當文檔對象被實例化的時候,其對偶的托管對象也將被實例化,而且有待創建的控件也會被實例化并插入到相關的字典中,同時該對偶托管對象被傳遞給MFC應用程序對象中的指針變量m_pManagedCnnObj,CUserCtrlView類在調用OnInitialUpdate時,會通過全局變量theApp得到m_pManagedCnnObj,m_pManagedCnnObj就是與MFC文檔對偶的托管對象,然后用.NET機制根據別名檢索從m_pManagedCnnObj得到所要創建的控件的實例,之后調用SetControl函數將該控件創建出來:
void CUserCtrlView::SetControl(
System::Object *control
)
{
m_Control.pUnkControl =
reinterpret_cast
(System::Runtime::InteropServices::
Marshal::GetIUnknownForObject(
control).ToPointer());
CRect clientRect;
GetClientRect(&clientRect);
CLSID clsid =
{ 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, 0 } };
m_Control.CreateControl(
clsid, 0, WS_VISIBLEWS_CHILD,
clientRect, this, 0);
m_Control.ModifyStyleEx(
0, WS_EX_CONTROLPARENT);
}

CUserCtrlView的OnInitialUpdate代碼如下:
void CUserCtrlView::OnInitialUpdate()
{
if(!m_bCtrlCreated&&m_strWinCtrlID!=_T(""))
{
Type *t =
theApp.m_pManagedCnnObj->GetType();
MethodInfo *mi = t->GetMethod(S"Connect");
Object *p[] = new Object*[2];
try
{
Object *pObj = NULL;
String *pString[] = new String*[1];
pString[0] = m_strWinCtrlID;
PropertyInfo *m_pPropertyInfo =

t->GetProperty(S"PrjItem");
pObj = m_pPropertyInfo->GetValue(
theApp.m_pManagedCnnObj,pString);
if(pObj)
{
p[0] = pObj;
p[1] = pString[0];
//?????o
mi->Invoke(
theApp.m_pManagedCnnObj, p);
SetControl(pObj);
m_bCtrlCreated = true;
}
}
catch (Exception *eXP)
{
CString strInfo = exp->Message;
AfxMessageBox(strInfo);
return;
}
}
CView::OnInitialUpdate();
}

  現在,你可以在MFC程序中創建.NET控件了,我們的第一個宿主.NET控件的程序運行結果如圖:
經典與現代的結合:在MFC中集成RAD .NET框架
(圖6:運行時.NET控件)檢索.NET用戶控件

  一個MFC應用中可能包含多個UserControl,這些控件一般以動態鏈接庫的形式存在。由于.NET控件一般不在全局注冊,因此,.NET程序需要一種組件檢索機制,使得它們能夠正確的發現運行時用到的組件。.NET框架支持為每一個應用程序提供一個xml格式的配置文件,配置文件的名稱是:“程序名.exe.config”,例如,名為 test.exe 的應用程序的配置文件為:test.exe.config,配置文件相當于局部的注冊表,該文件必須與可執行文件位于同一個目錄中。開發者可以在這個文件中更改、保存程序的設置,設置程序集綁定策略和其他的應用程序配置信息。.NET框架提供了專門的類以幫助開發人員操作該文件。下面是一段典型的配置文件片段:
<?xml version="1.0" encoding="UTF-8"?>

<configuration>

<runtime>

<assemblyBinding

xmlns=

"urn:schemas-microsoft-com:asm.v1"

>

<probing privatePath=

"bin;usercontrol;

component;doctemplate"

/>

</assemblyBinding>

</runtime>

</configuration>

  <configuration>元素是CLR和.NET Framework 應用程序所使用的每個配置文件中的根元素。<runtime>包含有關程序集綁定和垃圾回收的信息。<assemblyBinding>包含有關程序集版本重定向和程序集位置的信息,<probing>元素使用privatePath來指定加載程序集時運行庫搜索的子目錄(比如這里指定bin、usercontrol等目錄為搜索目錄)。


經典優雅與現代風流的完美結合  MFC曾經依靠其與Win32的良好的耦合性在Windows開發方面獨領風騷。然而,時過境遷,在.NET大行其道盡顯風流的今天,MFC的世界似乎已經感到風雨飄搖了。不容否認,十幾年的積累,已經使得MFC的世界有了相當的厚度,因此,MFC過時之說還言之過早,Visual Studio 2005之后,Microsoft依然在后續的Visual Studio中給MFC留下了位置。.NET環境,給Windows程序開發帶來了許多新鮮的感覺,例如多語言的交叉繼續、一致的Form設計等等。曾幾何時,這些新鮮事物看上去都是與MFC不相關的,現在你可以自豪地說,在MFC的世界里,你可以擁有所有這一切!你可以充分運用已有的MFC經驗積累去駕馭今天的新鮮事物。當所有的新鮮感覺消失之后,你會發現,經典的總是最好的,只要這種經典的感覺可以進一步延續。假如你希望左手托起傳統Windows開發世界而右手緊握托管的.NET世界,那么,我們向你推薦MFC,因為MFC可以整合經典的優雅與現代的風流。通過集成.NET機制,經典MFC設計哲學融入了新的活力,因此打開了一扇門,對傳統的MFC開發人員來說孤獨已經不存在,完全可以與C#、VB.NET等爭奇斗艷了。然而,當MFC具備同C#、VB.NET等一樣的RAD機制的時候,面對C#、VB.NET這些新貴們,MFC自然的傳統優勢就會充分的得以體現,這種優勢是內蘊的,C#、VB.NET們無法效仿。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 米易县| 偃师市| 沂南县| 武乡县| 公安县| 当涂县| 萨迦县| 华池县| 镇巴县| 漳州市| 临潭县| 高青县| 炉霍县| 上虞市| 项城市| 嘉鱼县| 江川县| 南城县| 永仁县| 五莲县| 盐城市| 祁门县| 天峻县| 南陵县| 弋阳县| 建始县| 临沂市| 永吉县| 鸡西市| 高雄市| 四川省| 彭州市| 高要市| 延庆县| 页游| 常宁市| 峨边| 宜阳县| 阳江市| 南平市| 岑溪市|