温故而知新,学习MFC框架如何创建的过程

转载 2007年10月10日 16:24:00
很久没有使用MFC了,以至于都忘记MFC框架复杂的窗口、文档、视的创建过程了。
下面我们跟踪一个MFC MDI的应用程序,来温习或学习一下。
 
使用AppWizard创建一个MDI应用程序,我创建的应用程序叫MDITest,这样MFC生成了如下的类:

类名
作用
CMDITestApp
派生于CWinApp的应用程序类。
CMainFrame
派生于CMDIFrameWnd的MDI框架窗口类。
CMDITestDoc
派生于CDocument的文档类。
CChildFrame
派生于CMDIChildWnd的MDI子窗口类。
CMDITestView
派生于CView的文档显示类。
在运行时刻,CMainFrame, CChildFrame, CMDITestView的窗口关系如下面的表格示出:

CMainFrame
(Menu, Toolbar
MDIClient
 
CChildFrame
CMDITestView
   pDocument = *CMDITestDoc   (带有文档的指针)
 
 
 
 
 
[StatusBar]
其中,最外层的是顶层窗口CMainFrame,里面包含一个MDIClient窗口。CChildFrame做为子窗口包含于MDIClient中(可以包含多个),CChildFrame里面则是真实的文档表示窗口CMDITestView了。
 
我们从这里开始:

// CMDITestApp 初始化
BOOL CMDITestApp::InitInstance()
 
做为CWinApp的派生类,通常需要重载InitInstance(), ExitInstance()两个函数,以完成应用的初始化和退出。我们现在关心InitInstance中关于文档模板、窗口处理的部分,而忽略掉一些CommonControl, OLE初始化部分。
 
整个InitInstance代码如下:

BOOL CMDITestApp::InitInstance()
{
     InitCommonControls();      // 这里删减了大量注释和错误处理
     CWinApp::InitInstance();
     AfxOleInit();
     AfxEnableControlContainer();
     SetRegistryKey(_T("应用程序向导生成的本地应用程序"));
     LoadStdProfileSettings(4); // 加载标准 INI 文件选项(包括 MRU)
 
     TRACE("Before CMultiDocTemplate/n");
     // 注册应用程序的文档模板。文档模板
     // 将用作文档、框架窗口和视图之间的连接
     CMultiDocTemplate* pDocTemplate;
     pDocTemplate = new CMultiDocTemplate(IDR_MDITestTYPE,
         RUNTIME_CLASS(CMDITestDoc),
         RUNTIME_CLASS(CChildFrame), // 自定义 MDI 子框架
         RUNTIME_CLASS(CMDITestView));
     if (!pDocTemplate)
         return FALSE;
     TRACE("Before AddDocTemplate/n");
     AddDocTemplate(pDocTemplate);
 
     // 创建主 MDI 框架窗口
     TRACE("Before new CMainFrame/n");
     CMainFrame* pMainFrame = new CMainFrame;
     TRACE("Before pMainFrame->LoadFrame/n");
     if (!pMainFrame || !pMainFrame->LoadFrame(IDR_MAINFRAME))
         return FALSE;
     m_pMainWnd = pMainFrame;
 
     TRACE("Before ParseCommandLine/n");
     CCommandLineInfo cmdInfo;
     ParseCommandLine(cmdInfo);
 
     // 调度在命令行中指定的命令。如果
     // 用 /RegServer、/Register、/Unregserver 或 /Unregister 启动应用程序,则返回 FALSE。
     TRACE("Before ProcessShellCommand/n");
     if (!ProcessShellCommand(cmdInfo))
         return FALSE;
 
     TRACE("Before pMainFrame->ShowWindow/n");
     // 主窗口已初始化,因此显示它并对其进行更新
     pMainFrame->ShowWindow(m_nCmdShow);
     TRACE("Before pMainFrame->UpdateWindow/n");
     pMainFrame->UpdateWindow();
     return TRUE;
}
 
为了研究整个创建过程,我在其中添加了一些TRACE来跟踪创建顺序。
 
忽略掉开始的乱七八糟的初始化,从CMultiDocTemplate开始:

     CMultiDocTemplate* pDocTemplate = new CMultiDocTemplate(IDR_MDITestTYPE,
         RUNTIME_CLASS(CMDITestDoc),
         RUNTIME_CLASS(CChildFrame), // 自定义 MDI 子框架
         RUNTIME_CLASS(CMDITestView));
     AddDocTemplate(pDocTemplate);
(作了一点点简化)
这里首先创建了一个CMultiDocTemplate —— 文档模板,文档模板包括的三个运行时刻类信息:Document – CMDITestDoc, FrameWnd – CChildFrame, View – CMDITestView。
然后通过AddDocTemplate函数将新创建的文档模板添加到模板管理器之中(我们以后再研究模板管理器)。
 
然后创建主框架窗口CMainFrame:

     CMainFrame* pMainFrame = new CMainFrame;
     if (!pMainFrame || !pMainFrame->LoadFrame(IDR_MAINFRAME))
         return FALSE;
 
其中,需要研究的是LoadFrame的实现,以及里面都做了些什么。我们稍后研究。
 
处理命令行,在这里第一个空文档被建立出来:

     CCommandLineInfo cmdInfo;
     ParseCommandLine(cmdInfo);
 
     // 调度在命令行中指定的命令。如果用 /RegServer、/Register、/Unregserver 或 /Unregister 启动应用程序,则返回 FALSE。
     if (!ProcessShellCommand(cmdInfo))               // ß 这里创建出初始空文档
         return FALSE;
 
我们一会会重点研究ProcessShellCommand。
 
最后,显示主窗口:

     pMainFrame->ShowWindow(m_nCmdShow);
     pMainFrame->UpdateWindow();
 
至此,WinApp::InitInstance()完成了自己的工作。
 
上面遗留了三个待研究的分支,让我们现在去研究它们:
1、 CDocTemplate
2、 CFrameWnd::LoadFrame
3、 CWnd::ProcessShellCommand
 
 
 
研究CDocTemplate
 
我们的例子中是构造了一个CMultiDocTemplate,它是从CDocTemplate派生而来,所以我们主要研究CDocTemplate。
CDocTemplate的几个关键属性列表如下:

     CRuntimeClass* m_pDocClass;         // class for creating new documents
     CRuntimeClass* m_pFrameClass;       // class for creating new frames
     CRuntimeClass* m_pViewClass;        // class for creating new views
 
其中:

m_pDocClass
表示文档类类型,在此例子中就是CMDITestDoc
m_pFrameClass
表示容纳View窗口的框架窗口类类型,此例中为CChildFrame
m_pViewClass
表示显示文档的View视类类型,此例中为CMDITestView
 
我们可以这样认为,CDocTemplate用于描述Frame-View-Doc的关系。当然它还有一大堆别的属性,我们暂时先忽略。
 
一会还会看到CDocTemplate的创建文档、框架、视的过程,放在ProcessShellCommand中研究。
 
 
研究LoadFrame
 
让我们继续研究CFrameWnd::LoadFrame是怎么运作的。使用的方法是跟踪进入。。。

BOOL CMDIFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle,
     CWnd* pParentWnd, CCreateContext* pContext)
{
     // 调用基类 CFrameWnd 的 LoadFrame, pContext 在创建主窗口时 = NULL
     //   pParentWnd = NULL
     if (!CFrameWnd::LoadFrame(nIDResource, dwDefaultStyle,
      pParentWnd, pContext))
         return FALSE;
 
     // save menu to use when no active MDI child window is present
     ASSERT(m_hWnd != NULL);
     // 主窗口带有菜单,所以。。。
     m_hMenuDefault = ::GetMenu(m_hWnd);
     if (m_hMenuDefault == NULL)
         TRACE(traceAppMsg, 0, "Warning: CMDIFrameWnd without a default menu./n");
     return TRUE;
}
注意,我们的MDITest Application的主窗口CMainFrame是从CMDIFrameWnd派生的,所以进入到这里,参考代码中红色的注释部分。继续跟踪进入CFrameWnd::LoadFrame。
 

