分享
 
 
 

The Accel Application

王朝vc·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

The Accel Application

Let's put this newfound knowledge to work by writing an application that scrolls. Accel draws a window that resembles Microsoft Excel. (See Figure 2-13.) The spreadsheet depicted in the window is 26 columns wide and 99 rows high—much too large to be displayed all at once. However, scroll bars allow the user to view all parts of the spreadsheet. In addition to providing a hands-on look at the principles discussed in the preceding sections, Accel demonstrates another way that a program can scale its output. Rather than use a non-MM_TEXT mapping mode, it uses CDC::GetDeviceCaps to query the display device for the number of pixels per inch displayed horizontally and vertically. Then it draws each spreadsheet cell so that it's 1 inch wide and ¼ inch tall using raw pixel counts.

Figure 2-13. The Accel window.

Figure 2-14. The Accel application.

Accel.h#define LINESIZE 8

class CMyApp : public CWinApp

{

public:

virtual BOOL InitInstance ();

};

class CMainWindow : public CFrameWnd

{

protected:

int m_nCellWidth; // Cell width in pixels

int m_nCellHeight; // Cell height in pixels

int m_nRibbonWidth; // Ribbon width in pixels

int m_nViewWidth; // Workspace width in pixels

int m_nViewHeight; // Workspace height in pixels

int m_nHScrollPos; // Horizontal scroll position

int m_nVScrollPos; // Vertical scroll position

int m_nHPageSize; // Horizontal page size

int m_nVPageSize; // Vertical page size

public:

CMainWindow ();

protected:

afx_msg void OnPaint ();

afx_msg int OnCreate (LPCREATESTRUCT lpCreateStruct);

afx_msg void OnSize (UINT nType, int cx, int cy);

afx_msg void OnHScroll (UINT nCode, UINT nPos,

CScrollBar* pScrollBar);

afx_msg void OnVScroll (UINT nCode, UINT nPos,

CScrollBar* pScrollBar);

DECLARE_MESSAGE_MAP ()

};

Accel.cpp#include <afxwin.h>

#include "Accel.h"

CMyApp myApp;

/////////////////////////////////////////////////////////////////////////

// CMyApp member functions

BOOL CMyApp::InitInstance ()

{

m_pMainWnd = new CMainWindow;

m_pMainWnd->ShowWindow (m_nCmdShow);

m_pMainWnd->UpdateWindow ();

return TRUE;

}

/////////////////////////////////////////////////////////////////////////

// CMainWindow message map and member functions

BEGIN_MESSAGE_MAP (CMainWindow, CFrameWnd)

ON_WM_CREATE ()

ON_WM_SIZE ()

ON_WM_PAINT ()

ON_WM_HSCROLL ()

ON_WM_VSCROLL ()

END_MESSAGE_MAP ()

CMainWindow::CMainWindow ()

{

Create (NULL, _T ("Accel"),

WS_OVERLAPPEDWINDOW ¦ WS_HSCROLL ¦ WS_VSCROLL);

}

int CMainWindow::OnCreate (LPCREATESTRUCT lpCreateStruct)

{

if (CFrameWnd::OnCreate (lpCreateStruct) == -1)

return -1;

CClientDC dc (this);

m_nCellWidth = dc.GetDeviceCaps (LOGPIXELSX);

m_nCellHeight = dc.GetDeviceCaps (LOGPIXELSY) / 4;

m_nRibbonWidth = m_nCellWidth / 2;

m_nViewWidth = (26 * m_nCellWidth) + m_nRibbonWidth;

m_nViewHeight = m_nCellHeight * 100;

return 0;

}

void CMainWindow::OnSize (UINT nType, int cx, int cy)

{

CFrameWnd::OnSize (nType, cx, cy);

//

// Set the horizontal scrolling parameters.

//

int nHScrollMax = 0;

m_nHScrollPos = m_nHPageSize = 0;

if (cx < m_nViewWidth) {

nHScrollMax = m_nViewWidth - 1;

m_nHPageSize = cx;

m_nHScrollPos = min (m_nHScrollPos, m_nViewWidth -

m_nHPageSize - 1);

}

SCROLLINFO si;

si.fMask = SIF_PAGE ¦ SIF_RANGE ¦ SIF_POS;

si.nMin = 0;

si.nMax = nHScrollMax;

si.nPos = m_nHScrollPos;

si.nPage = m_nHPageSize;

SetScrollInfo (SB_HORZ, &si, TRUE);

//

// Set the vertical scrolling parameters.

//

int nVScrollMax = 0;

m_nVScrollPos = m_nVPageSize = 0;

if (cy < m_nViewHeight) {

nVScrollMax = m_nViewHeight - 1;

m_nVPageSize = cy;

m_nVScrollPos = min (m_nVScrollPos, m_nViewHeight -

m_nVPageSize - 1);

}

si.fMask = SIF_PAGE ¦ SIF_RANGE ¦ SIF_POS;

si.nMin = 0;

si.nMax = nVScrollMax;

si.nPos = m_nVScrollPos;

si.nPage = m_nVPageSize;

SetScrollInfo (SB_VERT, &si, TRUE);

}

