# Windows API 中的 GDI
> 原文: [http://zetcode.com/gui/winapi/gdi/](http://zetcode.com/gui/winapi/gdi/)
图形设备接口(GDI)是用于处理图形的接口。 它用于与图形设备(例如监视器,打印机或文件)进行交互。 GDI 允许程序员在屏幕或打印机上显示数据,而不必担心特定设备的详细信息。 GDI 使程序员与硬件隔离。 从程序员的角度来看,GDI 是一组用于处理图形的 API 函数。 GDI 由 2D 向量图形,字体和图像组成。 要开始绘制图形,我们必须获得设备上下文(DC)对象。
每当需要重绘窗口时,都会生成`WM_PAINT`消息。 程序员在窗口的客户区域画图。 操作系统会自动绘制包括标题栏在内的周围框架。
```c
HDC BeginPaint(HWND hwnd, LPPAINTSTRUCT lpPaint);
```
`BeginPaint()`函数为指定的绘图准备窗口,并用绘图信息填充`PAINTSTRUCT`结构。 它返回设备上下文的句柄。 设备上下文是我们执行绘制操作所通过的对象。
```c
BOOL EndPaint(HWND hWnd, const PAINTSTRUCT *lpPaint);
```
每个绘图操作都以`EndPaint()`结束。 每次调用`BeginPaint()`函数都需要此函数,但是仅在绘制完成之后才需要。
## 像素点
像素是可以在视频显示系统中单独处理的图像的最小元素。 `SetPixel()`是在窗口上绘制单个像素的功能。
```c
COLORREF SetPixel(HDC hdc, int x, int y, COLORREF crColor);
```
函数的第一个参数是设备上下文的句柄。 接下来的两个参数是该点的 x 和 y 坐标。 最后一个参数是用于绘制点的颜色。 如果函数成功,则返回值为函数将像素设置为的 RGB 值。
`pixels.c`
```c
#include <windows.h>
#include <time.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void DrawPixels(HWND hwnd);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PWSTR lpCmdLine, int nCmdShow) {
MSG msg;
WNDCLASSW wc = {0};
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpszClassName = L"Pixels";
wc.hInstance = hInstance;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClassW(&wc);
CreateWindowW(wc.lpszClassName, L"Pixels",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 300, 250, NULL, NULL, hInstance, NULL);
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
srand(time(NULL));
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam) {
switch(msg) {
case WM_PAINT:
DrawPixels(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
void DrawPixels(HWND hwnd) {
PAINTSTRUCT ps;
RECT r;
GetClientRect(hwnd, &r);
if (r.bottom == 0) {
return;
}
HDC hdc = BeginPaint(hwnd, &ps);
for (int i=0; i<1000; i++) {
int x = rand() % r.right;
int y = rand() % r.bottom;
SetPixel(hdc, x, y, RGB(255, 0, 0));
}
EndPaint(hwnd, &ps);
}
```
在我们的示例中,我们在窗口的客户区域随机显示 1000 个红色像素。
```c
wc.style = CS_HREDRAW | CS_VREDRAW;
```
这两个标志会导致在调整窗口大小时重新绘制窗口。
```c
srand(time(NULL));
```
`srand()`函数为随机数生成器提供种子。
```c
case WM_PAINT:
DrawPixels(hwnd);
break;
```
绘制是对`WM_PAINT`消息的反应。 实际图形委托给`DrawPixels()`函数。
```c
HDC hdc = BeginPaint(hwnd, &ps);
```
`BeginPaint()`函数准备指定的窗口进行绘图。 它用有关绘图的信息填充`PAINTSTRUCT`结构。 它为指定窗口返回显示设备上下文的句柄。
```c
GetClientRect(hwnd, &r);
```
我们检索窗口客户区的坐标。 我们随机在窗口上绘制,我们需要知道当前可以在哪里绘制。
```c
for (int i=0; i<1000; i++) {
int x = rand() % r.right;
int y = rand() % r.bottom;
SetPixel(hdc, x, y, RGB(255, 0, 0));
}
```
在窗口上随机绘制一千个点。 `SetPixel()`函数使用所选颜色在指定位置绘制像素。
```c
EndPaint(hwnd, &ps);
```
在绘图的结尾,我们调用`EndPaint()`函数。 该函数释放`BeginPaint()`检索到的显示设备上下文。
![Pixels](https://img.kancloud.cn/6e/e3/6ee3ec04031250c8940aee2bc36caba3_300x250.jpg)
图:像素
## 直线
线是基本的图形基元。 它具有两个函数:`MoveToEx()`和`LineTo()`。
```c
BOOL MoveToEx(HDC hdc, int x, int y, LPPOINT lpPoint);
```
`MoveToEx()`函数将当前位置更新到指定点,并有选择地返回先前位置。
```c
BOOL LineTo(HDC hdc, int nXEnd, int nYEnd);
```
`LineTo()`函数从当前位置开始绘制一条线,但不包括指定点。
`lines.c`
```c
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PWSTR lpCmdLine, int nCmdShow) {
MSG msg;
WNDCLASSW wc = {0};
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpszClassName = L"Lines";
wc.hInstance = hInstance;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClassW(&wc);
CreateWindowW(wc.lpszClassName, L"Lines",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 300, 200, NULL, NULL, hInstance, NULL);
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam) {
HDC hdc;
PAINTSTRUCT ps;
switch(msg) {
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
MoveToEx(hdc, 50, 50, NULL);
LineTo(hdc, 250, 50);
HPEN hWhitePen = GetStockObject(WHITE_PEN);
HPEN hOldPen = SelectObject(hdc, hWhitePen);
MoveToEx(hdc, 50, 100, NULL);
LineTo(hdc, 250, 100);
SelectObject(hdc, hOldPen);
EndPaint(hwnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
```
这个例子画了两条线。 一种是黑色,另一种是白色。
```c
MoveToEx(hdc, 50, 50, NULL);
LineTo(hdc, 250, 50);
```
在点(50,50)和(250,50)之间绘制了一条线。 使用默认的`BLACK_PEN`。
```c
HPEN hWhitePen = GetStockObject(WHITE_PEN);
```
`GetStockObject()`函数检索用`WHITE_PEN`值指定的内置白笔的句柄。 通过调用`DeleteObject()`不必删除库存对象(但这不是有害的)。
```c
HPEN hOldPen = SelectObject(hdc, hWhitePen);
```
`SelectObject()`函数将一个对象选择到指定的设备上下文(DC)中。 新对象将替换相同类型的先前对象。
```c
SelectObject(hdc, hOldPen);
```
我们恢复到旧的`BLACK_PEN`笔。
![Lines](https://img.kancloud.cn/3a/c1/3ac1608b5c384052859a41505eaeb0b2_300x200.jpg)
图:直线
## 长方形
要绘制矩形,我们使用`Rectangle()`函数。
```c
BOOL Rectangle(HDC hdc, int nLeftRect, int nTopRect, int nRightRect,
int nBottomRect);
```
函数的第一个参数是设备上下文的句柄。 接下来的两个参数是矩形左上角的 x 和 y 坐标。 最后两个参数是矩形右下角的 x,y 坐标。 如果函数失败,则返回值为零。 如果成功,则返回值为非零。
`rectangle.c`
```c
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PWSTR lpCmdLine, int nCmdShow) {
MSG msg;
WNDCLASSW wc = {0};
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpszClassName = L"Rectangle";
wc.hInstance = hInstance;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClassW(&wc);
CreateWindowW(wc.lpszClassName, L"Rectangle",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 250, 200, NULL, NULL, hInstance, NULL);
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam) {
HDC hdc;
PAINTSTRUCT ps;
switch(msg) {
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
Rectangle(hdc, 50, 50, 200, 100);
EndPaint(hwnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
```
使用当前的笔绘制矩形的轮廓。 使用当前画笔绘制背景。
```c
Rectangle(hdc, 50, 50, 200, 100);
```
使用`Rectangle()`函数绘制矩形。 我们使用两个点绘制矩形:左上角点和右下角点。
![Rectangle](https://img.kancloud.cn/dc/1c/dc1c181cc92e35056824b340af47fb34_250x200.jpg)
图:矩形
## 贝塞尔曲线
贝塞尔曲线是由数学公式定义的曲线。 绘制曲线的数学方法由 PierreBézier 在 1960 年代后期创建,用于雷诺的汽车制造。
```c
BOOL PolyBezier(HDC hdc, const POINT *lppt, DWORD cPoints);
```
函数的第一个参数是设备上下文的句柄。 第二个参数是指向`POINT`结构数组的指针,该数组包含曲线的端点和控制点。
`beziercurve.c`
```c
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PWSTR lpCmdLine, int nCmdShow) {
MSG msg;
WNDCLASSW wc = {0};
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpszClassName = L"BezierCurve";
wc.hInstance = hInstance;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClassW(&wc);
CreateWindowW(wc.lpszClassName, L"Beziér curve",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 500, 200, NULL, NULL, hInstance, NULL);
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam) {
HDC hdc;
PAINTSTRUCT ps;
POINT points[4] = { 20, 40, 320, 200, 330, 110, 450, 40 };
switch(msg) {
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
PolyBezier(hdc, points, 4);
EndPaint(hwnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
```
在示例中,我们使用`PolyBezier()`函数绘制一条曲线。
```c
POINT points[4] = { 20, 40, 320, 200, 330, 110, 450, 40 };
```
这些点形成贝塞尔曲线。 第一点是起点。 接下来的两个点是控制点。 最后一点是曲线的终点。
```c
PolyBezier(hdc, points, 4);
```
`PolyBezier()`函数绘制曲线。
![Bézier curve](https://img.kancloud.cn/13/ed/13ed1fbc729c71aeb786f6802db43bcd_500x200.jpg)
图:贝塞尔曲线
## 钢笔
笔是基本图形对象。 它用于绘制矩形,椭圆形,多边形或其他形状的线,曲线和轮廓。
笔有两种类型:化妆笔和几何笔。化妆笔是固定宽度为 1 的简单笔。它们具有三个属性:宽度,样式和颜色。 它们比几何笔更有效。 可以使用`CreatePen()`,`CreatePenIndirect()`或`ExtCreatePen()`函数创建化妆笔。
几何笔比化妆笔复杂。 它们具有七个属性:宽度,样式,颜色,图案,剖面线,端盖和连接样式。 几何笔是使用`ExtCreatePen()`函数创建的。
```c
HPEN CreatePen(int fnPenStyle, int nWidth, COLORREF crColor);
```
`CreatePen()`函数创建具有指定样式,宽度和颜色的逻辑笔。
```c
HPEN ExtCreatePen(DWORD dwPenStyle, DWORD dwWidth, const LOGBRUSH *lplb,
DWORD dwStyleCount, const DWORD *lpStyle);
```
`ExtCreatePen()`函数创建逻辑的化妆笔或几何笔。 第一个参数是类型,样式,端盖和联接属性的组合。 第二个参数是笔的宽度。 第三个参数是指向`LOGBRUSH`结构的指针。 该结构定义了物理笔刷的样式,颜色和图案。 第四个参数是`lpStyle`数组的长度,以`DWORD`单位。 如果`dwPenStyle`不是`PS_USERSTYLE`,则此值必须为零。 样式计数限制为 16。最后一个参数是指向数组的指针。 第一个值以用户定义的样式指定第一个笔划线的长度,第二个值指定第一个空格的长度,依此类推。 如果`dwPenStyle`不是`S_USERSTYLE`,则此指针必须为`NULL`。
创建笔后,我们使用`SelectObject()`函数将其选择到应用的设备上下文中。 从现在开始,应用使用此笔在其客户区中进行任何画线操作。
### 笔样式
笔样式是应用于线对象的特定图案。 有预定义的笔样式,例如`PS_SOLID`,`PS_DASH`,`PS_DOT`或`PS_DASHDOT`。 也可以创建自定义笔样式。
`penstyles.c`
```c
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void DrawLines(HWND);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PWSTR lpCmdLine, int nCmdShow) {
MSG msg;
WNDCLASSW wc = {0};
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpszClassName = L"Pen styles";
wc.hInstance = hInstance;
wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClassW(&wc);
CreateWindowW(wc.lpszClassName, L"Pen styles",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 350, 180, NULL, NULL, hInstance, NULL);
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam) {
switch(msg) {
case WM_PAINT:
DrawLines(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
void DrawLines(HWND hwnd) {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
HPEN hPen1 = CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
HPEN hPen2 = CreatePen(PS_DASH, 1, RGB(0, 0, 0));
HPEN hPen3 = CreatePen(PS_DOT, 1, RGB(0, 0, 0));
HPEN hPen4 = CreatePen(PS_DASHDOT, 1, RGB(0, 0, 0));
HPEN hPen5 = CreatePen(PS_DASHDOTDOT, 1, RGB(0, 0, 0));
HPEN holdPen = SelectObject(hdc, hPen1);
MoveToEx(hdc, 50, 30, NULL);
LineTo(hdc, 300, 30);
SelectObject(hdc, hPen2);
MoveToEx(hdc, 50, 50, NULL);
LineTo(hdc, 300, 50);
SelectObject(hdc, hPen2);
MoveToEx(hdc, 50, 70, NULL);
LineTo(hdc, 300, 70);
SelectObject(hdc, hPen3);
MoveToEx(hdc, 50, 90, NULL);
LineTo(hdc, 300, 90);
SelectObject(hdc, hPen4);
MoveToEx(hdc, 50, 110, NULL);
LineTo(hdc, 300, 110);
SelectObject(hdc, holdPen);
DeleteObject(hPen1);
DeleteObject(hPen2);
DeleteObject(hPen3);
DeleteObject(hPen4);
DeleteObject(hPen5);
EndPaint(hwnd, &ps);
}
```
在我们的示例中,我们使用五种不同的笔样式绘制五根不同的线。
```c
case WM_PAINT:
DrawLines(hwnd);
break;
```
实际图形委托给`DrawLines()`函数。
```c
HPEN hPen1 = CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
```
`CreatePen()`函数创建具有指定样式,宽度和颜色的逻辑笔。 `PS_SOLID`代表实心笔。 我们使用`RGB`宏为笔生成颜色。
```c
SelectObject(hdc, hPen1);
```
要激活笔,我们调用`SelectObject()`函数。
```c
MoveToEx(hdc, 50, 30, NULL);
LineTo(hdc, 300, 30);
```
要绘制线条,我们使用`MoveToEx()`和`LineTo()`函数。
```c
DeleteObject(hPen1);
DeleteObject(hPen2);
DeleteObject(hPen3);
DeleteObject(hPen4);
DeleteObject(hPen5);
```
最后,我们清理资源。
![Pen styles](https://img.kancloud.cn/d8/dc/d8dcf46768fd50b849841e7420b843ec_350x180.jpg)
图:笔的样式
### 直线连接
可以使用三种不同的连接样式来连接线:`PS_JOIN_BEVEL`,`PS_JOIN_MITEl`和`PS_JOIN_ROUND`。
`linejoins.c`
```c
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void DoDrawing(HWND);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PWSTR lpCmdLine, int nCmdShow) {
MSG msg;
WNDCLASSW wc = {0};
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpszClassName = L"Pens";
wc.hInstance = hInstance;
wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClassW(&wc);
CreateWindowW(wc.lpszClassName, L"Line joins",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 450, 200, NULL, NULL, hInstance, NULL);
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam) {
switch(msg) {
case WM_PAINT:
DoDrawing(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
void DoDrawing(HWND hwnd) {
LOGBRUSH brush;
COLORREF col = RGB(0, 0, 0);
DWORD pen_style = PS_SOLID | PS_JOIN_MITER | PS_GEOMETRIC;
brush.lbStyle = BS_SOLID;
brush.lbColor = col;
brush.lbHatch = 0;
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
HPEN hPen1 = ExtCreatePen(pen_style, 8, &brush, 0, NULL);
HPEN holdPen = SelectObject(hdc, hPen1);
POINT points[5] = { { 30, 30 }, { 130, 30 }, { 130, 100 },
{ 30, 100 }, { 30, 30}};
Polygon(hdc, points, 5);
pen_style = PS_SOLID | PS_GEOMETRIC | PS_JOIN_BEVEL;
HPEN hPen2 = ExtCreatePen(pen_style, 8, &brush, 0, NULL);
SelectObject(hdc, hPen2);
DeleteObject(hPen1);
POINT points2[5] = { { 160, 30 }, { 260, 30 }, { 260, 100 },
{ 160, 100 }, {160, 30 }};
MoveToEx(hdc, 130, 30, NULL);
Polygon(hdc, points2, 5);
pen_style = PS_SOLID | PS_GEOMETRIC | PS_JOIN_ROUND;
HPEN hPen3 = ExtCreatePen(pen_style, 8, &brush, 0, NULL);
SelectObject(hdc, hPen3);
DeleteObject(hPen2);
POINT points3[5] = { { 290, 30 }, { 390, 30 }, { 390, 100 },
{ 290, 100 }, {290, 30 }};
MoveToEx(hdc, 260, 30, NULL);
Polygon(hdc, points3, 5);
SelectObject(hdc, holdPen);
DeleteObject(hPen3);
EndPaint(hwnd, &ps);
}
```
在示例中,我们显示了矩形上的三种类型的线连接。
```c
pen_style = PS_SOLID | PS_GEOMETRIC | PS_JOIN_BEVEL;
HPEN hPen2 = ExtCreatePen(pen_style, 8, &brush, 0, NULL);
```
`ExtCreatePen()`函数创建一个带有`PS_JOIN_BEVEL`连接的实心几何笔。
```c
POINT points2[5] = { { 160, 30 }, { 260, 30 }, { 260, 100 },
{ 160, 100 }, {160, 30 }};
MoveToEx(hdc, 130, 30, NULL);
Polygon(hdc, points2, 5);
```
从提供的点开始,我们使用`Polygon()`函数创建一个矩形。
![Line joins](https://img.kancloud.cn/f3/3a/f33ae33bc658cfcba008215ef61b89ca_450x200.jpg)
图:直线连接
## 笔刷
画笔是基本图形对象。 它用于绘制图形形状的背景,例如矩形,椭圆形或多边形。 笔刷可以是纯色,阴影线或自定义位图图案。
### 实心笔刷
实心画笔是一种颜色。 它是用`CreateSolidBrush()`函数创建的。
```c
HBRUSH CreateSolidBrush(COLORREF crColor);
```
`CreateSolidBrush()`函数创建具有指定纯色的画笔。
`solidbrush.c`
```c
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void DrawRectangles(HWND);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PWSTR lpCmdLine, int nCmdShow) {
MSG msg;
WNDCLASSW wc = {0};
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpszClassName = L"Brush";
wc.hInstance = hInstance;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClassW(&wc);
CreateWindowW(wc.lpszClassName, L"Solid Brush",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 220, 240, NULL, NULL, hInstance, NULL);
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam) {
switch(msg) {
case WM_PAINT:
DrawRectangles(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
void DrawRectangles(HWND hwnd) {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
HPEN hPen = CreatePen(PS_NULL, 1, RGB(0, 0, 0));
HPEN holdPen = SelectObject(hdc, hPen);
HBRUSH hBrush1 = CreateSolidBrush(RGB(121, 90, 0));
HBRUSH hBrush2 = CreateSolidBrush(RGB(240, 63, 19));
HBRUSH hBrush3 = CreateSolidBrush(RGB(240, 210, 18));
HBRUSH hBrush4 = CreateSolidBrush(RGB(9, 189, 21));
HBRUSH holdBrush = SelectObject(hdc, hBrush1);
Rectangle(hdc, 30, 30, 100, 100);
SelectObject(hdc, hBrush2);
Rectangle(hdc, 110, 30, 180, 100);
SelectObject(hdc, hBrush3);
Rectangle(hdc, 30, 110, 100, 180);
SelectObject(hdc, hBrush4);
Rectangle(hdc, 110, 110, 180, 180);
SelectObject(hdc, holdPen);
SelectObject(hdc, holdBrush);
DeleteObject(hPen);
DeleteObject(hBrush1);
DeleteObject(hBrush2);
DeleteObject(hBrush3);
DeleteObject(hBrush4);
EndPaint(hwnd, &ps);
}
```
在示例中,我们创建了 4 个矩形,其中填充了 4 种不同的纯色。
```c
HBRUSH hBrush1 = CreateSolidBrush(RGB(121, 90, 0));
```
在这里,我们创建一个纯色笔刷。
```c
HBRUSH holdBrush = SelectObject(hdc, hBrush1);
```
在设备上下文中选择了一个新画笔。
![Solid brushes](https://img.kancloud.cn/8a/7b/8a7bb44358e90921a74de118a810b111_220x240.jpg)
图:实心刷
### 舱口笔刷
有六种预定义的舱口笔刷。 在我们的示例中,我们展示了所有这些。
```c
HBRUSH CreateHatchBrush(int fnStyle, COLORREF clrref);
```
`CreateHatchBrush()`函数创建具有指定填充图案和颜色的画笔。
`hatchbrushes.c`
```c
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void DrawRectangles(HWND hwnd);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PWSTR lpCmdLine, int nCmdShow) {
MSG msg;
WNDCLASSW wc = {0};
wc.style = CS_VREDRAW | CS_HREDRAW;
wc.lpszClassName = L"Brush";
wc.hInstance = hInstance;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClassW(&wc);
CreateWindowW(wc.lpszClassName, L"Hatch brushes",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 300, 220, NULL, NULL, hInstance, NULL);
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam) {
switch(msg) {
case WM_PAINT:
DrawRectangles(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
void DrawRectangles(HWND hwnd) {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
HPEN hPen = CreatePen(PS_NULL, 1, RGB(0, 0, 0));
HPEN holdPen = SelectObject(hdc, hPen);
HBRUSH hBrush1 = CreateHatchBrush(HS_BDIAGONAL, RGB(0, 0, 0));
HBRUSH hBrush2 = CreateHatchBrush(HS_FDIAGONAL, RGB(0, 0, 0));
HBRUSH hBrush3 = CreateHatchBrush(HS_CROSS, RGB(0, 0, 0));
HBRUSH hBrush4 = CreateHatchBrush(HS_HORIZONTAL, RGB(0, 0, 0));
HBRUSH hBrush5 = CreateHatchBrush(HS_DIAGCROSS, RGB(0, 0, 0));
HBRUSH hBrush6 = CreateHatchBrush(HS_VERTICAL, RGB(0, 0, 0));
HBRUSH holdBrush = SelectObject(hdc, hBrush1);
DWORD col = GetSysColor(COLOR_BTNFACE);
SetBkColor(hdc, col);
Rectangle(hdc, 30, 30, 100, 80);
SelectObject(hdc, hBrush2);
Rectangle(hdc, 110, 30, 180, 80);
SelectObject(hdc, hBrush3);
Rectangle(hdc, 190, 30, 260, 80);
SelectObject(hdc, hBrush4);
Rectangle(hdc, 30, 110, 100, 160);
SelectObject(hdc, hBrush5);
Rectangle(hdc, 110, 110, 180, 160);
SelectObject(hdc, hBrush6);
Rectangle(hdc, 190, 110, 260, 160);
SelectObject(hdc, holdPen);
SelectObject(hdc, holdBrush);
DeleteObject(hPen);
DeleteObject(hBrush1);
DeleteObject(hBrush2);
DeleteObject(hBrush3);
DeleteObject(hBrush4);
DeleteObject(hBrush5);
DeleteObject(hBrush6);
EndPaint(hwnd, &ps);
}
```
此示例与上一个示例非常相似。 我们仅使用一个新的函数调用`CreateHatchBrush()`。
```c
HBRUSH hBrush1 = CreateHatchBrush(HS_BDIAGONAL, RGB(0, 0, 0));
```
将创建对角线阴影笔刷。
```c
HBRUSH holdBrush = SelectObject(hdc, hBrush1);
```
画笔被选择到设备上下文中。 返回旧画笔的句柄。
```c
DeleteObject(hBrush1);
```
笔刷对象被删除。
![Hatch brushes](https://img.kancloud.cn/6b/23/6b232c3311c285026042fc33c8c8259b_300x220.jpg)
图:舱口刷
### 定制笔刷
可以使用`CreatePatternBrush()`函数创建自定义画笔。
```c
HBRUSH CreatePatternBrush(HBITMAP hbmp);
```
该函数获取要用于创建画笔的位图的句柄。
`custombrush.c`
```c
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PWSTR lpCmdLine, int nCmdShow) {
MSG msg;
WNDCLASSW wc = {0};
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpszClassName = L"Custom brush";
wc.hInstance = hInstance;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClassW(&wc);
CreateWindowW(wc.lpszClassName, L"Custom brush",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 300, 200, NULL, NULL, hInstance, NULL);
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam) {
HDC hdc;
PAINTSTRUCT ps;
static HBITMAP hBtm;
UINT bits[8] = { 0x111111ff, 0xffffffff, 0xffffffff, 0xffffffff,
0x00000000, 0x00000000, 0x00000000, 0x00000000 };
switch(msg) {
case WM_CREATE:
hBtm = CreateBitmap(8, 8, 1, 1, (LPBYTE) bits);
break;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
HBRUSH hCustomBrush = CreatePatternBrush(hBtm);
HBRUSH hOldBrush = SelectObject(hdc, hCustomBrush);
SelectObject(hdc, GetStockObject(NULL_PEN));
Rectangle(hdc, 20, 20, 250, 160);
SelectObject(hdc, hOldBrush);
DeleteObject(hCustomBrush);
SelectObject(hdc, GetStockObject(BLACK_PEN));
EndPaint(hwnd, &ps);
break;
case WM_DESTROY:
DeleteObject(hBtm);
PostQuitMessage(0);
return 0;
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
```
该示例绘制了一个矩形。 其内部充满了自定义的画笔图案。
```c
hBtm = CreateBitmap(8, 8, 1, 1, (LPBYTE) bits);
```
我们使用`CreateBitmap()`函数创建位图图案。
```c
HBRUSH hCustomBrush = CreatePatternBrush(hBtm);
```
`CreatePatternBrush()`函数从提供的位图创建画笔对象。
```c
HBRUSH hOldBrush = SelectObject(hdc, hCustomBrush);
```
我们使用`SelectObject()`函数选择自定义画笔。
```c
SelectObject(hdc, GetStockObject(NULL_PEN));
```
我们不会绘制矩形的轮廓。 当我们选择`NULL_PEN`时,没有画出轮廓。
```c
Rectangle(hdc, 20, 20, 250, 160);
```
矩形用`Rectangle()`函数绘制; 其内部使用选定的自定义画笔绘制。
![Custom brush](https://img.kancloud.cn/03/e6/03e60b07dfef67f382f88c242924fa04_300x200.jpg)
图:自定义刷
## 形状
形状是更复杂的几何对象。 在下面的示例中,我们将绘制各种几何形状。
`shapes.c`
```c
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PWSTR lpCmdLine, int nCmdShow) {
MSG msg;
WNDCLASSW wc = {0};
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpszClassName = L"Shapes";
wc.hInstance = hInstance;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClassW(&wc);
CreateWindowW(wc.lpszClassName, L"Shapes",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 390, 230, NULL, NULL, hInstance, NULL);
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam) {
HDC hdc;
PAINTSTRUCT ps;
const POINT polygon[10] = { 30, 145, 85, 165, 105,
110, 65, 125, 30, 105 };
switch(msg) {
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
Ellipse(hdc, 30, 30, 120, 90);
RoundRect(hdc, 150, 30, 240, 90, 15, 20);
Chord(hdc, 270, 30, 360, 90, 270, 45, 360, 45);
Polygon(hdc, polygon, 5);
Rectangle(hdc, 150, 110, 230, 160);
EndPaint(hwnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
```
在我们的示例中,我们创建了一个椭圆,一个圆角矩形,一个弦,一个多边形和一个矩形。
```c
Ellipse(hdc, 30, 30, 120, 90);
```
`Ellipse()`函数绘制一个椭圆。 `Ellipse()`的参数是边界矩形的左上角和右下角的 x 和 y 坐标。 在此矩形内绘制椭圆。
```c
RoundRect(hdc, 150, 30, 240, 90, 15, 20);
```
`RoundRect()`函数绘制带有圆角的矩形。 `RoundRect()`的参数是边界矩形的左上角和右下角的 x 和 y 坐标。 最后两个参数是用于绘制圆角的椭圆的宽度和高度。
```c
Chord(hdc, 270, 30, 360, 90, 270, 45, 360, 45);
```
`Chord()`函数绘制和弦。 和弦是由椭圆和线段的交点界定的区域。 前四个参数是边界矩形的左上角的 x 和 y 坐标以及右下角的 x 和 y 坐标。 接下来的四个参数是定义弦的起点的径向的 x 和 y 坐标以及定义弦的终点的径向的 x 和 y 坐标。
```c
Polygon(hdc, polygon, 5);
```
`Polygon()`函数绘制由两个或多个通过直线连接的顶点组成的多边形。 多边形是指向`POINT`结构数组的指针,该数组指定多边形的顶点。 最后一个参数是数组中的点数。
```c
Rectangle(hdc, 150, 110, 230, 160);
```
`Rectangle()`函数绘制一个矩形。 该函数的参数是矩形左上角和右下角的 x 和 y 坐标。
![Shapes](https://img.kancloud.cn/73/0c/730c7080c5a2c53bd8f238181e1529f0_390x230.jpg)
图:形状
## 星形
在下面的示例中,我们使用`Polyline()`函数绘制星形。
```c
BOOL Polyline(HDC hdc, const POINT *lppt, int cPoints);
```
`Polyline()`函数通过连接指定数组中的点来绘制一系列线段。 函数的第一个参数是设备上下文的句柄。 第二个参数是指向`POINT`结构数组的指针。 第三个参数是数组中的点数。 此数字必须大于或等于 2。
`star.c`
```c
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PWSTR lpCmdLine, int nCmdShow) {
MSG msg;
WNDCLASSW wc = {0};
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpszClassName = L"Star";
wc.hInstance = hInstance;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClassW(&wc);
CreateWindowW(wc.lpszClassName, L"Star",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 300, 250, NULL, NULL, hInstance, NULL);
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam) {
HDC hdc;
PAINTSTRUCT ps;
POINT points[11] = {
{ 10, 85 },
{ 85, 75 },
{ 110, 10 },
{ 135, 75 },
{ 210, 85 },
{ 160, 125 },
{ 170, 190 },
{ 110, 150 },
{ 50, 190 },
{ 60, 125 },
{ 10, 85 }
};
switch(msg) {
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
Polyline(hdc, points, 11);
EndPaint(hwnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
```
该示例绘制一个星形对象。
```c
POINT points[11] = {
{ 10, 85 },
{ 85, 75 },
{ 110, 10 },
{ 135, 75 },
{ 210, 85 },
{ 160, 125 },
{ 170, 190 },
{ 110, 150 },
{ 50, 190 },
{ 60, 125 },
{ 10, 85 }
};
```
这是恒星的`POINTS`的数组。
```c
Polyline(hdc, points, 11);
```
`Polyline()`函数绘制星形。
![Star](https://img.kancloud.cn/c6/62/c662a6f8a203e1300ecdcd985dae12fb_300x250.jpg)
图:星星
## 文本
`TextOutW()`函数使用当前选择的字体,背景色和文本色在指定位置写入字符串。
```c
BOOL TextOut(HDC hdc, int nXStart, int nYStart, LPCTSTR lpString, int cchString);
```
函数的第一个参数是设备上下文的句柄。 接下来的两个参数是系统用于对齐字符串的参考点的 x 和 y 坐标。 第三个参数是指向要绘制的字符串的指针。 最后一个参数是字符串的长度。
`sonnet55.c`
```c
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PWSTR lpCmdLine, int nCmdShow) {
MSG msg ;
WNDCLASSW wc = {0};
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpszClassName = L"Sonnet 55";
wc.hInstance = hInstance;
wc.hbrBackground = GetSysColorBrush(COLOR_BTNFACE);
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClassW(&wc);
CreateWindowW(wc.lpszClassName, L"Sonnet 55",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 390, 350, NULL, NULL, hInstance, NULL);
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam) {
HDC hdc;
PAINTSTRUCT ps;
DWORD color;
HFONT hFont, holdFont;
static wchar_t *ver1 = L"Not marble, nor the gilded monuments";
static wchar_t *ver2 = L"Of princes, shall outlive this powerful rhyme;";
static wchar_t *ver3 = L"But you shall shine more bright in these contents";
static wchar_t *ver4 = L"Than unswept stone, besmear'd with sluttish time.";
static wchar_t *ver5 = L"When wasteful war shall statues overturn,";
static wchar_t *ver6 = L"And broils root out the work of masonry,";
static wchar_t *ver7 = L"Nor Mars his sword, nor war's quick fire shall burn";
static wchar_t *ver8 = L"The living record of your memory.";
static wchar_t *ver9 = L"'Gainst death, and all oblivious enmity";
static wchar_t *ver10 = L"Shall you pace forth; your praise shall still find room";
static wchar_t *ver11 = L"Even in the eyes of all posterity";
static wchar_t *ver12 = L"That wear this world out to the ending doom.";
static wchar_t *ver13 = L"So, till the judgment that yourself arise,";
static wchar_t *ver14 = L"You live in this, and dwell in lovers' eyes.";
switch(msg) {
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
color = GetSysColor(COLOR_BTNFACE);
SetBkColor(hdc, color);
hFont = CreateFontW(15, 0, 0, 0, FW_MEDIUM, 0, 0, 0, 0,
0, 0, 0, 0, L"Georgia");
holdFont = SelectObject(hdc, hFont);
TextOutW(hdc, 50, 20, ver1, lstrlenW(ver1));
TextOutW(hdc, 50, 40, ver2, lstrlenW(ver2));
TextOutW(hdc, 50, 60, ver3, lstrlenW(ver3));
TextOutW(hdc, 50, 80, ver4, lstrlenW(ver4));
TextOutW(hdc, 50, 100, ver5, lstrlenW(ver5));
TextOutW(hdc, 50, 120, ver6, lstrlenW(ver6));
TextOutW(hdc, 50, 140, ver7, lstrlenW(ver7));
TextOutW(hdc, 50, 160, ver8, lstrlenW(ver8));
TextOutW(hdc, 50, 180, ver9, lstrlenW(ver9));
TextOutW(hdc, 50, 200, ver10, lstrlenW(ver10));
TextOutW(hdc, 50, 220, ver11, lstrlenW(ver11));
TextOutW(hdc, 50, 240, ver12, lstrlenW(ver12));
TextOutW(hdc, 50, 260, ver13, lstrlenW(ver13));
TextOutW(hdc, 50, 280, ver14, lstrlenW(ver14));
SelectObject(hdc, holdFont);
DeleteObject(hFont);
EndPaint(hwnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
```
我们使用`TextOutW()`函数在窗口上绘制几节经文。
```c
color = GetSysColor(COLOR_BTNFACE);
SetBkColor(hdc, color);
```
默认情况下,如果我们在窗口的工作区上绘制一些文本,则背景设置为白色。 我们可以通过使用`SetBkColor()`函数设置背景颜色来更改此设置。 我们使用了典型的 Windows 灰色。 `GetSysColor()`函数用于获取按钮,标题或窗口控件背景中使用的系统颜色。
```c
hFont = CreateFontW(15, 0, 0, 0, FW_MEDIUM, 0, 0, 0, 0,
0, 0, 0, 0, L"Georgia");
holdFont = SelectObject(hdc, hFont);
```
在这里,我们使用`CreateFontW()`函数创建一个字体对象。 该函数有 14 个参数; 我们不必全部指定。 我们仅指定字体大小,字体粗细和 fontface 参数。
```c
TextOutW(hdc, 50, 20, verse1, lstrlenW(verse1));
```
使用`TextOutW()`函数将文本绘制到窗口上。 字符串的长度由`lstrlenW()`函数确定。
![Text](https://img.kancloud.cn/e4/b9/e4b952952db0a4073ff6affa925f2b32_390x350.jpg)
图:文本
## 绘制位图
位图是一个图形对象,用于创建,处理图像并将其作为文件存储在磁盘上。 BMP 是 Windows 的本机位图格式,实际上用于存储任何类型的位图数据。
`drawbitmap.c`
```c
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PWSTR lpCmdLine, int nCmdShow) {
MSG msg;
WNDCLASSW wc = {0};
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpszClassName = L"Draw Bitmap";
wc.hInstance = hInstance;
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClassW(&wc);
CreateWindowW(wc.lpszClassName, L"Draw Bitmap",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
100, 100, 280, 220, NULL, NULL, hInstance, NULL);
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam) {
static HBITMAP hBitmap;
HDC hdc;
PAINTSTRUCT ps;
BITMAP bitmap;
HDC hdcMem;
HGDIOBJ oldBitmap;
switch(msg) {
case WM_CREATE:
hBitmap = (HBITMAP) LoadImageW(NULL, L"C:\\prog\\slovakia.bmp",
IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
if (hBitmap == NULL) {
MessageBoxW(hwnd, L"Failed to load image", L"Error", MB_OK);
}
break;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
hdcMem = CreateCompatibleDC(hdc);
oldBitmap = SelectObject(hdcMem, hBitmap);
GetObject(hBitmap, sizeof(bitmap), &bitmap);
BitBlt(hdc, 5, 5, bitmap.bmWidth, bitmap.bmHeight,
hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, oldBitmap);
DeleteDC(hdcMem);
EndPaint(hwnd, &ps);
break;
case WM_DESTROY:
DeleteObject(hBitmap);
PostQuitMessage(0);
return 0;
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
```
该示例在窗口上绘制了斯洛伐克国旗。 图片为 BMP 文件格式。
```c
static HBITMAP hBitmap;
```
`HBITMAP`是位图对象的句柄。
```c
BITMAP bitmap;
```
`BITMAP`结构定义位图的类型,宽度,高度,颜色格式和位值。
```c
hBitmap = (HBITMAP) LoadImageW(NULL, L"C:\\prog\\slovakia.bmp",
IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
```
`LoadImageW()`函数从磁盘加载 BMP 图像。 它返回位图的句柄。
```c
GetObject(hBitmap, sizeof(bitmap), &bitmap);
```
`GetObject()`函数在提供的`BITMAP`结构中存储有关位图的信息。
```c
hdcMem = CreateCompatibleDC(hdc);
```
`CreateCompatibleDC()`函数创建与应用当前屏幕兼容的存储设备上下文。
```c
oldBitmap = SelectObject(hdcMem, hBitmap);
```
`SelectObject()`函数将一个对象选择到存储设备上下文中。 在将位图用于任何事物之前,必须先将其选择到存储设备上下文中。
```c
BitBlt(hdc, 5, 5, bitmap.bmWidth, bitmap.bmHeight, hdcMem, 0, 0, SRCCOPY);
```
`BitBlt()`函数执行与像素矩形相对应的颜色数据从指定的源设备上下文到目标设备上下文的位块传输。
```c
SelectObject(hdcMem, oldBitmap);
```
应用在完成使用新对象的绘制之后,应始终将其替换为原始的默认对象。
```c
DeleteDC(hdcMem);
```
与存储设备上下文关联的资源被释放。
![Drawing a bitmap](https://img.kancloud.cn/17/85/178579c7e2c04de44f11eead918886e4_280x220.jpg)
图:绘制位图
在 Windows API 教程的这一部分中,我们进行了一些绘制。
- ZetCode 数据库教程
- MySQL 教程
- MySQL 简介
- MySQL 安装
- MySQL 的第一步
- MySQL 快速教程
- MySQL 存储引擎
- MySQL 数据类型
- 在 MySQL 中创建,更改和删除表
- MySQL 表达式
- 在 MySQL 中插入,更新和删除数据
- MySQL 中的SELECT语句
- MySQL 子查询
- MySQL 约束
- 在 MySQL 中导出和导入数据
- 在 MySQL 中连接表
- MySQL 函数
- MySQL 中的视图
- MySQL 中的事务
- MySQL 存储过程
- MySQL Python 教程
- MySQL Perl 教程
- MySQL & Perl DBI
- 使用 Perl 连接到 MySQL 数据库
- MySQL 中的 Perl 错误处理
- 使用 Perl 进行 MySQL 查询
- 在 MySQL 中使用 Perl 绑定参数&列
- 在 MySQL 中使用 Perl 处理图像
- 使用 Perl 获取 MySQL 元数据
- Perl 的 MySQL 事务
- MySQL C API 编程教程
- MySQL Visual Basic 教程
- MySQL PHP 教程
- MySQL Java 教程
- MySQL Ruby 教程
- MySQL C# 教程
- SQLite 教程
- SQLite 简介
- sqlite3 命令行工具
- 在 SQLite 中创建,删除和更改表
- SQLite 表达式
- SQLite 插入,更新,删除数据
- SQLite SELECT语句
- SQLite 约束
- SQLite 连接表
- SQLite 函数
- SQLite 视图,触发器,事务
- SQLite C 教程
- SQLite Python 教程
- SQLite Perl 教程
- Perl DBI
- 使用 Perl 连接到 SQLite 数据库
- SQLite Perl 错误处理
- 使用 Perl 的 SQLite 查询
- 使用 Perl 绑定 SQLite 参数&列
- 使用 Perl 在 SQLite 中处理图像
- 使用 Perl 获取 SQLite 元数据
- 使用 Perl 进行 SQLite 事务
- SQLite Ruby 教程
- 连接到 SQLite 数据库
- 在 SQLite 中使用 Ruby 进行 SQL 查询
- 绑定参数
- 处理图像
- 使用 Ruby 获取 SQLite 元数据
- Ruby 的 SQLite 事务
- SQLite C# 教程
- SQLite C# 简介
- 使用SqliteDataReader检索数据
- ADO.NET 数据集
- 使用 C# 在 SQLite 中处理图像
- 使用 C# 获取 SQLite 元数据
- 使用 C# 的 SQLite 事务
- SQLite Visual Basic 教程
- SQLite Visual Basic 简介
- 使用SqliteDataReader检索数据
- ADO.NET 的数据集
- 使用 Visual Basic 在 SQLite 中处理图像
- 使用 Visual Basic 获取 SQLite 元数据
- 使用 Visual Basic 的 SQLite 事务
- PostgreSQL C 教程
- PostgreSQL Ruby 教程
- PostgreSQL PHP 教程
- PostgreSQL PHP 编程简介
- 在 PostgreSQL 中使用 PHP 检索数据
- 在 PostgreSQL 中使用 PHP 处理图像
- 用 PHP 获取 PostgreSQL 元数据
- 在 PostgreSQL 中使用 PHP 进行事务
- PostgreSQL Java 教程
- Apache Derby 教程
- Derby 简介
- Derby 的安装&配置
- Derby 工具
- ij 工具
- Derby 中的 SQL 查询
- 在 Derby 中使用 JDBC 进行编程
- Derby 安全
- 使用 Derby & Apache Tomcat
- NetBeans 和 Derby
- SQLAlchemy 教程
- SQLAlchemy 简介
- 原始 SQL
- 模式定义语言
- SQL 表达式语言
- SQLAlchemy 中的对象关系映射器
- MongoDB PHP 教程
- MongoDB JavaScript 教程
- MongoDB Ruby 教程
- Spring JdbcTemplate 教程
- JDBI 教程
- MyBatis 教程
- Hibernate Derby 教程
- ZetCode .NET 教程
- Visual Basic 教程
- Visual Basic
- Visual Basic 语法结构
- 基本概念
- Visual Basic 数据类型
- Visual Basic 中的字符串
- 运算符
- 控制流
- Visual Basic 数组
- Visual Basic 中的过程&函数
- 在 Visual Basic 中组织代码
- 面向对象编程
- Visual Basic 中的面向对象编程 II
- Visual Basic 中的集合
- 输入和输出
- C# 教程
- C# 语言
- C# 语法结构
- C# 基础
- C# 数据类型
- C# 中的字符串
- C# 运算符
- C# 中的流控制
- C# 数组
- C# 面向对象编程
- C# 中的方法
- C# 面向对象编程 II
- C# 属性
- C# 结构
- C# 委托
- 命名空间
- C# 集合
- C# 输入和输出
- C# 目录教程
- C# 字典教程
- 在 C# 中读取文本文件
- C# 中的日期和时间
- 在 C# 中读取网页
- C# HttpClient教程
- ASP.NET Core 教程
- ZetCode 图形教程
- Java 2D 游戏教程
- Java 游戏基础
- 动画
- 移动精灵
- 碰撞检测
- Java 益智游戏
- Java Snake
- Breakout 游戏
- Java 俄罗斯方块
- Java 吃豆人
- Java 太空侵略者
- Java 扫雷
- Java 推箱子
- Java 2D 教程
- 介绍
- 基本绘图
- 形状和填充
- 透明度
- 合成
- 剪裁
- 变换
- 特效
- 图像
- 文字和字体
- 命中测试,移动物体
- 俄罗斯方块
- Cario 图形教程
- Cario 图形库
- Cario 定义
- Cairo 后端
- Cairo 基本图形
- 形状和填充
- 渐变
- 透明度
- 合成
- 剪裁和遮罩
- 变换
- Cairo 文字
- Cairo 中的图像
- 根窗口
- PyCairo 教程
- PyCairo 简介
- PyCairo 后端
- PyCairo 中的基本绘图
- PyCairo 形状和填充
- PyCairo 渐变
- PyCairo 剪裁&遮罩
- PyCairo 的透明度
- PyCairo 中的变换
- PyCairo 中的文字
- PyCairo 中的图像
- 根窗口
- HTML5 画布教程
- 介绍
- HTML5 画布中的直线
- HTML5 画布形状
- HTML5 画布填充
- HTML5 画布中的透明度
- HTML5 画布合成
- HTML5 canvas 中的变换
- HTML5 画布中的文字
- HTML5 画布中的动画
- HTML5 画布中的 Snake
- ZetCode GUI 教程
- Windows API 教程
- Windows API 简介
- Windows API main函数
- Windows API 中的系统函数
- Windows API 中的字符串
- Windows API 中的日期和时间
- Windows API 中的一个窗口
- UI 的第一步
- Windows API 菜单
- Windows API 对话框
- Windows API 控件 I
- Windows API 控件 II
- Windows API 控件 III
- Windows API 中的高级控件
- Windows API 中的自定义控件
- Windows API 中的 GDI
- PyQt4 教程
- PyQt4 简介
- PyQt4 中的第一个程序
- PyQt4 中的菜单和工具栏
- PyQt4 中的布局管理
- PyQt4 中的事件和信号
- PyQt4 中的对话框
- PyQt4 小部件
- PyQt4 小部件 II
- PyQt4 中的拖放
- PyQt4 中的绘图
- PyQt4 中的自定义小部件
- PyQt4 中的俄罗斯方块游戏
- PyQt5 教程
- PyQt5 简介
- PyQt5 日期和时间
- PyQt5 中的第一个程序
- PyQt5 中的菜单和工具栏
- PyQt5 中的布局管理
- PyQt5 中的事件和信号
- PyQt5 中的对话框
- PyQt5 小部件
- PyQt5 小部件 II
- PyQt5 拖放
- PyQt5 中的绘图
- PyQt5 中的自定义小部件
- PyQt5 中的俄罗斯方块
- Qt4 教程
- Qt4 工具包简介
- Qt4 工具类
- Qt4 中的字符串
- Qt4 中的日期和时间
- 在 Qt4 中使用文件和目录
- Qt4 中的第一个程序
- Qt4 中的菜单和工具栏
- Qt4 中的布局管理
- Qt4 中的事件和信号
- Qt4 小部件
- Qt4 小部件 II
- Qt4 中的绘图
- Qt4 中的自定义小部件
- Qt4 中的打砖块游戏
- Qt5 教程
- Qt5 工具包简介
- Qt5 中的字符串
- Qt5 中的日期和时间
- Qt5 中的容器
- 在 Qt5 中处理文件和目录
- Qt5 中的第一个程序
- Qt5 中的菜单和工具栏
- Qt5 中的布局管理
- Qt5 中的事件和信号
- Qt5 小部件
- Qt5 小部件 II
- Qt5 中的绘图
- Qt5 中的自定义小部件
- Qt5 中的贪食蛇
- Qt5 中的打砖块游戏
- PySide 教程
- PySide 工具包简介
- PySide 中的第一个程序
- PySide 中的菜单和工具栏
- PySide 中的布局管理
- PySide 中的事件和信号
- PySide 中的对话框
- PySide 小部件
- PySide 小部件 II
- 在 PySide 中拖放
- 在 PySide 中绘图
- PySide 中的自定义小部件
- PySide 中的俄罗斯方块游戏
- Tkinter 教程
- Tkinter 简介
- Tkinter 中的布局管理
- Tkinter 标准小部件属性
- Tkinter 小部件
- Tkinter 中的菜单和工具栏
- Tkinter 中的对话框
- Tkinter 中的绘图
- Tkinter 中的贪食蛇
- Tcl/Tk 教程
- Tcl/Tk 简介
- Tcl/Tk 中的布局管理
- Tcl/Tk 小部件
- Tcl/Tk 中的菜单和工具栏
- Tcl/Tk 中的对话框
- Tcl/Tk 绘图
- 贪食蛇
- Qt 快速教程
- Java Swing 教程
- Java Swing 简介
- Java Swing 首个程序
- Java Swing 中的菜单和工具栏
- Swing 布局管理
- GroupLayout管理器
- Java Swing 事件
- 基本的 Swing 组件
- 基本的 Swing 组件 II
- Java Swing 对话框
- Java Swing 模型架构
- Swing 中的拖放
- Swing 中的绘图
- Java Swing 中的可调整大小的组件
- Java Swing 中的益智游戏
- 俄罗斯方块
- JavaFX 教程
- JavaFX 简介
- JavaFX 首个程序
- JavaFX 布局窗格
- 基本的 JavaFX 控件
- 基本 JavaFX 控件 II
- JavaFX 事件
- JavaFX 效果
- JavaFX 动画
- JavaFX 画布
- JavaFX 图表
- Java SWT 教程
- Java SWT 简介
- Java SWT 中的布局管理
- Java SWT 中的菜单和工具栏
- Java SWT 中的小部件
- Table小部件
- Java SWT 中的对话框
- Java SWT 绘图
- Java SWT 中的贪食蛇
- wxWidgets 教程
- wxWidgets 简介
- wxWidgets 助手类
- wxWidgets 中的第一个程序
- wxWidgets 中的菜单和工具栏
- wxWidgets 中的布局管理
- wxWidgets 中的事件
- wxWidgets 中的对话框
- wxWidgets 小部件
- wxWidgets 小部件 II
- wxWidgets 中的拖放
- wxWidgets 中的设备上下文
- wxWidgets 中的自定义小部件
- wxWidgets 中的俄罗斯方块游戏
- wxPython 教程
- wxPython 简介
- 第一步
- 菜单和工具栏
- wxPython 中的布局管理
- wxPython 中的事件
- wxPython 对话框
- 小部件
- wxPython 中的高级小部件
- wxPython 中的拖放
- wxPython 图形
- 创建自定义小部件
- wxPython 中的应用框架
- wxPython 中的俄罗斯方块游戏
- C# Winforms Mono 教程
- Mono Winforms 简介
- Mono Winforms 中的第一步
- Mono Winforms 中的布局管理
- Mono Winforms 中的菜单和工具栏
- Mono Winforms 中的基本控件
- Mono Winforms 中的高级控件
- 对话框
- Mono Winforms 中的拖放
- Mono Winforms 中的绘图
- Mono Winforms 中的贪食蛇
- Java Gnome 教程
- Java Gnome 简介
- Java Gnome 的第一步
- Java Gnome 中的布局管理
- Java Gnome 中的布局管理 II
- Java Gnome 中的菜单
- Java Gnome 中的工具栏
- Java Gnome 中的事件
- Java Gnome 中的小部件
- Java Gnome 中的小部件 II
- Java Gnome 中的高级小部件
- Java Gnome 中的对话框
- Java Gnome 中的 Pango
- 在 Java Gnome 中用 Cairo 绘图
- Cario 绘图 II
- Java Gnome 中的贪食蛇
- QtJambi 教程
- QtJambi 简介
- QtJambi 中的布局管理
- QtJambi 中的小部件
- QtJambi 中的菜单和工具栏
- QtJambi 对话框
- QtJambi 中的绘图
- QtJambi 中的自定义小部件
- 贪食蛇
- GTK+ 教程
- GTK+ 简介
- GTK+ 中的第一个程序
- GTK+ 中的菜单和工具栏
- GTK+ 布局管理
- GTK+ 事件和信号
- GTK+ 对话框
- GTK+ 小部件
- GTK+ 小部件 II
- GtkTreeView小部件
- GtkTextView小部件
- 自定义 GTK+ 小部件
- Ruby GTK 教程
- Ruby GTK 简介
- Ruby GTK 中的布局管理
- Ruby GTK 中的小部件
- Ruby GTK 中的菜单和工具栏
- Ruby GTK 中的对话框
- Ruby GTK Cario 绘图
- Ruby GTK 中的自定义小部件
- Ruby GTK 中的贪食蛇
- GTK# 教程
- GTK# 简介
- GTK 的第一步
- GTK# 中的布局管理
- GTK 中的菜单
- GTK# 中的工具栏
- GTK# 中的事件
- GTK# 中的小部件
- GTK 中的小部件 II
- GTK# 中的高级小部件
- GTK# 中的对话框
- Pango
- GTK# 中的 Cario 绘图
- GTK# 中的 Cario 绘图 II
- GTK# 中的自定义小部件
- Visual Basic GTK# 教程
- Visual Basic GTK# 简介
- 布局管理
- 小部件
- 菜单和工具栏
- 对话框
- Cario 绘图
- 自定义小部件
- 贪食蛇
- PyGTK 教程
- PyGTK 简介
- PyGTK 的第一步
- PyGTK 中的布局管理
- PyGTK 中的菜单
- PyGTK 中的工具栏
- PyGTK 中的事件和信号
- PyGTK 中的小部件
- PyGTK 中的小部件 II
- PyGTK 中的高级小部件
- PyGTK 中的对话框
- Pango
- Pango II
- PyGTK 中的 Cario 绘图
- Cario 绘图 II
- PyGTK 中的贪食蛇游戏
- PyGTK 中的自定义小部件
- PHP GTK 教程
- PHP GTK 简介
- PHP GTK 中的布局管理
- PHP GTK 中的小部件
- PHP GTK 中的菜单和工具栏
- 对话框
- Cario 绘图
- 自定义小部件
- 贪食蛇
- C# Qyoto 教程
- Qyoto 介绍
- 布局管理
- Qyoto 中的小部件
- Qyoto 中的菜单和工具栏
- Qyoto 对话框
- Qyoto 中的绘图
- Qyoto 中的绘图 II
- Qyoto 中的自定义小部件
- 贪食蛇
- Ruby Qt 教程
- Ruby Qt 简介
- Ruby Qt 中的布局管理
- Ruby Qt 中的小部件
- 菜单和工具栏
- Ruby Qt 中的对话框
- 用 Ruby Qt 绘图
- Ruby Qt 中的自定义小部件
- Ruby Qt 中的贪食蛇
- Visual Basic Qyoto 教程
- Qyoto 介绍
- 布局管理
- Qyoto 中的小部件
- Qyoto 中的菜单和工具栏
- Qyoto 对话框
- Qyoto 中的绘图
- Qyoto 中的自定义小部件
- 贪食蛇
- Mono IronPython Winforms 教程
- 介绍
- IronPython Mono Winforms 中的第一步
- 布局管理
- 菜单和工具栏
- Mono Winforms 中的基本控件
- Mono Winforms 中的基本控件 II
- Mono Winforms 中的高级控件
- 对话框
- Mono Winforms 中的拖放
- 绘图
- IronPython Mono Winforms 中的绘图 II
- IronPython Mono Winforms 中的贪食蛇
- IronPython Mono Winforms 中的俄罗斯方块游戏
- FreeBASIC GTK 教程
- Jython Swing 教程
- Jython Swing 简介
- Jython Swing 中的布局管理
- Jython Swing 中的组件
- Jython Swing 中的菜单和工具栏
- Jython Swing 中的对话框
- Jython Swing 中的绘图
- Jython Swing 中的半字节
- JRuby Swing 教程
- JRuby Swing 简介
- JRuby Swing 中的布局管理
- JRuby Swing 中的组件
- 菜单和工具栏
- JRuby Swing 中的对话框
- 在 JRuby Swing 中绘图
- JRuby Swing 中的贪食蛇
- Visual Basic Winforms 教程
- Visual Basic Winforms 简介
- 布局管理
- 基本控制
- 进阶控件
- 菜单和工具栏
- 对话框
- 绘图
- 拖放
- 贪食蛇
- JavaScript GTK 教程
- JavaScript GTK 简介
- 布局管理
- JavaScript GTK 中的小部件
- JavaScript GTK 中的菜单和工具栏
- JavaScript GTK 中的对话框
- JavaScript GTK 中的 Cario 绘图
- ZetCode Java 教程
- Java 教程
- Java 语言
- Java 语法结构
- Java 基础
- Java 数据类型
- Java 数据类型 II
- Java 字符串
- Java 数组
- Java 表达式
- Java 控制流程
- Java 面向对象的编程
- Java 方法
- Java 面向对象编程 II
- Java 包
- Java 中的异常
- Java 集合
- Java 流
- Java Future 教程
- Java Comparable和Comparator
- Java DOM 教程
- Java MVC 教程
- Java SAX 教程
- Java JAXB 教程
- Java JSON 处理教程
- Java H2 教程
- MongoDB Java 教程
- Java 正则表达式教程
- Java PDFBox 教程
- Java 文件教程
- Java Files.list教程
- Java Files.walk教程
- Java DirectoryStream教程
- Java 外部与内部迭代器
- Java 文件大小
- 用 Java 创建目录
- 用 Java 创建文件
- Java Log4j 教程
- Gson 教程
- Java RequestDispatcher
- Java HTTP GET/POST 请求
- Java InputStream教程
- Java FileOutputStream教程
- Java FileInputStream教程
- Java ZipInputStream教程
- Java FileWriter教程
- EJB 简介
- Java forEach教程
- Jetty 教程
- Tomcat Derby 教程
- Stripes 介绍
- 使用 Stripes 的 Java webapp,MyBatis,& Derby
- EclipseLink 简介
- Java 中的数据源
- JSTL 中的 SQL 查询标记
- Java 验证过滤器
- Hibernate 验证器
- 用 Java 显示图像
- Play 框架简介
- Spark Java 简介
- Java ResourceBundle教程
- Jtwig 教程
- Java Servlet 教程
- Java 套接字教程
- FreeMarker 教程
- Android 教程
- Java EE 5 教程
- JSoup 教程
- JFreeChart 教程
- ImageIcon教程
- 用 Java 复制文件
- Java 文件时间教程
- 如何使用 Java 获取当前日期时间
- Java 列出目录内容
- Java 附加到文件
- Java ArrayList教程
- 用 Java 读写 ICO 图像
- Java int到String的转换
- Java HashSet教程
- Java HashMap教程
- Java static关键字
- Java 中的HashMap迭代
- 用 Java 过滤列表
- 在 Java 中读取网页
- Java 控制台应用
- Java 集合的便利工厂方法
- Google Guava 简介
- OpenCSV 教程
- 用 Java8 的StringJoiner连接字符串
- Java 中元素迭代的历史
- Java 谓词
- Java StringBuilder
- Java 分割字串教学
- Java NumberFormat
- Java TemporalAdjusters教程
- Apache FileUtils教程
- Java Stream 过滤器
- Java 流归约
- Java 流映射
- Java InputStreamReader教程
- 在 Java 中读取文本文件
- Java Unix 时间
- Java LocalTime
- Java 斐波那契
- Java ProcessBuilder教程
- Java 11 的新功能
- ZetCode JavaScript 教程
- Ramda 教程
- Lodash 教程
- Collect.js 教程
- Node.js 简介
- Node HTTP 教程
- Node-config 教程
- Dotenv 教程
- Joi 教程
- Liquid.js 教程
- faker.js 教程
- Handsontable 教程
- PouchDB 教程
- Cheerio 教程
- Axios 教程
- Jest 教程
- JavaScript 正则表达式
- 用 JavaScript 创建对象
- Big.js 教程
- Moment.js 教程
- Day.js 教程
- JavaScript Mustache 教程
- Knex.js 教程
- MongoDB JavaScript 教程
- Sequelize 教程
- Bookshelf.js 教程
- Node Postgres 教程
- Node Sass 教程
- Document.querySelector教程
- Document.all教程
- JSON 服务器教程
- JavaScript 贪食蛇教程
- JavaScript 构建器模式教程
- JavaScript 数组
- XMLHttpRequest教程
- 从 JavaScript 中的 URL 读取 JSON
- 在 JavaScript 中循环遍历 JSON 数组
- jQuery 教程
- Google 图表教程
- ZetCode Kotlin 教程
- Kotlin Hello World 教程
- Kotlin 变量
- Kotlin 的运算符
- Kotlin when表达式
- Kotlin 数组
- Kotlin 范围
- Kotlin Snake
- Kotlin Swing 教程
- Kotlin 字符串
- Kotlin 列表
- Kotlin 映射
- Kotlin 集合
- Kotlin 控制流程
- Kotlin 写入文件
- Kotlin 读取文件教程
- Kotlin 正则表达式
- ZetCode 其它教程
- TCL 教程
- Tcl
- Tcl 语法结构
- Tcl 中的基本命令
- Tcl 中的表达式
- Tcl 中的控制流
- Tcl 中的字符串
- Tcl 列表
- Tcl 中的数组
- Tcl 中的过程
- 输入&输出
- AWK 教程
- Vaadin 教程
- Vaadin 框架介绍
- Vaadin Grid教程
- Vaadin TextArea教程
- Vaadin ComboBox教程
- Vaadin Slider教程
- Vaadin CheckBox教程
- Vaadin Button教程
- Vaadin DateField教程
- Vaadin Link教程
- ZetCode PHP 教程
- PHP 教程
- PHP
- PHP 语法结构
- PHP 基础
- PHP 数据类型
- PHP 字符串
- PHP 运算符
- PHP 中的控制流
- PHP 数组
- PHP 数组函数
- PHP 中的函数
- PHP 正则表达式
- PHP 中的面向对象编程
- PHP 中的面向对象编程 II
- PHP Carbon 教程
- PHP Monolog 教程
- PHP 配置教程
- PHP Faker 教程
- Twig 教程
- Valitron 教程
- Doctrine DBAL QueryBuilder 教程
- PHP Respect 验证教程
- PHP Rakit 验证教程
- PHP PDO 教程
- CakePHP 数据库教程
- PHP SQLite3 教程
- PHP 文件系统函数
- ZetCode Python 教程
- Python 教程
- Python 语言
- 交互式 Python
- Python 语法结构
- Python 数据类型
- Python 字符串
- Python 列表
- Python 字典
- Python 运算符
- Python 关键字
- Python 函数
- Python 中的文件
- Python 中的面向对象编程
- Python 模块
- Python 中的包
- Python 异常
- Python 迭代器和生成器
- Python 内省
- Python Faker 教程
- Python f 字符串教程
- Python bcrypt 教程
- Python 套接字教程
- Python smtplib教程
- OpenPyXL 教程
- Python pathlib教程
- Python YAML 教程
- Python 哈希教程
- Python ConfigParser教程
- Python 日志教程
- Python argparse 教程
- Python SQLite 教程
- Python Cerberus 教程
- Python PostgreSQL 教程
- PyMongo 教程
- PyMySQL 教程
- Peewee 教程
- pyDAL 教程
- pytest 教程
- Bottle 教程
- Python Jinja 教程
- PrettyTable 教程
- BeautifulSoup 教程
- pyquery 教程
- Python for循环
- Python 反转
- Python Lambda 函数
- Python 集合
- Python 映射
- Python CSV 教程-读写 CSV
- Python 正则表达式
- Python SimpleJson 教程
- SymPy 教程
- Pandas 教程
- Matplotlib 教程
- Pillow 教程
- Python FTP 教程
- Python Requests 教程
- Python Arrow 教程
- Python 列表推导式
- Python 魔术方法
- PyQt 中的QPropertyAnimation
- PyQt 中的QNetworkAccessManager
- ZetCode Ruby 教程
- Ruby 教程
- Ruby
- Ruby 语法结构
- Ruby 基础
- Ruby 变量
- Ruby 中的对象
- Ruby 数据类型
- Ruby 字符串
- Ruby 表达式
- Ruby 控制流
- Ruby 数组
- Ruby 哈希
- Ruby 中的面向对象编程
- Ruby 中的面向对象编程 II
- Ruby 正则表达式
- Ruby 输入&输出
- Ruby HTTPClient教程
- Ruby Faraday 教程
- Ruby Net::HTTP教程
- ZetCode Servlet 教程
- 从 Java Servlet 提供纯文本
- Java Servlet JSON 教程
- Java Servlet HTTP 标头
- Java Servlet 复选框教程
- Java servlet 发送图像教程
- Java Servlet JQuery 列表教程
- Servlet FreeMarker JdbcTemplate 教程-CRUD 操作
- jQuery 自动补全教程
- Java servlet PDF 教程
- servlet 从 WAR 内读取 CSV 文件
- Java HttpServletMapping
- EasyUI datagrid
- Java Servlet RESTFul 客户端
- Java Servlet Log4j 教程
- Java Servlet 图表教程
- Java ServletConfig教程
- Java Servlet 读取网页
- 嵌入式 Tomcat
- Java Servlet 分页
- Java Servlet Weld 教程
- Java Servlet 上传文件
- Java Servlet 提供 XML
- Java Servlet 教程
- JSTL forEach标签
- 使用 jsGrid 组件
- ZetCode Spring 教程
- Spring @Bean注解教程
- Spring @Autowired教程
- Spring @GetMapping教程
- Spring @PostMapping教程
- Spring @DeleteMapping教程
- Spring @RequestMapping教程
- Spring @PathVariable教程
- Spring @RequestBody教程
- Spring @RequestHeader教程
- Spring Cookies 教程
- Spring 资源教程
- Spring 重定向教程
- Spring 转发教程
- Spring ModelAndView教程
- Spring MessageSource教程
- Spring AnnotationConfigApplicationContext
- Spring BeanFactoryPostProcessor教程
- Spring BeanFactory教程
- Spring context:property-placeholder教程
- Spring @PropertySource注解教程
- Spring @ComponentScan教程
- Spring @Configuration教程
- Spring C 命名空间教程
- Spring P 命名空间教程
- Spring bean 引用教程
- Spring @Qualifier注解教程
- Spring ClassPathResource教程
- Spring 原型作用域 bean
- Spring Inject List XML 教程
- Spring 概要文件 XML 教程
- Spring BeanDefinitionBuilder教程
- Spring 单例作用域 bean
- 独立的 Spring 应用
- 经典 Spring 应用中的JdbcTemplate
- Spring EmbeddedDatabaseBuilder教程
- Spring HikariCP 教程
- Spring Web 应用简介
- Spring BeanPropertyRowMapper教程
- Spring DefaultServlet教程
- Spring WebSocket 教程
- Spring WebJars 教程
- Spring @MatrixVariable教程
- Spring Jetty 教程
- Spring 自定义 404 错误页面教程
- Spring WebApplicationInitializer教程
- Spring BindingResult教程
- Spring FreeMarker 教程
- Spring Thymeleaf 教程
- Spring ResourceHandlerRegistry教程
- SpringRunner 教程
- Spring MockMvc 教程
- ZetCode Spring Boot 教程
- Spring Boot 发送电子邮件教程
- Spring Boot WebFlux 教程
- Spring Boot ViewControllerRegistry教程
- Spring Boot CommandLineRunner教程
- Spring Boot ApplicationReadyEvent 教程
- Spring Boot CORS 教程
- Spring Boot @Order教程
- Spring Boot @Lazy教程
- Spring Boot Flash 属性
- Spring Boot CrudRepository 教程
- Spring Boot JpaRepository 教程
- Spring Boot findById 教程
- Spring Boot Data JPA @NamedQuery教程
- Spring Boot Data JPA @Query教程
- Spring Boot Querydsl 教程
- Spring Boot Data JPA 排序教程
- Spring Boot @DataJpaTest教程
- Spring Boot TestEntityManager 教程
- Spring Boot Data JPA 派生的查询
- Spring Boot Data JPA 查询示例
- Spring Boot Jersey 教程
- Spring Boot CSV 教程
- SpringBootServletInitializer教程
- 在 Spring Boot 中加载资源
- Spring Boot H2 REST 教程
- Spring Boot RestTemplate
- Spring Boot REST XML 教程
- Spring Boot Moustache 教程
- Spring Boot Thymeleaf 配置
- Spring Boot 自动控制器
- Spring Boot FreeMarker 教程
- Spring Boot Environment
- Spring Boot Swing 集成教程
- 在 Spring Boot 中提供图像文件
- 在 Spring Boot 中创建 PDF 报告
- Spring Boot 基本注解
- Spring Boot @ResponseBody教程
- Spring Boot @PathVariable教程
- Spring Boot REST Data JPA 教程
- Spring Boot @RequestParam教程
- Spring Boot 列出 bean
- Spring Boot @Bean
- Spring Boot @Qualifier教程
- 在 Spring Boot 中提供静态内容
- Spring Boot Whitelabel 错误
- Spring Boot DataSourceBuilder 教程
- Spring Boot H2 教程
- Spring Boot Web JasperReports 集成
- Spring Boot iText 教程
- Spring Boot cmd JasperReports 集成
- Spring Boot RESTFul 应用
- Spring Boot 第一个 Web 应用
- Spring Boot Groovy CLI
- Spring Boot 上传文件
- Spring Boot @ExceptionHandler
- Spring Boot @ResponseStatus
- Spring Boot ResponseEntity
- Spring Boot @Controller
- Spring Boot @RestController
- Spring Boot @PostConstruct
- Spring Boot @Component
- Spring Boot @ConfigurationProperties教程
- Spring Boot @Repository
- Spring Boot MongoDB 教程
- Spring Boot MongoDB Reactor 教程
- Spring Boot PostgreSQL 教程
- Spring Boot @ModelAttribute
- Spring Boot 提交表单教程
- Spring Boot Model
- Spring Boot MySQL 教程
- Spring Boot GenericApplicationContext
- SpringApplicationBuilder教程
- Spring Boot Undertow 教程
- Spring Boot 登录页面教程
- Spring Boot RouterFunction 教程
- ZetCode Symfony 教程
- Symfony DBAL 教程
- Symfony 表单教程
- Symfony CSRF 教程
- Symfony Vue 教程
- Symfony 简介
- Symfony 请求教程
- Symfony HttpClient教程
- Symfony Flash 消息
- 在 Symfony 中发送邮件
- Symfony 保留表单值
- Symfony @Route注解教程
- Symfony 创建路由
- Symfony 控制台命令教程
- Symfony 上传文件
- Symfony 服务教程
- Symfony 验证教程
- Symfony 翻译教程