BOOL CFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle,
     CWnd* pParentWnd, CCreateContext* pContext)
{
     // only do this once
     ASSERT_VALID_IDR(nIDResource);    // nIDResource = 128, IDR_MAINFRAME
     ASSERT(m_nIDHelp == 0 || m_nIDHelp == nIDResource);
 
     m_nIDHelp = nIDResource;    // ID for help context (+HID_BASE_RESOURCE)
 
     CString strFullString;
     if (strFullString.LoadString(nIDResource)) // = MDITest
         AfxExtractSubString(m_strTitle, strFullString, 0);    // 取得第一个子串
 
     VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));
 
     // attempt to create the window
    // GetIconWndClass 会调用 virtual PreCreateWindow 函数,别处也会调用,从而
    // 使得子类的PreCreateWindow 将被调用多次
     LPCTSTR lpszClass = GetIconWndClass(dwDefaultStyle, nIDResource);
     CString strTitle = m_strTitle;
     // 调用 CFrameWnd::Create() 实际创建出窗口。
     // 注意:在这里将给 CMainFrame 发送 WM_CREATE 等多个消息。触发 CMainFrame 的
     //   OnCreate 处理等。
     if (!Create(lpszClass, strTitle, dwDefaultStyle, rectDefault,
      pParentWnd, MAKEINTRESOURCE(nIDResource), 0L, pContext))
     {
         return FALSE;   // will self destruct on failure normally
     }
 
     // save the default menu handle, 好像 CMDIFrameWnd 也保存了一次?
     ASSERT(m_hWnd != NULL);
     m_hMenuDefault = ::GetMenu(m_hWnd);
 
     // load accelerator resource
     LoadAccelTable(MAKEINTRESOURCE(nIDResource));
 
     // WM_INITIALUPDATE 是 MFC 发明的消息,参见后面的说明。
     if (pContext == NULL)   // send initial update
         SendMessageToDescendants(WM_INITIALUPDATE, 0, 0, TRUE, TRUE);
 
     return TRUE;
}
 
