📄 05o015.htm
字号:
理,解决的办法很多,如在每次视口的OnPaint()事件中采用灰色刷子人为填充背<br>
景,但是这不是最好的办法。笔者发现最好的办法就是采用AfxRegisterWndClass()<br>
函数注册一个使用灰色背景刷的新的窗口类,这需要重载PreCreateWindow()函数来<br>
实现这一点,如下程序代码片段所示:<br>
BOOL CSimuView::PreCreateWindow(CREATESTRUCT& cs)<br>
{<br>
HBRUSH hbkbrush=CreateSolidBrush(RGB(192,192,192));//创建灰色背景刷<br>
LPCSTR lpMyOwnClass=AfxRegisterWndClass(CS_HREDRAW<br>
|CS_VREDRAW|CS_OWNDC,0,hbkbrush);//注册新类<br>
cs.lpszClass=lpMyOwnClass;//修改缺省的类风格<br>
return TRUE;<br>
}<br>
采用这种方法速度最快,也最省力。同时,还可以在PreCreateWindow()函数定义所<br>
希望的任何窗口风格,如窗口大小,光标式样等。<br>
三、使用单文档-多视结构<br>
如果用户使用过MFC进行编程,那么就会发现借助于AppWizard基于MFC无论编写SDI<br>
(单文档界面)还是编写MDI(多文档界面)都是十分方便的。MDI应用程序目前使用越<br>
来越普遍,人们熟悉的Microsoft公司的Office系列产品以及Visual系列产品都是典<br>
型的多文档应用程序。这种多文档界面具有多窗口的特点,因而人们可以在一个程<br>
序中使用多个子窗口来实现不同数据的浏览查看。如果用户要实现在MDI各个窗口之<br>
间针对同一数据进行不同的可视化就是一件比较麻烦的事情。值得庆幸的是,MFC提<br>
供的文档-视结构大大简化了这一工作。文档-视结构通过将数据从用户对数据的观<br>
察中分离出来,从而方便实现多视,亦即多个视口针对同一数据,如果一个视口中<br>
数据发生改变,那么其它相关视口中的内容也会随之发生改变以反映数据的变化。<br>
SDI和MDI这两种Windows标准应用程序框架并不是总能满足用户的需要,就作者的工<br>
作而言,就特别需要一种被称为单文档多视的应用程序,英文可以缩写为SDMV。通<br>
过SDMV应用我们可以利用文档类来统一管理应用程序的所有数据,同时需要采用多<br>
窗口以多种方式来可视化这些的数据,如棒图,趋势图和参数列表,从而方便用户<br>
从不同角度来观察数据。MDI虽然具有多窗口的特点,但是其为多文档,即通常情况<br>
下,一个视口对应一个文档,视口+文档便构成一个子窗口。在各个子窗口之间数据<br>
相互独立,如果要保持数据同步更新就需要采用特殊的技术了,采用这种方式既费<br>
时又费力。通过笔者的实践发现,利用MFC本身提供的多视概念通过适当改造MDI窗<br>
口应用程序就可以实现上述SDMV结构。<br>
所谓SDMV应用程序本质上仍然是一个MDI应用程序,只是在程序中我们人为控制使其<br>
只能生成一个文档类,这个文档在第一个视口创建时创建,注意,这里并不需要限<br>
制各个视口的创建先后顺序。此后与MDI窗口固有特性不同的是,所有新创建的子窗<br>
口都不再创建独立文档,而是把该新视口直接连接到已有的文档对象上,这样就使<br>
其成为单文档多视的结构,所有相关数据都存储在文档对象中,一旦文挡中数据发<br>
生改变,通过UpdateAllViews()函数通知所有相关视口,各个视口就可以在<br>
OnUpdate()中相应数据的变化。这种响应机制如下图所示:<br>
<br>
图 1 文档-视结构数据更新机制<br>
由于MDI本质上并不是为这种单文档多视机制服务的,因而在实际应用时需要解决一<br>
些问题。<br>
1、窗口标题问题<br>
窗口标题本来不应该成为问题,缺省情况下MDI窗口通过在文档模板中提供的资源ID<br>
所提供的对应字符串来确定窗口标题。但是对于SDMV应用,由于各个视口实质上是<br>
对应于同一个文挡,因此每个视口都具有相同标题,只不过增加了一个数据用于指<br>
示这是第几个视口。如果在各个视口中指明具体的窗口名字,那么由不同的视口启<br>
动创建文档产生的窗口标题就不同,这个名字会影响到后继视口。为了作到不同类<br>
型的视口如棒图视口和曲线视口具有不同的标题,这就需要一定的技术处理。根据<br>
笔者的摸索发现可以采用如下步骤实现:<br>
首先在从标准的MDI子窗口基类CMDIChildWnd派生一个自己的子窗口类,姑且命名为<br>
CMyChild,然后在其成员变量中增加一个CString型变量用以存储当前窗口标题:<br>
CString winTitle;<br>
然后在不同的视口创建过程中通过获取父窗口指针按自己的意愿对上述变量进行赋<br>
值,程序片段如下:<br>
pChild=(CMyChild*)GetParent();<br>
pChild->winTitle="棒图显示窗口";<br>
最后在CMyChild派生类中重载CMDIChildWnd基类中的OnUpdateFrameTitle()函数来<br>
强制实现窗口标题的个性化,这一函数在各种类库手册上和联机帮助中都没有,但<br>
的确有这样一个具有保护属性的函数用来实现窗口标题的更新操作,这可以从MFC类<br>
库的源代码中找到该函数的实现。重载后的源代码如下:<br>
void CMyChild::OnUpdateFrameTitle(BOOL bAddToTitle)<br>
{<br>
// update our parent window first<br>
GetMDIFrame()->OnUpdateFrameTitle(bAddToTitle);<br>
<br>
if ((GetStyle() & FWS_ADDTOTITLE) == 0)<br>
return; // leave child window alone!<br>
<br>
CDocument* pDocument = GetActiveDocument();<br>
if (bAddToTitle && pDocument != NULL)<br>
{<br>
char szOld[256];<br>
GetWindowText(szOld, sizeof(szOld));<br>
char szText[256];<br>
<br>
lstrcpy(szText,winTitle); //Modified by author!<br>
if (m_nWindow > 0)<br>
wsprintf(szText + lstrlen(szText), ":%d",
m_nWindow);<br>
<br>
// set title if changed, but don't remove completely<br>
if (lstrcmp(szText, szOld) != 0)<br>
SetWindowText(szText);<br>
}<br>
}<br>
2、如何创建SDMV应用<br>
如何创建SDMV应用比较麻烦,下面通过举例来具体说明。该例子假设用户需要建棒<br>
图类型和曲线形式的两种视口,假设用户已经利用CView基类派生并且实现了这两个<br>
类,分别对应于CMyChart和CMyTraceView两个类。<br>
1) 在应用类(从CWinApp派生出来的类)的头文件中加入下列变量和函数原型说<br>
明:<br>
CMultiDocTemplate* m_pMyTraceTemplate;<br>
CMultiDocTemplate* m_pMyChartTemplate;<br>
int ExitInstance();<br>
2) 在应用类的InitInstance成员函数中删除对AddDocTemplate函数的调用和<br>
OpenFileNew()语句,并且加入如下代码:<br>
m_pMyTraceTemplate = new CMultiDocTemplate(<br>
IDR_MYTRACEVIEW,<br>
RUNTIME_CLASS(CSimuDoc),<br>
RUNTIME_CLASS(CMyChild), // Derived MDI child frame<br>
RUNTIME_CLASS(CMyTraceView));<br>
<br>
m_pMyChartTemplate = new CMultiDocTemplate(<br>
IDR_MYCHART,<br>
RUNTIME_CLASS(CSimuDoc),<br>
RUNTIME_CLASS(CMyChild), // Derived MDI child frame<br>
RUNTIME_CLASS(CMyChart));<br>
3) 实现ExitInstance()函数,在其中删除所用的两个辅助模板:<br>
int CTestApp::ExitInstance()<br>
{<br>
if(m_pMyChartTemplate) delete m_pMyChartTemplate;<br>
if(m_pMyTraceTemplate) delete m_pMyTraceTemplate;<br>
return TRUE;<br>
}<br>
4) 在菜单资源中去掉File菜单中的New和Open项,加入New
Chart View和New <br>
Trace View两项,在对应的菜单命令中实现如下:<br>
void CMainFrame::OnNewMychart()<br>
{<br>
// TODO: Add your command handler code here<br>
OnNewView(((CSimuApp*)AfxGetApp())->m_pMyChartTemplate);<br>
}<br>
void CMainFrame::OnNewMyTrace()<br>
{<br>
// TODO: Add your command handler code here<br>
OnNewView(((CSimuApp*)AfxGetApp())->m_pMyTraceTemplate);<br>
}<br>
上中OnNewView的实现如下:<br>
BOOL CMainFrame::OnNewView(CMultiDocTemplate*
pDocTemplate)<br>
{<br>
CMDIChildWnd* pActiveChild = MDIGetActive();<br>
CDocument* pDocument;<br>
if (pActiveChild == NULL ||<br>
(pDocument = pActiveChild->GetActiveDocument()) ==
NULL)<br>
{<br>
TRACE0("Now New the specify view\n");<br>
ASSERT(pDocTemplate != NULL);<br>
ASSERT(pDocTemplate->IsKindOf(RUNTIME_CLASS(CDocTemplate)));<br>
pDocTemplate->OpenDocumentFile(NULL);<br>
return TRUE;<br>
}<br>
<br>
// otherwise we have a new frame to the same document!<br>
CMultiDocTemplate* pTemplate = pDocTemplate;<br>
ASSERT_VALID(pTemplate);<br>
CFrameWnd* pFrame =
pTemplate->CreateNewFrame(pDocument, pActiveChild);<br>
if (pFrame == NULL)<br>
{<br>
TRACE0("Warning: failed to create new
frame\n");<br>
return FALSE; // command failed<br>
}<br>
pTemplate->InitialUpdateFrame(pFrame, pDocument);<br>
return TRUE;<br>
}<br>
OnNewView是整个SDMV应用的核心组成,它的任务是创建一个新的指定类型的视口,<br>
它首先判断是否有活动视口存在,文档是否已经创建,正常情况下活动视口存在则<br>
表明文档存在,如果不存在则利用所指定的文档模板创建一个新的活动视口,否则<br>
则只创建视口,同时将其连接到已存在的文档对象上。<br>
通过以上步骤就可以实现SDMV应用,在其后的具体应用中利用文档对象的<br>
UpdateAllViews()函数和视口的OnUpdate()函数就可以很好的工作了。<br>
四、使用DDE服务<br>
Windows 3.x是一个分时多任务操作环境,在此环境下,多个应用程序可以并发地执<br>
行。为了在并发执行的多个任务之间共享数据和资源,Windows
提供了几种机制,<br>
主要是通过剪贴板(Clipboard)和动态数据交换(Dynamic
Data Exchange)。前者对<br>
于用户需要直接参与的数据交换来说,是一个非常方便的工具,但是如果希望数据<br>
交换自动进行时就必须依靠DDE技术了。编写DDE应用的技术也发展了好几代,从最<br>
初的基于消息的DDE到基于DDEML(动态数据交换管理库),再到现在流行的OLE技<br>
术。DDE技术的发展使得程序开发人员编写DDE应用更为简洁。从发展趋势来看,基<br>
于OLE的数据交换是最好的,它特别符合当今软件领域的客户-服务器机制<br>
(Client-Server)。为适应多平台和Internet的需要,在OLE基础上微软又开发了<br>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -