列表框是将一批文本字符串显示在一个具有滚动功能的方框中的控件。通过发送消息到列表框的窗口过程,程序可以添加或删除列表中的字符串。当列表框中的一个项目被选中时,列表框控件便发送 WM_COMMAND消息到其父窗口。然后父窗口确定哪个项目被选中。
本节必须掌握的知识点:
列表框类控件
第54练:列表框控件
第55练:列表框控件—head程序
8.5.1 列表框类控件
■列表框控件分类
列表框可以是单选的或多选的。后者允许用户从列表框中选择一个以上的项目。当一个列表框获得输入焦点时,它会在列表框的一个项目周围显示虚线框。这种光标并不表示列表框中的选中项。被选中的项目通过高亮显示来表示。
●在单选列表框中,用户可以按动空格键选择光标所在的项目。方向键可以同时移动光 标和当前的选择,并可以滚动列表框中的内容。上下翻页键也可通过移动光标来滚动列表框,但不会移动选择的项目。按下一个字母键可移动光标和当前选择到以那个字母开头的第一项(或下一项)。通过在某一项上单击或双击鼠标也可以选中该项。
●在多选列表框中,空格键用于切换光标所在项目的选择状态。(如果该项目已经被选定, 它的选定状态会被取消。)方向键取消所有以前选定的项目,并移动光标和选中项,就像在单选列表框中一样。然而,Ctrl键加方向键可以移动光标,但不移动选中项。Shift键加方向键可以扩展选中项。
单击或双击多选列表框中的一个项目,会取消所有先前选定的项目,只选择被单击的项目。然而,在单击一个项目的同时按下Shift键会切换该项目的选择状态而不改变任何其他项目的选择状态。
■列表框控件的重要信息和功能
●创建列表框控件:可以使用 CreateWindowEx 或 CreateWindow 函数来创建列表框控件。需要指定控件类名为 "LISTBOX",并设置相应的样式和属性。
●添加项:使用 LB_ADDSTRING 消息或 ListBox_AddString 函数向列表框中添加文本项。每个项都具有唯一的索引,可以使用该索引进行引用和操作。
●删除项:使用 LB_DELETESTRING 消息或 ListBox_DeleteString 函数从列表框中删除指定索引的项。
●获取选中项:使用 LB_GETCURSEL 消息或 ListBox_GetCurSel 函数获取当前选中项的索引。可以进一步使用 LB_GETTEXT 消息或 ListBox_GetText 函数获取选中项的文本内容。
●选择项:使用 LB_SETCURSEL 消息或 ListBox_SetCurSel 函数设置列表框中指定索引的项为选中状态。
●多选功能:通过设置列表框控件的样式为 LBS_MULTIPLESEL,可以启用多选功能。用户可以按住 Ctrl 键或 Shift 键进行多项选择。
●滚动条:当列表框中的项超过可见区域时,会自动出现垂直滚动条,以方便导航。
●通知消息:列表框控件可以发送一系列的通知消息,用于响应用户的操作或状态变化。例如,LBN_SELCHANGE 消息在选中项发生变化时发送。
●自定义绘制:可以通过处理 WM_DRAWITEM 消息来自定义绘制列表框中的项,以改变其外观和样式。
●其他操作:还有许多其他的列表框操作和功能,如获取项的数量、清空列表框、滚动列表框等。
●键盘操作
键盘操作 | 单选列表框 | 多选列表框 |
空格键 | 选择光标所在项 | 切换选中状态 |
方向键 | 移动光标和当前的选择 | 取消以前所选,并移动光标和选中项 |
翻页键 | 移动光标,但不移动选择项 | |
字母键 | 移动和选择以该字母开头的第一项 | |
Ctrl+方向 | 移动光标,但不移动选中项 | |
Shift+方向 | 扩展选中项 |
■列表框的样式
列表框控件(List Box Control)提供了一些样式选项,可以用于自定义列表框的外观和行为。以下是一些常用的列表框样式:
●LBS_STANDARD:标准列表框样式,默认样式,显示垂直滚动条并支持单选。
●LBS_SORT:排序样式,列表框中的项按字母顺序排序。
●LBS_MULTIPLESEL:多选样式,允许用户选择列表框中的多个项。用户可以按住 Ctrl 键或 Shift 键进行多项选择。
●LBS_EXTENDEDSEL:扩展多选样式,类似于 LBS_MULTIPLESEL,但支持连续选择。用户可以按住鼠标左键拖动以选择多个连续项。
●LBS_OWNERDRAWFIXED:固定尺寸的自绘样式,允许自定义绘制列表框中的项。
●LBS_NOINTEGRALHEIGHT:禁用自动调整高度样式,列表框的高度将根据项的数量进行调整。
●LBS_DISABLENOSCROLL:禁用无项时的滚动条样式,即使列表框中没有项,也显示垂直滚动条。
●LBS_NODATA:无数据样式,仅用于通知列表框控件不包含任何数据。
●这些样式可以通过在创建列表框控件时使用 CreateWindowEx 或 CreateWindow 函数的 dwStyle 参数来设置。例如:
HWND hListBox = CreateWindowEx(
0, // 扩展窗口样式
L"LISTBOX", // 窗口类名
NULL, // 窗口标题
WS_VISIBLE | WS_CHILD | LBS_STANDARD, // 窗口样式
x, y, width, height, // 窗口位置和尺寸
hWndParent, // 父窗口句柄
(HMENU)IDC_LISTBOX, // 控件标识符
hInstance, // 应用程序实例句柄
NULL // 创建参数
);
在上述示例中,我们创建了一个具有标准样式的列表框控件,并设置了一些常用的样式,如可见、子窗口。可以根据需求自由组合和设置其他样式。
●除了创建时设置样式,还可以使用 SetWindowLongPtr 或 SetWindowLongPtr 函数来动态修改列表框控件的样式。例如:
DWORD dwStyle = GetWindowLongPtr(hListBox, GWL_STYLE);
dwStyle |= LBS_SORT; // 设置排序样式
SetWindowLongPtr(hListBox, GWL_STYLE, dwStyle);
在上述示例中,我们首先获取列表框控件的当前样式,然后通过按位或运算符将 LBS_SORT 样式添加到样式中,最后使用 SetWindowLongPtr 函数设置新的样式。
这些是一些常见的列表框样式,你可以根据具体的需求选择适合的样式来自定义列表框的外观和行为。
■向列表框中添加字符串
要向列表框中添加字符串,你可以使用 LB_ADDSTRING 消息或 ListBox_AddString 函数。下面是一个示例:
●使用 LB_ADDSTRING 消息:
HWND hListBox = GetDlgItem(hWnd, IDC_LISTBOX); // 获取列表框控件句柄
SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)L"Item 1"); // 添加字符串项
SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)L"Item 2"); // 添加字符串项
●使用 ListBox_AddString 函数:
HWND hListBox = GetDlgItem(hWnd, IDC_LISTBOX); // 获取列表框控件句柄
ListBox_AddString(hListBox, L"Item 1"); // 添加字符串项
ListBox_AddString(hListBox, L"Item 2"); // 添加字符串项
在上述示例中,我们首先获取列表框控件的句柄 hListBox,然后使用 LB_ADDSTRING 消息或 ListBox_AddString 函数将字符串项添加到列表框中。每个字符串项都是一个以 null 终止的 Unicode 字符串,使用 LPARAM 类型进行传递。
你可以根据需要多次调用 LB_ADDSTRING 消息或 ListBox_AddString 函数来添加多个字符串项。列表框将按照添加的顺序显示这些项。
请确保在添加字符串之前,列表框已经被创建并且有效。你可以使用 GetDlgItem 函数根据控件标识符获取列表框的句柄。
注意:在使用 LB_ADDSTRING 消息或 ListBox_AddString 函数添加字符串项之前,你需要确保列表框控件已经创建,且在窗口中可见。
■项目的选择和提取
要选择列表框中的项目并提取选中的项目,你可以使用 LB_GETCURSEL 消息或 ListBox_GetCurSel 函数来获取当前选中项目的索引。然后,使用 LB_GETTEXT 消息或 ListBox_GetText 函数来获取选中项目的文本内容。
下面是一个示例:
●使用 LB_GETCURSEL 消息和 LB_GETTEXT 消息:
HWND hListBox = GetDlgItem(hWnd, IDC_LISTBOX); // 获取列表框控件句柄
// 获取当前选中项目的索引
int selectedIndex = SendMessage(hListBox, LB_GETCURSEL, 0, 0);
if (selectedIndex != LB_ERR) {
// 获取选中项目的文本长度
int textLength = SendMessage(hListBox, LB_GETTEXTLEN, selectedIndex, 0);
if (textLength != LB_ERR) {
// 分配内存来存储选中项目的文本
wchar_t* buffer = new wchar_t[textLength + 1];
// 获取选中项目的文本内容
SendMessage(hListBox, LB_GETTEXT, selectedIndex, (LPARAM)buffer);
// 在这里可以使用选中项目的文本内容
delete[] buffer; // 释放内存
}
}
●使用 ListBox_GetCurSel 函数和 ListBox_GetText 函数:
HWND hListBox = GetDlgItem(hWnd, IDC_LISTBOX); // 获取列表框控件句柄
// 获取当前选中项目的索引
int selectedIndex = ListBox_GetCurSel(hListBox);
if (selectedIndex != LB_ERR) {
// 获取选中项目的文本长度
int textLength = ListBox_GetTextLen(hListBox, selectedIndex);
if (textLength != LB_ERR) {
// 分配内存来存储选中项目的文本
wchar_t* buffer = new wchar_t[textLength + 1];
// 获取选中项目的文本内容
ListBox_GetText(hListBox, selectedIndex, buffer);
// 在这里可以使用选中项目的文本内容
delete[] buffer; // 释放内存
}
}
在上述示例中,我们首先获取列表框控件的句柄 hListBox。然后,使用 LB_GETCURSEL 消息或 ListBox_GetCurSel 函数获取当前选中项目的索引。如果索引不是 LB_ERR,表示有项目被选中。
接下来,我们使用 LB_GETTEXTLEN 消息或 ListBox_GetTextLen 函数获取选中项目的文本长度。如果长度不是 LB_ERR,表示获取长度成功。
然后,我们分配足够的内存来存储选中项目的文本,使用 LB_GETTEXT 消息或 ListBox_GetText 函数获取选中项目的文本内容,并将其存储在我们分配的缓冲区中。
在获取到选中项目的文本内容后,你可以在相应的位置使用该文本进行处理。最后,不要忘记释放分配的内存,以避免内存泄漏。
■接收来自列表框的消息
要接收来自列表框的消息,你需要在消息处理函数或窗口过程中处理列表框相关的消息。
当用户用鼠标单击一个列表框时,这个列表框将获得输入焦点。父窗口可以通过下面的调用把输入焦点给一个列表框控件:
SetFocus ( hwndList );
当一个列表框具有输入焦点时,光标移动键、字符键和空格键也可用于从列表框中选择项目。
—个列表框控件会发送WM COMMAND信息到其父窗口。相应的wParam和IParam 变量的含义和在按钮控件、编辑控件中是一样的:
LOWORD (wParam) 子窗口 ID
HIWORD (wParam) 通知码
IParam 子窗口句柄
通知码和它们的值如下:
LBN_ERRSPACE -2 //表明该列表框控件己经用尽了它的空间
LBN_SELCHANGE 1 //表明当前的选择发生了变化
LBN_DBLCLK 2 //代码表明列表框中的某个项目己被鼠标双击
LBN_SELCANCEL 3 //通知应用程序用户已取消列表框中的选择
LBN_SETFOCUS 4 //通知应用程序列表框已收到键盘焦点
LBN_KILLFOCUS 5 //通知应用程序列表框已失去键盘焦点
只有在列表框窗口样式包含了 LBS_NOTIFY时,列表框控件才会向父窗口发送 LBN_SELCHANGE 和 LBN_DBLCLK。
LBN_ERRSPACE代码表明,该列表框控件己经用尽了它的空间。
LBN_SELCHANGE 代码表明当前的选择发生了变化:这些消息可能出现在当用户在列表框中移动高亮显示的选中项时,或是通过空格键切换选择状态时,又或者是用鼠标单击一个项目时。
LBN_DBLCLK代码表明列表框中的某个项目己被鼠标双击。(通知码LBN_SELCHANGE 和LBN_DBLCLK的值表明了鼠标单击的次数。)
根据你的应用程序,你可能想要使用LBN_SELCHANGE或LBN_DBLCLK,或同时使用这两者。程序将收到许多LBN_SELCHANGE消息,但只有当用户用鼠标双击时 LBN_DBLCLK才会出现。如果程序使用了双击,则需要提供一个键盘接口,以提供 LBN_DBLCLK的等效功能。
以下是一些常见的列表框消息及其处理方式:
●WM_COMMAND:列表框的选择变化会触发 WM_COMMAND 消息,其中的 wParam 参数指示了列表框的控件标识符,而 lParam 参数指示了列表框控件的句柄。可以使用 HIWORD(wParam) 来检查列表框事件类型。
case WM_COMMAND:
{
int controlId = LOWORD(wParam); // 获取控件标识符
if (controlId == IDC_LISTBOX) // 检查列表框控件标识符
{
int eventType = HIWORD(wParam); // 获取事件类型
if (eventType == LBN_SELCHANGE) // 选择变化事件
{
// 处理列表框选择变化的逻辑
// 可以使用 SendMessage 或 ListBox_GetCurSel 等函数
获取选中项的索引或文本
}
}
}
break;
●WM_NOTIFY:通过设置列表框的扩展样式为 LBS_NOTIFY,可以接收到更详细的通知消息。当列表框中的事件发生时,将会发送 WM_NOTIFY 消息,其中的 NMHDR 结构中的 code 成员表示事件类型。
case WM_NOTIFY:
{
NMHDR* nmhdr = (NMHDR*)lParam;
if (nmhdr->code == LBN_SELCHANGE) // 选择变化事件
{
// 处理列表框选择变化的逻辑
// 可以使用 SendMessage 或 ListBox_GetCurSel 等函数
获取选中项的索引或文本
}
}
break;
在上述示例中,我们检查了列表框相关消息中的事件类型,例如 LBN_SELCHANGE 表示选择变化事件。在事件处理逻辑中,你可以使用适当的函数(如 SendMessage 或 ListBox_GetCurSel)来获取选中项的索引或文本,并进行相应的处理。
需要注意的是,列表框控件是单列的,适用于显示简单的文本项。如果需要更复杂的布局或自定义内容,可以考虑使用组合框控件(Combo Box Control)或其他更高级的控件。
8.5.2 第54练:列表框控件
/*------------------------------------------------------------------
054 WIN32 API 每日一练
第54个例子ENVIRON.C:列表框类控件——显示环境变量
CreateWindow创建列表框控件
FillListBox函数对环境变量内存块进行解析
GetEnvironmentStrings 函数
GetEnvironmentVariable函数
(c) www.bcdaren.com, 2020
----------------------------------------------------------------*/
#include <windows.h>
#define ID_LIST 1
#define ID_TEXT 2
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("Environ");
…(略)
return msg.wParam;
}
//填充列表框
void FillListBox (HWND hwndList) //对内存块进行解析
{
int iLength ;
TCHAR * pVarBlock, * pVarBeg, * pVarEnd, * pVarName ;
//获取指向环境变量的指针
pVarBlock = GetEnvironmentStrings () ;
while (*pVarBlock) //获取成功
{
if (*pVarBlock != '=') // 跳过以'='开头的变量名
{ //变量名的开始位置。注意环境变量的格式varName=value
pVarBeg = pVarBlock ;
while (*pVarBlock++ != '=') ; //扫描到‘=’号
pVarEnd = pVarBlock - 1 ; // pVarCurr指向'='号
iLength = pVarEnd - pVarBeg ; // 变量名的长度
// 为变量名分配内存,复制变量名,结尾添加结束符0
pVarName = calloc (iLength + 1, sizeof (TCHAR)) ;
CopyMemory (pVarName, pVarBeg, iLength * sizeof (TCHAR)) ;
pVarName[iLength] = '\0' ; //添加结尾0
// 将变量名放入列表框并释放内存
SendMessage (hwndList, LB_ADDSTRING, 0, (LPARAM) pVarName) ;
free (pVarName) ; //释放内存
}
// 找字符串的结束符'\0',并把指针移向下一行开始。
while (*pVarBlock++ != '\0') ;
}
//正在使用,不能释放
//FreeEnvironmentStrings (pVarBlock);//释放环境内存块,*pVarBlock=""空字符串
}
LRESULT CALLBACK WndProc ( HWND hwnd, UINT message, WPARAM wParam,LPARAM
lParam)
{
static HWND hwndList, hwndText ;
int iIndex, iLength, cxChar, cyChar ;
TCHAR * pVarName, * pVarValue ;
switch (message)
{
case WM_CREATE :
cxChar = LOWORD (GetDialogBaseUnits ()) ;
cyChar = HIWORD (GetDialogBaseUnits ()) ;
// 创建列表框和静态文本窗口
hwndList = CreateWindow (TEXT ("listbox"), NULL,
//LBS_STANDARD:(LBS_NOTIFY | LBS_SORT | WS_VSCROLL | WS_BORDER )
WS_CHILD | WS_VISIBLE | LBS_STANDARD,
cxChar, cyChar * 3,
//最长字符串宽度+垂直滚动条的宽度
cxChar * 39 + GetSystemMetrics (SM_CXVSCROLL),
cyChar * 15,
hwnd, (HMENU) ID_LIST,
(HINSTANCE) GetWindowLong (hwnd, GWL_HINSTANCE),NULL) ;
hwndText = CreateWindow (TEXT ("static"), NULL,
WS_CHILD | WS_VISIBLE | SS_LEFT,
cxChar, cyChar,
GetSystemMetrics (SM_CXSCREEN), cyChar,//主显示屏的屏幕宽度
hwnd, (HMENU) ID_TEXT,
(HINSTANCE) GetWindowLong (hwnd, GWL_HINSTANCE),NULL) ;
FillListBox (hwndList) ; //对列表框控件内存模块进行解析
return 0 ;
case WM_SETFOCUS :
SetFocus (hwndList) ; //捕获焦点
return 0 ;
case WM_COMMAND :
if (LOWORD (wParam) == ID_LIST && HIWORD (wParam) == LBN_SELCHANGE)
{
//获取选中项的索引值
iIndex = SendMessage (hwndList, LB_GETCURSEL, 0, 0) ;
//获取列表框中字符串的长度
iLength = SendMessage (hwndList, LB_GETTEXTLEN, iIndex, 0) + 1 ;
pVarName = calloc (iLength, sizeof (TCHAR)) ; //分配内存
//获取环境变量的名称
SendMessage (hwndList, LB_GETTEXT, iIndex, (LPARAM) pVarName) ;
//获得变量值的数据长度,含'\0'
iLength = GetEnvironmentVariable (pVarName, NULL, 0) ;
pVarValue = calloc (iLength, sizeof (TCHAR)) ;
//获得变量的值
GetEnvironmentVariable (pVarName, pVarValue, iLength) ;
//将变量的值显示在文本框中
SetWindowText (hwndText, pVarValue) ;
free (pVarName) ;
free (pVarValue) ;
}
return 0 ;
case WM_DESTROY :
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
/******************************************************************************
创建列表框控件
列表框常用样式:
LBS_STANDARD:(LBS_NOTIFY | LBS_SORT | WS_VSCROLL | WS_BORDER )
LBS_NOTIFY:默认的列表框样式不能发送WM_COMMAND到父窗口,须指定该样式才行
LBS_SORT:排序
LBS_MULTIPLESEL:多选
LBS_NOREDRAW:新增项目默认不会自我更新,可在WM_SETREDRAW中设置
******************************************************************************
对环境变量内存块进行解析
FillListBox函数:解析环境块字符串信息
******************************************************************************
GetEnvironmentStrings 函数:检索当前进程的环境变量
pVarBlock = GetEnvironmentStrings () ; // 获取指向环境块的指针
******************************************************************************
GetEnvironmentVariable函数:从调用过程的环境块中检索指定变量的内容
DWORD GetEnvironmentVariable(
LPCTSTR lpName, //环境变量的名称
LPTSTR lpBuffer, //指向缓冲区的指针
DWORD nSize //缓冲区大小
);
*/
运行结果:
图8-7 环境变量列表框
总结
实例ENVIRON.C将环境变量添加到一个列表框控件,并读取环境变量的值,显示在一个静态文本框中。
实例自定义了一个FillListBox函数,对环境变量内存块进行解析。首先调用GetEnvironmentStrings函数获取环境变量指针,然后通过一个while循环结构逐个读取环境变量,并调用SendMessage函数向列表框控件发送LB_ADDSTRING消息,将环境变量添加到列表框。【注意】读取的环境变量名字符串结尾添加0,添加列表框后,及时释放保存环境变量的内存。
窗口过程在处理WM_CREATE消息时,获取对话框字符的宽和高,然后调用CreateWindow函数分别创建一个列表框控件和一个静态文本控件。最后调用FillListBox函数将环境变量填充列表框控件。
处理WM_COMMAND消息:首先调用SendMessage函数向列表框控件分别发送LB_GETCURSEL消息和LB_GETTEXTLEN消息,获取列表框选中项的索引和字符串长度。然后再次调用SendMessage函数向列表框控件分别发送LB_GETTEXT消息,获取选中项字符串。
接着两次调用GetEnvironmentVariable函数,第一次调用根据环境变量名获取环境变量的长度。第二次调用获取环境变量的值。最后调用SetWindowText函数将环境变量值显示在静态文本框中。
8.5.3 第55练:列表框控件—head程序
/*------------------------------------------------------------------
055 WIN32 API 每日一练
第55个例子HEAD.C:列表框控件--head程序
窗口子类ListProc
GetCurrentDirectory 函数
SetWindowText函数
(c) www.bcdaren.com, 2020
----------------------------------------------------------------*/
#include <windows.h>
#define ID_LIST 1
#define ID_TEXT 2
#define MAXREAD 8192
#define DIRATTR (DDL_READWRITE | DDL_READONLY | DDL_HIDDEN | DDL_SYSTEM | \
DDL_DIRECTORY | DDL_ARCHIVE | DDL_DRIVES) //文件属性代码
#define DTFLAGS (DT_WORDBREAK | DT_EXPANDTABS | DT_NOCLIP |DT_NOPREFIX) //格式化文本
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
LRESULT CALLBACK ListProc (HWND, UINT, WPARAM, LPARAM) ; //窗口子类--子窗口过程
WNDPROC OldList ; //Windows默认的子窗口过程
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("head");
…(略)
return msg.wParam;
}
LRESULT CALLBACK WndProc ( HWND hwnd, UINT message, WPARAM wParam,LPARAM
lParam)
{
static BOOL bValidFile ;
static BYTE buffer[MAXREAD] ;
static HWND hwndList, hwndText ;
static RECT rect ; //在客户区指定的地方显示文件的内容
static TCHAR szFile[MAX_PATH + 1] ; //保存选中的文件名
HANDLE hFile ;
HDC hdc ;
int i, cxChar, cyChar ;
PAINTSTRUCT ps ;
TCHAR szBuffer[MAX_PATH + 1] ;
switch (message)
{
case WM_CREATE :
cxChar = LOWORD(GetDialogBaseUnits());//对话框字符宽度
cyChar = HIWORD(GetDialogBaseUnits());//对话框字符高度
rect.left = 20 * cxChar;
rect.top = 3 * cyChar;
//创建子窗口控件--列表框
hwndList = CreateWindow(TEXT("listbox"), NULL,
//列表框-按字母顺序对列表框中的字符串进行排序
WS_CHILDWINDOW | WS_VISIBLE | LBS_STANDARD,
cxChar, cyChar * 3,
cxChar * 15 + GetSystemMetrics(SM_CXVSCROLL),//加上滚动条宽度
cyChar * 15,
hwnd, (HMENU)ID_LIST,
(HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE),
NULL);
GetCurrentDirectory(MAX_PATH + 1, szBuffer);//检索当前进程的当前目录
//创建一个静态文本框控件
hwndText = CreateWindow(TEXT("static"), szBuffer,
//静态控件:一个简单的矩形,将矩形中的文本左对齐
WS_CHILDWINDOW | WS_VISIBLE | SS_LEFT,
cxChar, cyChar, cxChar * MAX_PATH, cyChar,
hwnd, (HMENU)ID_TEXT,
(HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE),
NULL);
//窗口子类,创建一个自定义列表框窗口过程
OldList = (WNDPROC)SetWindowLong(hwndList, GWL_WNDPROC,
(LPARAM)ListProc);
//向列表框控件发送目录消息,
//添加具有预定义的DIRATTR文件属性代码的所有目录和文件
SendMessage(hwndList, LB_DIR, DIRATTR, (LPARAM)TEXT("*.*"));
return 0;
case WM_SIZE :
rect.right = LOWORD(lParam);
rect.bottom = HIWORD(lParam);
return 0;
case WM_SETFOCUS :
SetFocus(hwndList);
return 0;
case WM_COMMAND :
//如果双击列表框子目录
if (LOWORD (wParam) == ID_LIST && HIWORD (wParam) == LBN_DBLCLK)
{
//判断是否选中项目。在单选列表框中,返回值是当前所选项目的从零开
//始的索引。如果没有选择,则返回值为LB_ERR。
//在单选列表框中获取当前所选项目的索引(如果有)
if (LB_ERR == (i = SendMessage(hwndList, LB_GETCURSEL, 0, 0)))
break;
//获取选中项的文本
//从零开始的字符串的索引
SendMessage(hwndList, LB_GETTEXT, i, (LPARAM)szBuffer);
//打开文件,选中项为文件
if (INVALID_HANDLE_VALUE != (hFile = CreateFile(szBuffer,
GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, 0, NULL)))
{
CloseHandle(hFile);//关闭打开的文件句柄
bValidFile = TRUE;
lstrcpy(szFile, szBuffer);//保存选中项的文件名
//将文件及路径显示在文本框中
//检索当前进程的当前目录
GetCurrentDirectory(MAX_PATH + 1, szBuffer);
if (szBuffer[lstrlen(szBuffer) - 1] != '\\')//找到文件
lstrcat(szBuffer, TEXT("\\"));//最未尾加个“\”
//更改指定窗口标题栏的文本
SetWindowText(hwndText, lstrcat(szBuffer, szFile));
}
else //选中项为目录或驱动器
{
//1、上级目录:[..]
//2、子目录如:[SubDir]
//3、驱动器符:[-c-]
bValidFile = FALSE ;
//判断是否目录
szBuffer [lstrlen (szBuffer) - 1] = '\0' ; //加个'\0'
//如果设置的目录不工作,也许它是
//更换驱动器,试试吧。
//SubDir加上当前目录为新路径。失败,说明选中的是驱动器
if (!SetCurrentDirectory (szBuffer + 1))
{
szBuffer[3] = ':';
szBuffer[4] = '\0';
//将当前路径设为驱动器符,如c:
SetCurrentDirectory(szBuffer + 2);
}
//获取新目录名并填充列表框
//检索当前进程的当前目录
GetCurrentDirectory (MAX_PATH + 1, szBuffer) ;
//更改指定窗口标题栏的文本
SetWindowText (hwndText, szBuffer) ;
//发送消息:从列表框中删除所有项目
SendMessage (hwndList, LB_RESETCONTENT, 0, 0) ;
//发送消息:将名称添加到列表框显示的列表中
SendMessage (hwndList, LB_DIR, DIRATTR,
(LPARAM) TEXT ("*.*")) ;
}
InvalidateRect (hwnd, NULL, TRUE) ; //重绘窗口
}
return 0 ;
case WM_PAINT :
if (!bValidFile) //文件打开失败,不做任何事,交给DefWindowProc处理
break ;
//打开文件
if (INVALID_HANDLE_VALUE == (hFile = CreateFile (szFile,
GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL)))
{
bValidFile = FALSE;//打开文件失败
break;
}
ReadFile (hFile, buffer, MAXREAD, &i, NULL) ; //读取文件
CloseHandle (hFile) ; //关闭文件句柄
//显示读取到的内容,i为读取文本到buffer中的字节总数
hdc = BeginPaint (hwnd, &ps) ;
SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;
SetTextColor (hdc, GetSysColor (COLOR_BTNTEXT)) ;
SetBkColor (hdc, GetSysColor (COLOR_BTNFACE)) ;
// 假设文件是ASCII字符,未考虑UNICODE字符
DrawText (hdc, buffer, i, &rect, DTFLAGS) ; //显示文本
EndPaint (hwnd, &ps) ;
return 0 ;
case WM_DESTROY :
PostQuitMessage(0);
return 0;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
//自定义列表框控件窗口过程
//子窗口类回调函数--接收键盘消息
LRESULT CALLBACK ListProc (HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam)
{ //处理按键消息--如果按下回车键
if (message == WM_KEYDOWN && wParam == VK_RETURN)
//向父窗口发送WM_COMMAND鼠标双击消息
SendMessage(GetParent(hwnd), WM_COMMAND,
MAKELONG(ID_LIST, LBN_DBLCLK), (LPARAM)hwnd);
//调用默认列表框控件窗口过程——增加键盘接口
return CallWindowProc(OldList, hwnd, message, wParam, lParam);
}
/******************************************************************************
窗口子类ListProc
子类化窗口若要对窗口的实例进行子类化,
请调用SetWindowLong函数并指定该窗口的句柄以对GWL_WNDPROC标志和该子类过程的指针进行子类化。
SetWindowLong返回一个指向原始窗口过程的指针;
使用此指针将消息传递给原始过程。子类窗口过程必须使用CallWindowProc函数来调用原始窗口过程。
******************************************************************************
GetCurrentDirectory 函数:检索当前进程的当前目录
DWORD GetCurrentDirectory(
DWORD nBufferLength,//当前目录字符串的缓冲区长度
LPTSTR lpBuffer //指向接收当前目录字符串的缓冲区的指针
);
******************************************************************************
SetWindowText函数:更改指定窗口标题栏的文本
BOOL SetWindowTextA(
HWND hWnd, //要更改其文本的窗口或控件的句柄
LPCSTR lpString //新标题或控件文本
);
*/
运行结果:
图8-8 head程序
总结
实例HEAD.C在列表框中列出了所有文件和子目录。在文件名上双击鼠标,或者当文件名被选择时按下回车键,可以显示这个文件。使用这些方法之一还可以更改子目录。该程序可在HEAD窗口的右侧客户区上显示出文件开头的内容,内容的大小不超过8KB。
实例窗口过程首先处理WM_CREATE消息,获取对话框字符宽和高,然后调用CreateWindow函数分别创建一个列表框控件和一个静态文本控件。静态文本控件用于显示目录信息。调用GetCurrentDirectory检索当前进程的当前目录,并向列表框控件发送LB_DIR消息,添加具有预定义的DIRATTR文件属性代码的所有目录和文件。
此外,为了给列表框控件增加一个键盘接口,创建了一个窗口子类,调用SetWindowLong函数为列表框控件添加一个自定义的窗口过程ListProc。ListProc窗口过程非常简单,当检测到列表框控件内按下回车键,给主窗口发送一个WM_COMMAND消息,模拟鼠标双击事件。
WM_COMMAND消息:如果检测到鼠标双击事件,则调用SendMessage函数获取鼠标选项及选中项文本字符串。如果是文件名,则调用CreateFile函数打开文件,并在静态文本框内显示文件路径。如果是磁盘根目录或驱动器,则更新目录名并重新填充列表框。最后调用InvalidateRect函数重绘主窗口,向主窗口发送一个WM_PAINT消息。
WM_PAINT消息:打开文件,并在主窗口右侧显示文件。