以下是从TN024: MFC-Defined Messages And Resources中抽取的部分说明:

WM_INITIALUPDATE
This message is sent by the document template to all descendants of a frame window when it is safe for them to do their initial update. It maps to a call to CView::OnInitialUpdate but can be used in other CWnd-derived classes for other one-shot updating.
wParam
Not used (0)
lParam
Not used (0)
returns
Not used (0)
 
归纳一下,LoadFrame中进行了如下事情:
1、 注册窗口类(AfxDeferRegisterClass)
2、 实际创建窗口(Create)
3、 处理菜单、快捷键,发送WM_INITIALUPDATE消息给所有子窗口。实际将在CView中处理此消息。(例如:在ToolBar上面放一个FormView,可能就能收到这个消息并处利?)
 
至此,CMainFrame已经成功创建,菜单已经装载,工具条、状态行等已经在CMainFrame::OnCreate中创建。让我们接着研究第一个子窗口是怎么被创建出来的,该过程和CMainFrame::LoadFrame比起来就不那么直接了。
 
 
研究CWnd::ProcessShellCommand
 
第一个MDI子窗口是从这里面建立出来的,这实在是缺乏直观性。不过MFC就是这样,没办法。

BOOL CWinApp::ProcessShellCommand(CCommandLineInfo& rCmdInfo)
{
     BOOL bResult = TRUE;
     switch (rCmdInfo.m_nShellCommand)
     {
     case CCommandLineInfo::FileNew:
         if (!AfxGetApp()->OnCmdMsg(ID_FILE_NEW, 0, NULL, NULL))      // 关键是这里
              OnFileNew();
         if (m_pMainWnd == NULL)
              bResult = FALSE;
         break;
 
     case CCommandLineInfo::FileOpen:                // 忽略
     case CCommandLineInfo::FilePrintTo:           // 忽略
     case CCommandLineInfo::FilePrint:
     case CCommandLineInfo::FileDDE:
     case CCommandLineInfo::AppRegister:
     case CCommandLineInfo::AppUnregister:
     }
     return bResult;
}
进入到ProcessShellCommand,要处理很多种不同命令,我们忽略其它命令,单独看FileNew部分。
注意:实际进入到了AfxGetApp()->OnCmdMsg(ID_FILE_NEW, 0, NULL, NULL)之中。
 
AfxGetApp()实际返回了CMDITestApp的唯一实例,它从CWinApp – CWinThread – CCmdTarget – CObject 派生而来。我们没有重载OnCmdMsg,所以进入到CCmdTarget的OnCmdMsg处理中。为了研究,我们删减了一些代码。

BOOL CCmdTarget::OnCmdMsg(UINT nID, int nCode, void* pExtra,
     AFX_CMDHANDLERINFO* pHandlerInfo)
{
     // 这里删减了一些代码
     // determine the message number and code (packed into nCode)
     const AFX_MSGMAP* pMessageMap;
     const AFX_MSGMAP_ENTRY* lpEntry;
     UINT nMsg = 0;
     // 这里删减了一些代码,处理后 nMsg = WM_COMMAND
     // 为了简化,删减了一些断言等。以下循环用于查找处理此消息的入口。
     for (pMessageMap = GetMessageMap(); pMessageMap->pfnGetBaseMap != NULL;
      pMessageMap = (*pMessageMap->pfnGetBaseMap)())
     {
         lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries, nMsg, nCode, nID);
         if (lpEntry != NULL)
         {
              // 找到了消息处理项入口,分发此消息。
              return _AfxDispatchCmdMsg(this, nID, nCode,
                   lpEntry->pfn, pExtra, lpEntry->nSig, pHandlerInfo);
         }
     }
     return FALSE;   // 未找到则不处理
}
最终MFC很愉快地找到了一个入口项,       CWinApp::OnFileNew(void)       要处理这个消息。继续进入到_AfxDispatchCmdMsg中去看看。
 

