您的位置:首页 > 其它

在窗口显示文字和绘制图形

2006-05-14 03:56 801 查看
昨天和大家谈了如何建立一个带有窗口的Windows程序,今天和大家说说如何在窗口中显示文字吧!!
先说说相关的基础知识
(1).绘制和更新
在Windows中,只能在窗口的显示区域绘制文字和图形,而且不能确保在显示区域内显示的内容会一直保留到程序下一次有意地改写它时还保留在那里。例如,使用者可能会在屏幕上移动另一个程序的窗口,这样就可能覆盖您的应用程序窗口的一部分。Windows不会保存您的窗口中被其它程序覆盖的区域,当程序移开后,Windows会要求您的程序更新显示区域的这个部分, Windows通过发送WM_PAINT消息通知窗口消息处理程序,窗口的部分显示区域需要绘制。
(2).WM_PAINT消息
大多数Windows程序在WinMain中进入消息循环之前的初始化期间都要呼叫函数UpdateWindow。Windows利用这个机会给窗口消息处理程序发送第一个WM_PAINT消息。这个消息通知窗口消息处理程序:必须绘制显示区域。此后,窗口消息处理程序应在任何时刻都准备好处理其它WM_PAINT消息,必要的话,甚至重新绘制窗口的整个显示区域。

(3).有效矩形和无效矩形
Windows内部为每个窗口保存一个「绘图信息结构」,这个结构包含了包围无效区域的最小矩形的坐标以及其它信息,这个矩形就叫做「无效矩形」,有时也称为「无效区域」。如果在窗口消息处理程序处理WM_PAINT消息之前显示区域中的另一个区域变为无效,则Windows计算出一个包围两个区域的新的无效区域(以及一个新的无效矩形),并将这种变化后的信息放在绘制信息结构中。Windows不会将多个WM_PAINT消息都放在消息队列中。
窗口消息处理程序可以通过呼叫InvalidateRect使显示区域内的矩形无效。如果消息队列中已经包含一个WM_PAINT消息,Windows将计算出新的无效矩形。否则,它将一个新的WM_PAINT消息放入消息队列中。在接收到WM_PAINT消息时,窗口消息处理程序可以取得无效矩形的坐标(我们马上就会看到这一点)。通过呼叫GetUpdateRect,可以在任何时候取得这些坐标。
在处理WM_PAINT消息处理期间,窗口消息处理程序在呼叫了BeginPaint之后,整个显示区域即变为有效。程序也可以通过呼叫ValidateRect函数使显示区域内的任意矩形区域变为有效。如果这呼叫具有令整个无效区域变为有效的效果,则目前队列中的任何WM_PAINT消息都将被删除
GDI相关的内容
要在窗口的显示区域绘图,可以使用Windows的图形设备接口(GDI)函数
(1). 设备内容
句柄只不过是一个数值,Windows以它在内部使用对象。程序写作者从Windows取得句柄,然后在其它函数中使用该句柄。设备内容句柄是GDI函数的窗口「通行证」,有了这种设备内容句柄,程序写作者就能自如地在显示区域上绘图,使图形如自己所愿地变得好看或者难看。
设备内容(简称为「DC」)实际上是GDI内部保存的数据结构。设备内容与特定的显示设备(如视讯显示器或打印机)相关。对于视讯显示器,设备内容总是与显示器上的特定窗口相关。
设备内容中的有些值是图形「属性」,这些属性定义了GDI绘图函数工作的细节。例如,对于TextOut,设备内容的属性确定了文字的颜色、文字的背景色、x坐标和y坐标映像到窗口的显示区域的方式,以及显示文字时Windows使用的字体。
当程序需要绘图时,它必须先取得设备内容句柄。在取得了该句柄后,Windows用内定的属性值填入内部设备内容结构。在后面的章节中您会看到,可以通过呼叫不同的GDI函数改变这些默认值。利用其它的GDI函数可以取得这些属性的目前值。当然,还有其它的GDI函数能够在窗口的显示区域真正地绘图。
当程序在显示区域绘图完毕后,它必须释放设备内容句柄。句柄被程序释放后就不再有效,且不能再被使用。程序必须在处理单个消息处理期间取得和释放句柄。除了呼叫CreateDC()建立的设备内容之外,程序不能在两个消息之间保存其它设备内容句柄
(2) 取得设备内容句柄的方法:
方法一:
caseWM_PAINT:

hdc = BeginPaint (hwnd, &ps) ;

使用GDI函数

EndPaint (hwnd, &ps) ;

return 0 ;

在处理WM_PAINT消息时,使用这种方法。它涉及BeginPaint和EndPaint两个函数,这两个函数需要窗口句柄(作为参数传给窗口消息处理程序)和PAINTSTRUCT结构的变量(在WINUSER.H表头文件中定义)的地址为参数
在处理WM_PAINT消息时,窗口消息处理程序首先呼叫BeginPaint。BeginPaint函数一般在准备绘制时导致无效区域的背景被擦除。该函数也填入ps结构的字段。BeginPaint传回的值是设备内容句柄,这一传回值通常被保存在叫做hdc的变量中
HDC数据型态定义为32位的无正负号整数。然后,程序就可以使用需要设备内容句柄的TextOut等GDI函数。呼叫EndPaint即可释放设备内容句柄。
方法二:
hdc = GetDC (hwnd) ;

使用GDI函数

ReleaseDC (hwnd, hdc) ;