void CMainWindow::OnPaint ()

{

CPaintDC dc (this);

//

// Set the window origin to reflect the current scroll positions.

//

dc.SetWindowOrg (m_nHScrollPos, m_nVScrollPos);

//

// Draw the grid lines.

//

CPen pen (PS_SOLID, 0, RGB (192, 192, 192));

CPen* pOldPen = dc.SelectObject (&pen);

for (int i=0; i<99; i++) {

int y = (i * m_nCellHeight) + m_nCellHeight;

dc.MoveTo (0, y);

dc.LineTo (m_nViewWidth, y);

}

for (int j=0; j<26; j++) {

int x = (j * m_nCellWidth) + m_nRibbonWidth;

dc.MoveTo (x, 0);

dc.LineTo (x, m_nViewHeight);

}

dc.SelectObject (pOldPen);

//

// Draw the bodies of the rows and the column headers.

//

CBrush brush;

brush.CreateStockObject (LTGRAY_BRUSH);

CRect rcTop (0, 0, m_nViewWidth, m_nCellHeight);

dc.FillRect (rcTop, &brush);

CRect rcLeft (0, 0, m_nRibbonWidth, m_nViewHeight);

dc.FillRect (rcLeft, &brush);

dc.MoveTo (0, m_nCellHeight);

dc.LineTo (m_nViewWidth, m_nCellHeight);

dc.MoveTo (m_nRibbonWidth, 0);

dc.LineTo (m_nRibbonWidth, m_nViewHeight);

dc.SetBkMode (TRANSPARENT);

//

// Add numbers and button outlines to the row headers.

//

for (i=0; i<99; i++) {

int y = (i * m_nCellHeight) + m_nCellHeight;

dc.MoveTo (0, y);

dc.LineTo (m_nRibbonWidth, y);

CString string;

string.Format (_T ("%d"), i + 1);

CRect rect (0, y, m_nRibbonWidth, y + m_nCellHeight);

dc.DrawText (string, &rect, DT_SINGLELINE ¦

DT_CENTER ¦ DT_VCENTER);

rect.top++;

dc.Draw3dRect (rect, RGB (255, 255, 255),

RGB (128, 128, 128));

}

//

// Add letters and button outlines to the column headers.

//

for (j=0; j<26; j++) {

int x = (j * m_nCellWidth) + m_nRibbonWidth;

dc.MoveTo (x, 0);

dc.LineTo (x, m_nCellHeight);

CString string;

string.Format (_T ("%c"), j + `A');

CRect rect (x, 0, x + m_nCellWidth, m_nCellHeight);

dc.DrawText (string, &rect, DT_SINGLELINE ¦

DT_CENTER ¦ DT_VCENTER);

rect.left++;

dc.Draw3dRect (rect, RGB (255, 255, 255),

RGB (128, 128, 128));

}

}

void CMainWindow::OnHScroll (UINT nCode, UINT nPos, CScrollBar* pScrollBar)

{

int nDelta;

switch (nCode) {

case SB_LINELEFT:

nDelta = -LINESIZE;

break;

case SB_PAGELEFT:

nDelta = -m_nHPageSize;

break;

case SB_THUMBTRACK:

nDelta = (int) nPos - m_nHScrollPos;

break;

case SB_PAGERIGHT:

nDelta = m_nHPageSize;

break;

case SB_LINERIGHT:

nDelta = LINESIZE;

break;

default: // Ignore other scroll bar messages

return;

}

int nScrollPos = m_nHScrollPos + nDelta;

int nMaxPos = m_nViewWidth - m_nHPageSize;

if (nScrollPos < 0)

nDelta = -m_nHScrollPos;

else if (nScrollPos > nMaxPos)

nDelta = nMaxPos - m_nHScrollPos;

if (nDelta != 0) {

m_nHScrollPos += nDelta;

SetScrollPos (SB_HORZ, m_nHScrollPos, TRUE);

ScrollWindow (-nDelta, 0);

}

}

void CMainWindow::OnVScroll (UINT nCode, UINT nPos, CScrollBar* pScrollBar)

{

int nDelta;

switch (nCode) {

case SB_LINEUP:

nDelta = -LINESIZE;

break;

case SB_PAGEUP:

nDelta = -m_nVPageSize;

break;

case SB_THUMBTRACK:

nDelta = (int) nPos - m_nVScrollPos;

break;

case SB_PAGEDOWN:

nDelta = m_nVPageSize;

break;

case SB_LINEDOWN:

nDelta = LINESIZE;

break;

default: // Ignore other scroll bar messages

return;

}

int nScrollPos = m_nVScrollPos + nDelta;

int nMaxPos = m_nViewHeight - m_nVPageSize;

if (nScrollPos < 0)

nDelta = -m_nVScrollPos;

else if (nScrollPos > nMaxPos)

nDelta = nMaxPos - m_nVScrollPos;

if (nDelta != 0) {

m_nVScrollPos += nDelta;

SetScrollPos (SB_VERT, m_nVScrollPos, TRUE);

ScrollWindow (0, -nDelta);

}

}

GetDeviceCaps is called from CMainWindow's OnCreate handler, which is called upon receipt of a WM_CREATE message. WM_CREATE is the first message a window receives. It is sent just once, and it arrives very early in the window's lifetime—before the window is even visible on the screen. An ON_WM_CREATE entry in the window's message map connects WM_CREATE messages to the member function named OnCreate. OnCreate is the ideal place to initialize member variables whose values can only be determined at run time. It is prototyped as follows:

afx_msg int OnCreate (LPCREATESTRUCT lpCreateStruct)

lpCreateStruct is a pointer to a structure of type CREATESTRUCT, which contains useful information about a window such as its initial size and location on the screen. The value returned by OnCreate determines what Windows does next. If all goes as planned, OnCreate returns 0, signaling to Windows that the window was properly initialized. If OnCreate returns -1, Windows fails the attempt to create the window. A prototype OnCreate handler looks like this:

int CMainWindow::OnCreate (LPCREATESTRUCT lpCreateStruct)

{

if (CFrameWnd::OnCreate (lpCreateStruct) == -1)

return -1;

return 0;

}

OnCreate should always call the base class's OnCreate handler to give the framework the opportunity to execute its own window-creation code. This is especially important when you write document/view applications, because it is a function called by CFrameWnd::OnCreate that creates the view that goes inside a frame window.

You'll find the code that does the scrolling in the window's OnHScroll and OnVScroll handlers. switch-case logic converts the notification code passed in nCode into a signed nDelta value that represents the number of pixels the window should be scrolled. Once nDelta is computed, the scroll position is adjusted by nDelta pixels and the window is scrolled with the statements

m_nVScrollPos += nDelta;

SetScrollPos (SB_VERT, m_nVScrollPos, TRUE);

ScrollWindow (0, -nDelta);

for the vertical scroll bar and

m_nHScrollPos += nDelta;

SetScrollPos (SB_HORZ, m_nHScrollPos, TRUE);

ScrollWindow (-nDelta, 0);

for the horizontal scroll bar.

How are the scroll positions stored in m_nHScrollPos and m_nVScrollPos factored into the program's output? When OnPaint is called to paint the part of the workspace that was exposed by the scrolling operation, it repositions the window origin with the statement

dc.SetWindowOrg (m_nHScrollPos, m_nVScrollPos);

Recall that CDC::SetWindowOrg tells Windows to map the logical point (x,y) to the device point (0,0), which, for a client-area device context, corresponds to the upper left corner of the window's client area. The statement above moves the origin of the coordinate system left m_nHScrollPos pixels and upward m_nVScrollPos pixels. If OnPaint tries to paint the pixel at (0,0), the coordinate pair is transparently transformed by the GDI into (_m_nHScrollPos,_m_nVScrollPos). If the scroll position is (0,100), the first 100 rows of pixels are clipped from the program's output and the real output—the output the user can see—begins with the 101st row. Repositioning the origin in this manner is a simple and effective way to move a scrollable window over a virtual display surface.

If you could enlarge the window enough to see the entire spreadsheet, you would see the scroll bars disappear. That's because CMainWindow::OnSize sets the scroll bar range to 0 if the window size equals or exceeds the size of the virtual workspace. The OnSize handler also updates the scrolling parameters whenever the window size changes so that the thumb size accurately reflects the relative proportions of the window and the virtual workspace.

And with that, all the pieces are in place. The user clicks a scroll bar or drags a scroll bar thumb; OnHScroll or OnVScroll receives the message and responds by updating the scroll position and scrolling the window; and OnPaint redraws the window, using SetWindowOrg to move the drawing origin an amount that equals the current scroll position. The program's entire workspace is now accessible, despite the physical limitations that the window size imposes on the output. And all for less than 100 additional lines of code. How could it be any easier?

Funny you should ask. Because that's exactly what MFC's CScrollView class is for: to make scrolling easier. CScrollView is an MFC class that encapsulates the behavior of a scrolling window. You tell CScrollView how large a landscape you wish to view, and it handles everything else. Among other things, CScrollView processes WM_VSCROLL and WM_HSCROLL messages for you, scrolls the window in response to scroll bar events, and updates the thumb size when the window size changes.

While it's perfectly possible to wire a CScrollView into an application like Accel, CScrollView was designed primarily for document/view applications. Chapter 10 examines CScrollView more closely and also introduces some of the other view classes that MFC provides.

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有