AFX_STATIC BOOL AFXAPI _AfxDispatchCmdMsg(CCmdTarget* pTarget, UINT nID, int nCode,
     AFX_PMSG pfn, void* pExtra, UINT_PTR nSig, AFX_CMDHANDLERINFO* pHandlerInfo)
         // return TRUE to stop routing
{
     union MessageMapFunctions mmf;
     mmf.pfn = pfn;
     BOOL bResult = TRUE; // default is ok
 
     if (pHandlerInfo != NULL)
     {
         // just fill in the information, don't do it
         pHandlerInfo->pTarget = pTarget;
         pHandlerInfo->pmf = mmf.pfn;
         return TRUE;
     }
 
     switch (nSig)
     {
     case AfxSigCmd_v:
         // normal command or control notification
         ASSERT(CN_COMMAND == 0);        // CN_COMMAND same as BN_CLICKED
         ASSERT(pExtra == NULL);
         (pTarget->*mmf.pfnCmd_v_v)();         // ß 实际调用 pTarget 指向的这个成员函数
         break;
     // 下面还有大量的多种 AfxSigCmd_xxx,忽略掉它们。
     default:    // illegal
         ASSERT(FALSE); return 0; break;
     }
     return bResult;
}
 
其中 (pTarget->*mmf.pfn_Cmd_v_v)() 对CWinApp::OnFileNew() 产生调用,pTarget = CMDITestApp类实例。调用进入如下:
 

void CWinApp::OnFileNew()
{
     if (m_pDocManager != NULL)
         m_pDocManager->OnFileNew();
}
 
进入进入到CDocManager::OnFileNew()
 

void CDocManager::OnFileNew()
{
     if (m_templateList.IsEmpty())
          // 提示没有模板并返回
     CDocTemplate* pTemplate = (CDocTemplate*)m_templateList.GetHead();    // 第一个
     if (m_templateList.GetCount() > 1)
          // 弹出一个对话框(很难看的)提示用户选择一个文档模板
 
     // 在这个例子里面,pTemplate 就是 CMDITestApp::InitInstance() 里面创建的那个模板
     pTemplate->OpenDocumentFile(NULL);
}
 
在进入CMultiDocTemplate::OpenDocumentFile之前,我观察了一下调用堆栈,结果如下:

>   mfc71d.dll!CDocManager::OnFileNew() 852 C++
    mfc71d.dll!CWinApp::OnFileNew() 25   C++
    mfc71d.dll!_AfxDispatchCmdMsg(CCmdTarget * pTarget=0x0042cae8, unsigned int nID=57600, int nCode=0, void (void)* pfn=0x0041153c, void * pExtra=0x00000000, unsigned int nSig=53, AFX_CMDHANDLERINFO * pHandlerInfo=0x00000000) 89   C++
    mfc71d.dll!CCmdTarget::OnCmdMsg(unsigned int nID=57600, int nCode=0, void * pExtra=0x00000000, AFX_CMDHANDLERINFO * pHandlerInfo=0x00000000) 396 + 0x27    C++
    mfc71d.dll!CWinApp::ProcessShellCommand(CCommandLineInfo & rCmdInfo={...}) 27 + 0x1e C++
    MDITest.exe!CMDITestApp::InitInstance() 101 + 0xc    C++
希望我还没有迷路:)
 
 
CMultiDocTemplate::OpenDocumentFile 又是很多很多代码,让我们选择一些。

CDocument* CMultiDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName,
     BOOL bMakeVisible)
{
     // 以下代码删减了验证、断言部分
     CDocument* pDocument = CreateNewDocument();              // 创建文档对象
     CFrameWnd* pFrame = CreateNewFrame(pDocument, NULL);    // 创建框架窗口
 
     if (lpszPathName == NULL)
     {
         pDocument->OnNewDocument();           // 初始化文档
     }
     else
          // 打开已有文档
 
     InitialUpdateFrame(pFrame, pDocument, bMakeVisible);
     return pDocument;
}
 
 
看一看CreateNewDocument()

CDocument* CDocTemplate::CreateNewDocument()
{
     // default implementation constructs one from CRuntimeClass
     if (m_pDocClass == NULL)
          // 错误提示啦
     // CRuntimeClass* m_pDocClass -> CreateObject 实例化文档类。
     // 在此例子中既是 CMDITestDoc
     CDocument* pDocument = (CDocument*)m_pDocClass->CreateObject();
     AddDocument(pDocument);      // 添加到模板里的文档列表,MultiDocTemplate 保存此一文档
     return pDocument;
}
 
 
CMDITestDoc有如下的定义,仅能从CRuntimeClass里面创建的。

class CMDITestDoc : public CDocument
{
protected: // 仅从序列化创建
     CMDITestDoc();               // 被保护的构造函数
     DECLARE_DYNCREATE(CMDITestDoc)             // 支持从 CRuntimeClass 信息中创建。
 
 
再接着进行CreateNewFrame。

CFrameWnd* CDocTemplate::CreateNewFrame(CDocument* pDoc, CFrameWnd* pOther)
{
     // create a frame wired to the specified document
     CCreateContext context;           // 这个 CreateContext 传递到 LoadFrame 中
     context.m_pCurrentFrame = pOther;         // 此例中 = NULL
     context.m_pCurrentDoc = pDoc;              // = 刚才创建的文档
     context.m_pNewViewClass = m_pViewClass;   // 显示此文档的视类的类型
     context.m_pNewDocTemplate = this;
 
     if (m_pFrameClass == NULL)
          // 提示错误并返回
     // 利用 CRuntimeClass 信息创建框架窗口对象,此例中为 CChildFrame
     CFrameWnd* pFrame = (CFrameWnd*)m_pFrameClass->CreateObject();
 
     // 这里,我们又看到了 LoadFrame , 参考前面的 LoadFrame 吧
     // 在这里面,View 窗口也被产生出来。参考 TRACE 输出。
     pFrame->LoadFrame(m_nIDResource,
              WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE,   // default frame styles
              NULL, &context);
     return pFrame;
}
 
 
LoadFrame之后View窗口将被创建出来,接着进入到CMDITestDoc::OnNewDocument中,现在仅仅是一个空的函数,没有特定代码。

BOOL CMDITestDoc::OnNewDocument()
{
   TRACE("CMDITestDoc::OnNewDocument() entry/n");
     if (!CDocument::OnNewDocument())
         return FALSE;
 
     // TODO: 在此添加重新初始化代码
     // (SDI 文档将重用该文档)
 
     return TRUE;
}
 
最后是CDocTemplate::InitialUpdateFrame,这里面主要是激活新建的框架、文档、视,看得挺头疼的。

void CDocTemplate::InitialUpdateFrame(CFrameWnd* pFrame, CDocument* pDoc,
     BOOL bMakeVisible)
{
     // just delagate to implementation in CFrameWnd
     pFrame->InitialUpdateFrame(pDoc, bMakeVisible);
}
 
现在,文档、框架窗口、视窗口全部被创建出来,我们胜利的返回到ProcessShellCommand处。显示和更新主窗口,完成了WinApp::InitInstance :