主要用来处理非WM_PAINT消息处理期间绘制显示区域的某个部分的;
主要步骤如下:
(1). 得到窗口显示区域的设备内容句柄,可以呼叫GetDC来取得句柄,在使用完后呼叫ReleaseDC;
注意:
与从BeginPaint传回设备内容句柄不同,GetDC传回的设备内容句柄具有一个剪取矩形,它等于整个显示区域。可以在显示区域的某一部分绘图,而不只是在无效矩形上绘图(如果确实存在无效矩形)。与BeginPaint不同,GetDC不会使任何无效区域变为有效。如果需要使整个显示区域有效,可以呼叫
ValidateRect (hwnd, NULL) ;

一般可以呼叫GetDC和ReleaseDC来对键盘消息(如在字处理程序中)和鼠标消息(如在画图程序中)作出反应。此时,程序可以立刻根据使用者的键盘或鼠标输入来更新显示区域,而不需要考虑为了窗口的无效区域而使用WM_PAINT消息。不过,一旦确实收到了WM_PAINT消息,程序就必须要收集足够的信息后才能更新显示。
与GetDC相似的函数是GetWindowDC。GetDC传回用于写入窗口显示区域的设备内容句柄,而GetWindowDC传回写入整个窗口的设备内容句柄。例如,您的程序可以使用从GetWindowDC传回的设备内容句柄在窗口的标题列上写入文字。然而,程序同样也应该处理WM_NCPAINT (「非显示区域绘制」)消息

下面请看具体的示例(注意,其中的实现窗口部分的代码是重复前面的代码的,以后再说的时候就不全部写了,请大家参考前面的代码)
#include <windows.h>
#include <math.h>

#define NUM 1000
#define myTWOPI (2*3.14159)

LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,PSTR szCmdLine,int iCmdShow)
{
static TCHAR szAppName[]=TEXT("SinWave");

HWND hwnd;

MSG msg;

WNDCLASS wndclass;

wndclass.style=CS_HREDRAW | CS_VREDRAW;

wndclass.lpfnWndProc=WndProc;

wndclass.cbClsExtra=0;
wndclass.cbWndExtra=0;

wndclass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);

wndclass.lpszMenuName=NULL;

wndclass.hInstance=hInstance;

wndclass.hIcon=LoadIcon(NULL,IDI_APPLICATION);

wndclass.hCursor=LoadCursor(NULL,IDC_ARROW);

wndclass.lpszClassName=szAppName;

if(!RegisterClass(&wndclass))
{
MessageBox(NULL,TEXT("This program requires Windows NT!!"),szAppName,MB_ICONERROR);
return 0;
}

hwnd=CreateWindow(szAppName,
TEXT("Draw Sine Wave Using Polyline"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL);

ShowWindow(hwnd,iCmdShow);

UpdateWindow(hwnd);

while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}

return msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
{
static int cxClient,cyClient;

HDC hdc;

int i;

PAINTSTRUCT ps;

POINT apt[NUM]; //定义1000组数据
TCHAR szBuffer[40];
HPEN hPen;
switch(message)
{
//处理WM_SIZE消息
case WM_SIZE:

cxClient=LOWORD(lParam);
cyClient=HIWORD(lParam);

return 0;

case WM_PAINT:

hdc=BeginPaint(hwnd,&ps);

// TCHAR szBuffer[40];注意此行要把szBuffer的定义放在消息处编译理的前面,不然编译会报错的;

SetTextColor(hdc,RGB(255,0,0));
//设置文字的背景色
SetBkColor(hdc,RGB(0,255,255));

/*
SetTextColor的MSDN文档

COLORREF SetTextColor(
HDC hdc, // handle to device context
COLORREF crColor // text color
);

COLORREF的文档
When specifying an explicit RGB color, the COLORREF value has the following hexadecimal form:
0x00bbggrr
The low-order byte contains a value for the relative intensity of red; the second byte contains a value for green; and the third byte contains a value for blue. The high-order byte must be zero. The maximum value for a single byte is 0xFF

The RGB macro selects a red, green, blue (RGB) color based on the arguments supplied and the color capabilities of the output device.

COLORREF RGB(
BYTE bRed, // red component of color
BYTE bGreen, // green component of color
BYTE bBlue // blue component of color
);

COLORREF SetBkColor(
HDC hdc, // handle of device context
COLORREF crColor // background color value
);

*/

TextOut(hdc,20,20,szBuffer,
wsprintf(szBuffer,TEXT("luliuyan!!!"))
);

/*
看看MSDN的文档
int wsprintf(
LPTSTR lpOut, // pointer to buffer for output
LPCTSTR lpFmt, // pointer to format-control string
... // optional arguments
);
返回的是字符串的长度;

BOOL TextOut(
HDC hdc, // handle to device context
int nXStart, // x-coordinate of starting position
int nYStart, // y-coordinate of starting position
LPCTSTR lpString, // pointer to string
int cbString // number of characters in string
);
*/

//创建画笔
hPen=CreatePen(PS_SOLID,3,RGB(255,0,0));
//把画笔选入设备环境
SelectObject(hdc,hPen);

/*
HPEN CreatePen(
int fnPenStyle, // pen style
int nWidth, // pen width
COLORREF crColor // pen color
);

*/
//画x方向的横线
MoveToEx(hdc,0,cyClient/2,NULL);
LineTo(hdc,cxClient,cyClient/2);

//画y方向的横线
MoveToEx(hdc,0,0,NULL);
LineTo(hdc,0,cyClient);

for(i=0;i<NUM;i++)
{
//把横坐标等分成1000个点
apt[i].x=i*cxClient/NUM;

//根据横坐标计算纵坐标
apt[i].y=(int)(cyClient/2*(1-sin(myTWOPI*i/NUM)));

}
Polyline(hdc,apt,NUM);
//不要忘了清除画笔对象;

DeleteObject(hPen);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}

return DefWindowProc(hwnd,message,wParam,lParam);

}

程序运行的结果:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