Scrolling Alternatives
The CscrollView class supports a particular kind of scrolling that involves one big window and a small viewport. Each item is assigned a unique position in this big window. If, for example, you have 10,000 address lines to display, instead of having a window 10,000 lines long, you probably want a smaller window with scrolling logic that selects only as many lines as the screen can display. In that case, you should write your own scrolling view class derived from Cview.
The OnInitialUpdate Function
You’ll be seeing more of the OnInitialupdate function when you study the document-view architecture, starting in Chapter 15. The virtual OnInitialUpdate function is important here because it is the first function called by the framework after your view window is fully created. The framework calls OnInitialUpdate before it calls OnDraw for the first time, so OnInitialUpdate is the natural place for setting the logical size and mapping mode for a scrolling view. You set these parameters with a call to the CscrollView::SetScrollSizes function.
Accepting Keyboard Input
Keyboard input is really a two-step process. Windows sends WM_KEYDOWN and WM_KEYUP message, with virtual key codes, to a window, but before they get to the window they are translated. If an ANSI character is typed (resulting in a WM_KEYDOWN message), the translation function checks the keyboard shift status and then sends a WM_CHAR message with the proper code, either uppercase or lowercase. Cursor keys and function keys don’t have codes, so there’s no translation to do. The window gets only the WM_KEYDOWN and WM_KEYUP messages.
You can use the Class View’s Properties window to map all these messages to your view. If you’re expecting characters, map WM_CHAR; if you’re expecting other keystrokes, map WM_KEYDOWN. The MFC library neatly supplies the character code or virtual key code as a handler function parameter.
选择滚动条
CscrollView类支持几中特殊的滚动条包括一个大窗口和一个小视口.每个成员在一个大窗口中分配一个位置.例如,你有10000行地址要显示,你大概想要个用滚动来选择跟屏幕大小一样多的行数的小窗口来代替一个有10000行的大窗口.在此例子中,你将要使用来自于Cview类来写你自己的滚动视图类. 光标键和和功能键是没有编码的,所以这里不需要转换工作.窗口仅获得WM_KEYDOWN和WM_KEYUP消息.
OnInitialUpdate函数
从第15章开始,当你将学习文档-视图架构的时候,你将看到很多关于OnInitialUpdate函数.OnInitialUpdate是虚函数,它是很重要的,因为它是在你视图建立之后第一个被框架调用的函数.框架在调用OnInitialUpdate之前它将第一次调用OnDraw函数,所以OnInitialUpdate是最佳的设置逻辑大小和滚动视图映射模式的地点.你要设置这些参数可以调用CscrollView::SetScrollSizes函数.
接受键盘输入
键盘输入实际上是分俩步处理的.Windows发送WM_KEYDOWN和WM_KEYUP消息,使用的是虚拟键盘码,给窗口,但是在给窗口之前它们要进行转换.如果是ANSI类型字符(结果放在WM_KEYDOWN消息中),转换函数检查键盘的shift状态然后使用正确的编码发送WM_CHAR消息,任何的大小写. 光标键和和功能键是没有编码的,所以这里不需要转换工作.窗口仅获得WM_KEYDOWN和WM_KEYUP消息.
你可以使用属性类视图窗口的来映射所有的这些消息给你的视图.如果你需要字符,就映射WM_CHAR;如果你需要其他按键,映射WM_KEYDOWN.MFC完美的支持字符和虚拟键码就跟处理函数参数一样.
The Ex05c Example: Scrolling
The goal of Ex05c is to make a logical window 20 centimeters wide by 30 centimeters high. The program draws the same ellipse that it drew in the Ex05b project. You could edit the Ex05b source files to convert the Cview base class to a CscrollView base class, but it’s easier to start over with the MFC Application Wizard. The wizard generates the OnInitialUpdate override function for you. Here are the steps:
1.Run the MFC Application Wizard to create Ex05c. Use the wizard to generate an SDI program named Ex05c in the \vcppnet subdirectory. Set the Cex05cView base class to CscrollView, as shown here:
2.Add the m_rectEllipse and m_nColor data members in Ex05cView.h. Insert the following code using the Add Member Variable Wizard available from the Class View’s Properties window or by typing inside the Cex05cView class declaration:
Private:
CRect m_rectEllipse;
Int m_nColor;
These are the same data members that were added in the Ex05a and Ex05b projects.
3.Modify the MFC Application Wizard-generated OnInitialUpdate function.Edit OnInitialUpdate in Ex05cView.cpp as shown here:
Void Cex05cView::OnInitialUpdate()
{
CscrollView::OnInitialUpdate();
Csize sizeTotal(20000,30000);//20 by 30 cm
Csize sizePage(sizeTotal.cx /2,sizeTotal.cy /2);
Csize sizeLine(sizeTotal.cx /50,sizeTotal.cy /50);
SetScrollSizes(MM_HIMETRIC,sizeTotal,sizePage,sizeLine);
}
4.Use the Class View’s Properties window to add a message handler for the WM_KEYDOWN message.The code wizards available from the Properties window generate the member function OnKeyDown along with the necessary message map entries and prototypes. Edit the code as follows:
Void Cex05cView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
switch (nChar){
case VK_HOME:
OnVSroll(SB_TOP,0,NULL);
OnHSroll(SB_LEFT,0,NULL);
Break;
Case VK_END:
OnVSroll(SB_BOTTOM,0,NULL);
OnHSroll(SB_RIGHT,0,NULL);
Break;
Case VK_UP:
OnVScroll(SB_LINEUP,0,NULL);
Break;
Case VK_DOWN:
OnVScroll(SB_LINEDOWN,0,NULL);
Break;
Case VK_PRIOR:
OnVScroll(SB_PAGEUP,0,NULL);
Break;
Case VK_NEXT:
OnVScroll(SB_PAGEDOWN,0,NULL);
Break;
Case VK_LEFT:
OnVHcroll(SB_LINELEFT,0,NULL);
break;
case VK_RIGHT:
OnVHcroll(SB_LINERIGHT,0,NULL);
Break;
Default:
Break;
}
}
5.Edit the constructor and the OnDraw function. Change the MFC Application Wizard-generated constructor and the OnDraw function in Ex05cView.cpp as follows:
Cex05cView::Cex05cView():m_rectEllipse(0,0,4000,-4000)
{
m_nColor=GRAY_BRUSH;
}
void Cex05cView::OnDraw(CDC* pDC)
{
pDC -SelectStockObject(m_nColor);
pDC -Ellipse(m_rectEllipse);
}
These functions are identical to those used in the Ex05a and Ex05b projects.
6.Map the WM_LBUTTONDOWN message and edit the handler. Make the following changes to the generated code:
Void Cex05cView::OnLButtonDown(UINT nFlags, Cpoint point)
{
CClientDC dc(this);
OnPrepareDC(&dc);
CRect rectDevice=m_rectEllipse;
dc.LPtoDP(rectDevice);
if (rectDevice.PtInRect(point)){
if (m_nColor==GRAY_BRUSH){
m_nColor=WHITE_BRUSH;
}
else {
m_nColor==GRAY_BRUSH);
}
InvalidateRect(rectDevice);
}
}
This function is identical to the OnLButtonDown handler in the Ex05b project. It calls OnPrepareDC as before, but something is different. The Cex05bView class doesn’t have an overridden OnPrepareDC function, so the call goes to CscrollView::OnPrepareDC.That function sets the mapping mode based on the first parameter to SetScrollSizes, and it sets the window origin based on the current scroll position. Even if you scroll view were to user the MM_TEXT mapping mode, you’d still need the coordinate conversion logic to adjust for the origin offset.
7.Build and run the Ex05c program. Check to be sure that the mouse hit logic is working even if the circle is scrolled partially out of the window. Also check the keyboard logic. The output should look like this:
例子Ex05c:滚动
该例子的目的是创建一个20厘米x30厘米的窗口,并绘制跟EX05B项目一样的椭圆.你可以通过修改Ex05b的源文件Cview基类转换成CscrollView基类,但是你用MFC应用向导会更容易.向导为你产生OnInitialUpdate重载函数,下面是产生步骤:
1.运行MFC应用程序向导建立Ex05c.使用向导在\vcppnet子目录下建立一个名为Ex05c的SDI程序.设置Cex05cView基类为CscrollView,如下图所示:
2.增加m_rectEllipse和m_nColor变量到Ex05cView.h中.从类视图的属性窗口的增加数据变量向导插入以下代码或在Cex05cView类定义中键入代码:
Private:
CRect m_rectEllipse;
Int m_nColor;
这些数据成员跟Ex05a和Ex05b项目中的一样.
3.修改MFC应用程序向导产生的OnInitialUpdate函数.编辑Ex05cView.cpp中的OnInitialUpdate函数,如下所示:
Void Cex05cView::OnInitialUpdate()
{
CscrollView::OnInitialUpdate();
Csize sizeTotal(20000,30000);//20 by 30 cm
Csize sizePage(sizeTotal.cx /2,sizeTotal.cy /2);
Csize sizeLine(sizeTotal.cx /50,sizeTotal.cy /50);
SetScrollSizes(MM_HIMETRIC,sizeTotal,sizePage,sizeLine);
}
4.使用类视图的属性窗口为WM_KEYDOWN消息增加消息处理.使用来自于属性窗口的代码向导产生OnKeyDown函数和必须的消息映射入口和原形.编辑以下代码:
Void Cex05cView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
switch (nChar){
case VK_HOME:
OnVSroll(SB_TOP,0,NULL);
OnHSroll(SB_LEFT,0,NULL);
Break;
Case VK_END:
OnVSroll(SB_BOTTOM,0,NULL);
OnHSroll(SB_RIGHT,0,NULL);
Break;
Case VK_UP:
OnVScroll(SB_LINEUP,0,NULL);
Break;
Case VK_DOWN:
OnVScroll(SB_LINEDOWN,0,NULL);
Break;
Case VK_PRIOR:
OnVScroll(SB_PAGEUP,0,NULL);
Break;
Case VK_NEXT:
OnVScroll(SB_PAGEDOWN,0,NULL);
Break;
Case VK_LEFT:
OnVHcroll(SB_LINELEFT,0,NULL);
break;
case VK_RIGHT:
OnVHcroll(SB_LINERIGHT,0,NULL);
Break;
Default:
Break;
}
}
5.修改构造器和OnDraw函数.用下列代码更改MFC应用程序向导的Ex05cView.CPP文件中产生的构造器和OnDraw函数
Cex05cView::Cex05cView():m_rectEllipse(0,0,4000,-4000)
{
m_nColor=GRAY_BRUSH;
}
void Cex05cView::OnDraw(CDC* pDC)
{
pDC -SelectStockObject(m_nColor);
pDC -Ellipse(m_rectEllipse);
}
这些函数在Ex05a和Ex05b中同样要使用.
6.映射WM_LBUTTONDOWN消息和编辑处理.安排下列产生改变的代码:
Void Cex05cView::OnLButtonDown(UINT nFlags, Cpoint point)
{
CClientDC dc(this);
OnPrepareDC(&dc);
CRect rectDevice=m_rectEllipse;
dc.LPtoDP(rectDevice);
if (rectDevice.PtInRect(point)){
if (m_nColor==GRAY_BRUSH){
m_nColor=WHITE_BRUSH;
}
else {
m_nColor==GRAY_BRUSH);
}
InvalidateRect(rectDevice);
}
}
这个OnLButtonDown是同样在Ex05b项目中处理的.它是在调用OnPrepareDC之前调用的,但是有时候又不同.Cex05bView类不可以重载OnPrepareDC函数,所以要调用CscrollView::OnPrepareDC.SetScrollSize函数的第一个参数是设置映射模式的,它同时也根据当前的滚动坐标来设置窗口的原点.即使你的滚动视图使用MM_TEXT映射模式,你仍旧需要转换逻辑坐标并调整原点偏移.
7.生成和运行Ex05c程序. 检查当圆有部分在滚动到窗口的外面而鼠标点击仍然有效同样也要用键盘测试.输出的程序如下图所示:
Using Other Windows Messages
The MFC library directly supports hundreds of Windows message-handling functions. In addition, you can define your own messages. You’ll see plenty of message-handling examples in later chapters, including handlers for menu items, child window controls, and so forth. In the meantime, five special Windows messages deserve special attention: WM_CREATE, WM_CLOSE, WM_QUERYENDSESSION, WM_DESTROY, and WM_NCDESTROY.
The WM_CREATE Message
This is the first message that Windows sends to a view. It is sent when the window’s Create function is called by the framework. At that time, the window creation is not finished, so the window is not visible. Therefore, your OnCrecte handler cannot call Windows functions that depend on the window being completely alive. You can call such functions in an overridden OnInitialUpdate function, but you must be aware that in an SDI application OnInitialUpdate can be called more than once in a view’s lifetime.
使用其他Windows消息
MFC库直接支持数百种Windows消息处理函数.另外,你能第一属于你自己的消息.你将在后面的章节中看到大量的消息处理的例子,包括菜单处理,子窗口控件,等等.在此其间,有五个特别的Windows消息应该特别注意: WM_CREATE, WM_CLOSE, WM_QUERYENDSESSION, WM_DESTROY, 和WM_NCDESTROY.
WM_CREATE消息
这个消息是Windows首先发送个视图的消息.当框架调用窗口的Create函数时发送它.在那时窗口没有被建立起来,是不可见的.因此,你的OnCreate函数里不能调用Windows函数它需要窗口完全的活动才可以.你可以在重载函数OnInitialUpdate中调用这样的函数,但是你必须明白在一个SDI应用程序的一个视图生存期内OnInitialUpdate将不止一次的被调用.
The WM_CLOSE Message
Windows sends the WM_CLOSE message when the user closes a window from the system menu and when a parent window is closed. If you implement the OnClose message map function in your derived view class, you can control the closing process. If, for example, you need to prompt the user to save changes to a file, you can do it in OnClose. Only after you’ve determined that it is safe to close the window should you call the base class OnClose function, which will continue the close process. The view object and the corresponding window will both still be active.
Note When you’re using the full application framework, you probably won’t use the WM_CLOSE message handler. You can override the Cdocument::SaveModified virtual function instead, as part of the application framework’s highly structured program exit procedure.
The WM_QUERYENDSESSION Message
Windows sends the WM_QUERYENDSESSION message to all running application when the user exits Windows. The OnQueryEndSession message map function handles it. If you write a handler for WM_CLOSE, you should write one for WM_QUERYENDSESSION, too.
The WM_DESTROY Message
Windows sends the WM_DESTROY message after the WM_CLOSE message, and the OnDestroy message map function handles it. When your program receives this message, it should assume that the view window is no longer visible on the screen but that it is still active and its child windows are still active. You use this message handler to do cleanup that depends on the existence of the underlying window. Be sure to call the base class OnDestroy function. You cannot “abort” the window destruction process in your view’s OnDestroy function. OnClose is the place to do that.
The WM_NCDESTROY Message
This is the last message that Windows sends when the window is being destroyed. All child windows have already been destroyed. You can do final processing in OnNcDestroy that doesn’t depend on a window being active. Be sure to call the base class OnNcDestroy function.
Note Do not try to destroy a dynamically allocated window object in OnNcDestroy. That job is reserved for a special CWnd virtual function, PostNcDestroy, that the base class OnNcDestroy calls. MFC Technical Note #17 in the online documentation offers hints about when it’s appropriate to destroy a window object.
WM_CLOSE消息
当用户从系统菜单关闭窗口并且父窗口是关闭的Windows就发送WM_CLOSE消息.如果你在源视图类中执行OnClose消息函数,你可以控制关闭的进程.例如,你需要提示用户保存改变的文件,你就要在OnClose中做.只有在你认为可以安全的关闭窗口之后,你才可以调用基类的OnClose函数,那将继续关闭进程.视图对象和相应的窗口仍旧是活动的.
注意:当你完全的使用应用程序框架的时候,你大概不会使用WM_CLOSE消息处理了.在应用程序框架高度结构化的程序中你可以重载Cdoument::SaveModified虚函数来代替它.
WM_QUERYENDSESSION消息
当用户退出Windows时发送WM_QUERYENDSESSION消息给正在运行的应用程序.OnQuerEndSession消息映射函数专门处理该消息.如果你要处理WM_CLOSE消息,你也要写一个处理WM_QUERENDSESSION的消息.
WM_DESTROY消息
当在WM_CLOSE消息之后Windows就发送WM_DESTROY消息,并且在OnDestroy消息函数中处理.当你的程序接收到这个消息,它将使视图窗口在屏幕上不再是显示的但是窗口和它的子窗口仍旧是存在的.你使用这个消息处理来清除依靠在下面的窗口.在调用基类的OnDestroy函数中确认.在你的视图的OnDestroy函数中你不能”异常中断”窗口的销毁过程.OnClose中可以处理.
WM_NCDESTROY消息
当窗口被彻底销毁之后Windows发送的最后的一个消息.所有的子窗口已经被销毁了.你能做的就是在不依赖活动窗口的OnNcDestroy中来处理最后结局.
注意:不可以在OnNcDestroy中销毁动态分配的窗口对象.这个工作留到特定的CWnd虚函数PostNcDestroy中去做,它在基类的OnNcDestroy中调用.MFC技术在线文档的17页提示它是最适合销毁窗口对象的.