     // 主窗口已初始化,因此显示它并对其进行更新
     pMainFrame->ShowWindow(m_nCmdShow);
     pMainFrame->UpdateWindow();
 
 
 
看一下至此的TRACE输出,中间的DLL加载被去掉了:

Before CMultiDocTemplate
Before AddDocTemplate
Before new CMainFrame
CMainFrame::CMainFrame()
Before pMainFrame->LoadFrame
CMainFrame::PreCreateWindow entry         // 注意:PreCreateWindow 被两次调用
CMainFrame::PreCreateWindow entry
CMainFrame::OnCreate entry before CMDIFrameWnd::OnCreate
CMainFrame::OnCreate before m_wndToolBar.CreateEx
CMainFrame::OnCreate before m_wndStatusBar.Create
Before ParseCommandLine
Before ProcessShellCommand
CMDITestDoc::CMDITestDoc()      // 文档对象被创建
CChildFrame::CChildFrame()      // 子框架窗口被创建
CChildFrame::PreCreateWindow entry
CChildFrame::PreCreateWindow entry
CChildFrame::PreCreateWindow entry
CMDITestView::CMDITestView() entry   // 子框架窗口的 OnCreate 中创建了 View 窗口
CMDITestView::PreCreateWindow entry
CMDITestDoc::OnNewDocument() entry
Before pMainFrame->ShowWindow
Before pMainFrame->UpdateWindow
 
// 退出时的 TRACE
CMDITestView::~CMDITestView()
CChildFrame::~CChildFrame()
CMDITestDoc::~CMDITestDoc()
CMainFrame::~CMainFrame()
 
 

初学者必看——最简单最清晰的Struts2项目搭建流程

我在项目中想要使用Struts2框架,我该怎么做?如何开始?这是我的疑问,我想也是很多初学者的最大的疑问。网上的理论知识应有尽有,详细的说明与操作过程却是凤毛麟角,因此把这两天的学习过程记录下来。这篇...
  • KEY0323
  • KEY0323
  • 2016年03月01日 17:44
  • 14760

SSM框架搭建,新手学习中,欢迎指教

1、前言 最近项目中用到了spring+mybatis框架,闲暇之余,自己就想搭一套SSM(spring+springMVC+mybatis)的框架,结果发现并没有自己想象的那么简单,看了网上的很多教...
  • weisong530624687
  • weisong530624687
  • 2015年08月25日 22:26
  • 3676

一位资深程序员大牛给予Java初学者的学习建议

这一部分其实也算是今天的重点,这一部分用来回答很多群里的朋友所问过的问题,那就是我你是如何学习Java的,能不能给点建议?今天我是打算来点干货,因此咱们就不说一些学习方法和技巧了,直接来谈每个阶段要学...
  • liang0000zai
  • liang0000zai
  • 2016年05月13日 10:51
  • 40065

温故而知新,学习MFC框架如何创建的过程

很久没有使用MFC了,以至于都忘记MFC框架复杂的窗口、文档、视的创建过程了。 下面我们跟踪一个MFC MDI的应用程序,来温习或学习一下。   使用AppWizard创建一个MDI应用程序,我...
  • vlily
  • vlily
  • 2012年03月22日 16:16
  • 855

温故而知新,学习MFC框架如何创建的过程

很久没有使用MFC了,以至于都忘记MFC框架复杂的窗口、文档、视的创建过程了。 下面我们跟踪一个MFC MDI的应用程序,来温习或学习一下。   使用AppWizard创建一个MDI应用程序,我创建的...
  • myiszjf
  • myiszjf
  • 2013年07月31日 23:13
  • 507

ASP.NET温故而知新学习系列之深度剖析ASP.NET架构—ASP.NET请求的处理过程(一)

前言   我为什么要写这一系列呢,因为只有了解运行机制,学好底层,对我们更深入的理解一定的问题有帮助。   一:ASP.NET的工作原理   我们根据下面这张图来说明ASP.NET是如何来工作的   ...
  • menglin2010
  • menglin2010
  • 2012年01月02日 14:58
  • 579

C#温故而知新学习系列之.NET框架高级特性—如何在.NET框架中自定义属性类?(三)

阅读目录   一:属性   二:自定义属性类BookAttribute实例   . 属性是由属性类来实现的,向上篇文章《C#温故而知新学习系列之.NET框架高级特性—.NET框架中自身提供的属性...
  • yw1688
  • yw1688
  • 2012年08月31日 10:07
  • 1013

C#温故而知新学习系列之.NET框架高级特性—.NET框架中自身提供的属性(二)

阅读目录   一:特性   二:如何指定属性?   三:.NET框架提供了哪些内置属性?   四:实例   一:特性   . C#允许在类和类成员上声明特性,这些特性其实也是个类,可以在运...
  • yw1688
  • yw1688
  • 2012年08月31日 10:24
  • 615

C#温故而知新学习系列之面向对象编程—1-定义类与创建类的对象

定义类   在C#中使用class关键字,一个名称,一对大括号来定义一个新类,类的数据成员和方法位于类的主体内(一对大括号之间)   类的语法格式   class MyClass   {  ...
  • menglin2010
  • menglin2010
  • 2012年01月12日 15:50
  • 482

IOS温故而知新(一) 一步一步分析新建App启动过程

IOS温故而知新(一)  一步一步分析新建App启动过程    笔者接触IOS大概半年的时间,可以说能够开发App,但是又有很多不足的地方,因此打算写一些技术文章,能...
  • go_fishing
  • go_fishing
  • 2016年04月20日 10:23
  • 240
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:温故而知新,学习MFC框架如何创建的过程
举报原因:
原因补充:

(最多只允许输入30个字)