首页/新闻资讯/正文详情

Win32高级控件编程:ListView与TreeView实战指南

发布时间:2026/11/2 21:50:28 来源:云帆数科 栏目:资讯中心
Win32高级控件编程:ListView与TreeView实战指南
1. Win32高级控件编程概览在Windows桌面应用开发领域Win32 API作为最底层的编程接口提供了对操作系统功能的完整控制能力。前几期我们已经探讨了按钮、编辑框等基础控件的使用方法今天我们将深入Win32控件体系中那些更强大但也更复杂的高级组件。这些高级控件包括但不限于列表视图(ListView)树形视图(TreeView)标签控件(TabControl)进度条(ProgressBar)状态栏(StatusBar)工具栏(Toolbar)富文本编辑框(RichEdit)与基础控件不同这些高级控件通常需要额外的初始化步骤依赖特定的消息处理机制具有更复杂的样式配置选项支持自定义绘制和外观调整提示在开始使用高级控件前建议先熟悉WM_NOTIFY消息机制这是大多数高级控件与父窗口通信的主要方式。2. 列表视图(ListView)深度解析2.1 创建与初始化ListView列表视图是展示结构化数据的利器Windows资源管理器中的文件列表就是典型应用。创建ListView需要以下步骤HWND hListView CreateWindowEx( 0, // 无扩展样式 WC_LISTVIEW, // 预定义的ListView类名 TEXT(示例列表), // 控件文本 WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_EDITLABELS, // 样式 10, 10, // 位置 300, 200, // 尺寸 hWndParent, // 父窗口句柄 (HMENU)IDC_LISTVIEW, // 控件ID hInstance, // 程序实例句柄 NULL); // 无额外数据初始化列信息LVCOLUMN lvc; lvc.mask LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM; // 添加第一列 lvc.iSubItem 0; lvc.pszText TEXT(名称); lvc.cx 150; lvc.fmt LVCFMT_LEFT; ListView_InsertColumn(hListView, 0, lvc); // 添加第二列 lvc.iSubItem 1; lvc.pszText TEXT(大小); lvc.cx 80; lvc.fmt LVCFMT_RIGHT; ListView_InsertColumn(hListView, 1, lvc);2.2 数据操作与通知处理添加数据项的基本流程LVITEM lvi {0}; lvi.mask LVIF_TEXT; lvi.iItem 0; // 第一行 lvi.iSubItem 0; // 第一列 lvi.pszText TEXT(示例文件.txt); ListView_InsertItem(hListView, lvi); // 设置子项文本 ListView_SetItemText(hListView, 0, 1, TEXT(1.5 MB));处理通知消息的典型模式case WM_NOTIFY: { LPNMHDR pnmh (LPNMHDR)lParam; if(pnmh-idFrom IDC_LISTVIEW) { switch(pnmh-code) { case LVN_ITEMCHANGED: // 处理项选择变化 break; case LVN_BEGINLABELEDIT: // 开始编辑标签 break; case LVN_ENDLABELEDIT: // 结束编辑标签 break; } } break; }2.3 高级功能实现实现图标视图模式// 创建图像列表 HIMAGELIST hImageList ImageList_Create(16, 16, ILC_COLOR32, 5, 5); // 添加图标(需提前加载) HICON hIcon LoadIcon(hInstance, MAKEINTRESOURCE(IDI_FILEICON)); ImageList_AddIcon(hImageList, hIcon); // 关联图像列表 ListView_SetImageList(hListView, hImageList, LVSIL_SMALL); // 设置视图模式 DWORD dwStyle GetWindowLong(hListView, GWL_STYLE); dwStyle (dwStyle ~LVS_TYPEMASK) | LVS_ICON; SetWindowLong(hListView, GWL_STYLE, dwStyle);注意事项图像列表的生命周期需要手动管理应在控件销毁前保持有效控件销毁后释放资源。3. 树形视图(TreeView)实战指南3.1 构建层次化数据结构树形控件适合展示层级关系数据如文件目录结构HWND hTreeView CreateWindowEx( 0, // 无扩展样式 WC_TREEVIEW, // TreeView类名 TEXT(目录结构), // 控件文本 WS_CHILD | WS_VISIBLE | TVS_HASLINES | TVS_LINESATROOT | TVS_HASBUTTONS, 10, 220, // 位置 300, 200, // 尺寸 hWndParent, // 父窗口句柄 (HMENU)IDC_TREEVIEW, // 控件ID hInstance, // 程序实例句柄 NULL); // 无额外数据添加树节点示例TVINSERTSTRUCT tvis {0}; tvis.hParent TVI_ROOT; // 根节点 tvis.hInsertAfter TVI_LAST; // 插入到最后 tvis.item.mask TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE; tvis.item.pszText TEXT(C盘); tvis.item.iImage 0; // 正常图标索引 tvis.item.iSelectedImage 1; // 选中时图标索引 HTREEITEM hRoot TreeView_InsertItem(hTreeView, tvis); // 添加子节点 tvis.hParent hRoot; tvis.item.pszText TEXT(Program Files); HTREEITEM hChild TreeView_InsertItem(hTreeView, tvis);3.2 交互与状态管理处理节点选择变化case WM_NOTIFY: { LPNMHDR pnmh (LPNMHDR)lParam; if(pnmh-idFrom IDC_TREEVIEW pnmh-code TVN_SELCHANGED) { LPNMTREEVIEW pnmtv (LPNMTREEVIEW)lParam; HTREEITEM hSelected pnmtv-itemNew.hItem; // 获取选中项文本 TVITEM tvi {0}; TCHAR szText[256]; tvi.mask TVIF_TEXT; tvi.hItem hSelected; tvi.pszText szText; tvi.cchTextMax sizeof(szText)/sizeof(TCHAR); TreeView_GetItem(hTreeView, tvi); // 更新状态显示等操作 } break; }实现右键菜单支持case WM_NOTIFY: { LPNMHDR pnmh (LPNMHDR)lParam; if(pnmh-idFrom IDC_TREEVIEW pnmh-code NM_RCLICK) { // 获取点击位置 DWORD dwPos GetMessagePos(); POINT pt { GET_X_LPARAM(dwPos), GET_Y_LPARAM(dwPos) }; // 显示上下文菜单 HMENU hMenu LoadMenu(hInstance, MAKEINTRESOURCE(IDR_TREE_MENU)); HMENU hSubMenu GetSubMenu(hMenu, 0); TrackPopupMenu(hSubMenu, TPM_LEFTALIGN | TPM_LEFTBUTTON, pt.x, pt.y, 0, hWnd, NULL); DestroyMenu(hMenu); } break; }4. 标签控件(TabControl)与多页面界面4.1 创建标签式界面标签控件是实现多页面布局的经典方案HWND hTab CreateWindowEx( 0, // 无扩展样式 WC_TABCONTROL, // TabControl类名 TEXT(), // 无文本 WS_CHILD | WS_VISIBLE | TCS_FIXEDWIDTH, 10, 10, // 位置 400, 300, // 尺寸 hWndParent, // 父窗口句柄 (HMENU)IDC_TABCONTROL, // 控件ID hInstance, // 程序实例句柄 NULL); // 无额外数据添加标签页TCITEM tie {0}; tie.mask TCIF_TEXT; tie.pszText TEXT(基本信息); TabCtrl_InsertItem(hTab, 0, tie); tie.pszText TEXT(高级设置); TabCtrl_InsertItem(hTab, 1, tie); tie.pszText TEXT(关于); TabCtrl_InsertItem(hTab, 2, tie);4.2 页面切换实现处理标签切换通知case WM_NOTIFY: { LPNMHDR pnmh (LPNMHDR)lParam; if(pnmh-idFrom IDC_TABCONTROL pnmh-code TCN_SELCHANGE) { int iSel TabCtrl_GetCurSel(hTab); // 隐藏所有页面 ShowWindow(hPage1, SW_HIDE); ShowWindow(hPage2, SW_HIDE); ShowWindow(hPage3, SW_HIDE); // 显示当前选中页面 switch(iSel) { case 0: ShowWindow(hPage1, SW_SHOW); break; case 1: ShowWindow(hPage2, SW_SHOW); break; case 2: ShowWindow(hPage3, SW_SHOW); break; } } break; }实操技巧为获得更好的视觉效果可以在创建子页面时使用WS_CLIPCHILDREN样式创建父窗口避免页面切换时的闪烁问题。5. 状态栏与工具栏集成5.1 状态栏实现状态栏通常用于显示程序状态信息HWND hStatus CreateWindowEx( 0, // 无扩展样式 STATUSCLASSNAME, // 状态栏类名 TEXT(就绪), // 初始文本 WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP, 0, 0, // 位置(通常由父窗口调整) 0, 0, // 尺寸(通常由父窗口调整) hWndParent, // 父窗口句柄 (HMENU)IDC_STATUS, // 控件ID hInstance, // 程序实例句柄 NULL); // 无额外数据设置状态栏分区// 定义分区宽度(最后一个为-1表示填充剩余空间) int nParts[] {100, 250, -1}; SendMessage(hStatus, SB_SETPARTS, sizeof(nParts)/sizeof(int), (LPARAM)nParts); // 设置各分区文本 SendMessage(hStatus, SB_SETTEXT, 0, (LPARAM)TEXT(状态)); SendMessage(hStatus, SB_SETTEXT, 1, (LPARAM)TEXT()); SendMessage(hStatus, SB_SETTEXT, 2, (LPARAM)TEXT(就绪));5.2 工具栏创建与配置工具栏提供快速访问常用功能的按钮// 初始化工具栏信息结构 TBBUTTON tbButtons[] { { 0, IDM_NEW, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0}, 0, 0 }, { 1, IDM_OPEN, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0}, 0, 0 }, { 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, {0}, 0, 0 }, // 分隔符 { 2, IDM_SAVE, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0}, 0, 0 } }; // 创建工具栏 HWND hToolbar CreateToolbarEx( hWndParent, // 父窗口 WS_CHILD | WS_VISIBLE | TBSTYLE_TOOLTIPS | TBSTYLE_FLAT, IDC_TOOLBAR, // 控件ID sizeof(tbButtons)/sizeof(TBBUTTON), // 按钮数量 hInstance, // 实例句柄 IDB_TOOLBAR, // 位图资源ID tbButtons, // 按钮定义 sizeof(tbButtons)/sizeof(TBBUTTON), // 按钮数量 16, 16, // 按钮宽高 16, 16, // 位图宽高 sizeof(TBBUTTON)); // 结构大小处理工具栏通知case WM_NOTIFY: { LPNMHDR pnmh (LPNMHDR)lParam; if(pnmh-idFrom IDC_TOOLBAR) { if(pnmh-code TTN_GETDISPINFO) // 工具提示 { LPTOOLTIPTEXT lpttt (LPTOOLTIPTEXT)lParam; switch(lpttt-hdr.idFrom) { case IDM_NEW: lpttt-lpszText TEXT(新建文件); break; case IDM_OPEN: lpttt-lpszText TEXT(打开文件); break; case IDM_SAVE: lpttt-lpszText TEXT(保存文件); break; } } } break; }6. 高级控件综合应用实例6.1 文件浏览器模拟实现结合ListView和TreeView实现简易文件浏览器// 初始化图像列表 HIMAGELIST hSysImageList NULL; SHFILEINFO sfi {0}; // 获取系统图像列表 hSysImageList (HIMAGELIST)SHGetFileInfo( TEXT(C:\\), 0, sfi, sizeof(SHFILEINFO), SHGFI_SYSICONINDEX | SHGFI_SMALLICON); // 关联到TreeView和ListView TreeView_SetImageList(hTreeView, hSysImageList, TVSIL_NORMAL); ListView_SetImageList(hListView, hSysImageList, LVSIL_SMALL); // 填充根目录 TVINSERTSTRUCT tvis {0}; tvis.hParent TVI_ROOT; tvis.hInsertAfter TVI_LAST; tvis.item.mask TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM; // 获取驱动器列表 TCHAR szDrives[MAX_PATH]; GetLogicalDriveStrings(MAX_PATH, szDrives); TCHAR* pDrive szDrives; while(*pDrive) { // 获取驱动器图标 SHGetFileInfo(pDrive, 0, sfi, sizeof(SHFILEINFO), SHGFI_SYSICONINDEX | SHGFI_DISPLAYNAME); tvis.item.pszText sfi.szDisplayName; tvis.item.iImage sfi.iIcon; tvis.item.iSelectedImage sfi.iIcon; tvis.item.lParam (LPARAM)pDrive; HTREEITEM hItem TreeView_InsertItem(hTreeView, tvis); // 为驱动器添加标记表示可能有子项 TVITEM tvItem {0}; tvItem.mask TVIF_CHILDREN; tvItem.hItem hItem; tvItem.cChildren 1; TreeView_SetItem(hTreeView, tvItem); pDrive _tcslen(pDrive) 1; }6.2 处理树形控件的展开事件动态加载子目录case WM_NOTIFY: { LPNMHDR pnmh (LPNMHDR)lParam; if(pnmh-idFrom IDC_TREEVIEW) { if(pnmh-code TVN_ITEMEXPANDING) { LPNMTREEVIEW pnmtv (LPNMTREEVIEW)lParam; // 只在首次展开时加载子项 if((pnmtv-action TVE_EXPAND) !(pnmtv-itemNew.state TVIS_EXPANDEDONCE)) { TVITEM tvItem {0}; tvItem.mask TVIF_HANDLE | TVIF_PARAM; tvItem.hItem pnmtv-itemNew.hItem; TreeView_GetItem(hTreeView, tvItem); // 设置已展开标记 tvItem.mask TVIF_STATE; tvItem.state TVIS_EXPANDEDONCE; tvItem.stateMask TVIS_EXPANDEDONCE; TreeView_SetItem(hTreeView, tvItem); // 加载子目录 LoadDirectoryContents(hTreeView, pnmtv-itemNew.hItem, (LPCTSTR)tvItem.lParam); } } } break; }7. 常见问题与调试技巧7.1 控件不显示或显示异常排查检查父窗口句柄确保传递给CreateWindowEx的hWndParent参数有效验证样式组合某些样式组合可能冲突尝试简化样式进行测试确认尺寸和位置控件可能创建在可视区域外检查资源加载图标、图像列表等资源是否成功加载7.2 通知消息处理问题未收到预期通知确认控件创建时使用了正确的通知样式(如LVS_EDITLABELS)检查父窗口的消息处理函数是否正确处理了WM_NOTIFY通知处理无效确保比较的是正确的控件ID(pnmh-idFrom)验证通知代码(pnmh-code)处理是否正确7.3 性能优化建议大量数据加载使用ListView_SetItemCount预分配空间配合LVS_OWNERDATA样式实现虚拟列表分批加载数据界面响应耗时操作放在后台线程使用BeginUpdate/EndUpdate减少重绘资源管理及时释放不再使用的图像列表重用GDI对象7.4 调试日志示例添加调试输出帮助定位问题#ifdef _DEBUG void DebugTrace(LPCTSTR lpszFormat, ...) { va_list args; va_start(args, lpszFormat); TCHAR szBuffer[1024]; _vstprintf_s(szBuffer, lpszFormat, args); OutputDebugString(szBuffer); va_end(args); } #else #define DebugTrace ((void)0) #endif // 使用示例 DebugTrace(TEXT(TreeView通知: code%d, idFrom%d\n), pnmh-code, pnmh-idFrom);8. 现代Win32开发补充说明虽然现代UI框架如WPF、Qt等提供了更高级的抽象但理解Win32原生控件仍有其价值系统级开发某些系统工具和底层应用仍需直接使用Win32 API性能关键场景对性能极度敏感的应用可能受益于轻量级的Win32实现自定义控件基础基于原生控件开发高度定制化的UI组件维护旧代码理解和维护遗留Win32应用程序对于新项目可以考虑以下现代替代方案使用Windows Template Library(WTL)简化Win32开发采用Windows UI Library(WinUI)获得现代Fluent设计考虑跨平台框架如Qt或wxWidgets在实际项目中我通常会根据以下因素选择技术方案项目规模和复杂度团队技术栈熟悉度目标平台要求维护和扩展需求对于小型工具或需要极致性能的组件Win32原生开发仍然是可靠选择。而对于大型应用或需要现代UI效果的项目则建议考虑更高级的框架。

相关推荐

OpenClaw TTS语音合成技术解析与应用实践
OpenClaw TTS语音合成技术解析与应用实践

1. 项目概述:OpenClaw TTS 语音合成技术解析OpenClaw TTS 作为新一代语音合成引擎,正在重新定义人机交互的边界。不同于传统TTS系统仅关注文本到语音的转换,OpenClaw构建了一个支持14种语音提供商、覆盖多场景输出的智能语音中枢。我在实际部… · 2026/11/2 21:50:28

矢量网络分析仪与示波器的原理、应用与选型对比
矢量网络分析仪与示波器的原理、应用与选型对比

1. 矢量网络分析仪与示波器的技术分野概述在电子测量领域,矢量网络分析仪(VNA)和示波器是两类截然不同的测试设备。VNA主要用于测量射频和微波器件的S参数,而示波器则专注于时域信号的捕获与分析。这两种仪器虽然都能处理电信号&a… · 2026/11/2 21:50:28

Wireshark高级流量分析:TCP重组与安全检测实战
Wireshark高级流量分析:TCP重组与安全检测实战

1. Wireshark流量分析进阶实战指南作为网络工程师和安全研究员最常用的工具之一,Wireshark的强大功能远不止于基础的抓包操作。在实际工作中,我们经常需要处理复杂的网络问题、安全事件调查和协议分析任务。本文将深入探讨Wireshark的高级分析技巧&#… · 2026/11/2 21:49:27

2026大模型学习路线:从工具收集到生态思维的四阶段指南
2026大模型学习路线:从工具收集到生态思维的四阶段指南

1. 从"工具收集癖"到"生态思维":为什么2026年学大模型不能再靠拼凑教程我见过太多人学大模型的方式是这样的:收藏夹里躺着几十篇"XX大模型从入门到精通",硬盘里存着十几个G的教程视频,GitHub星标了… · 2026/11/2 22:43:01

Java与Python在AI领域的分工差异:从模型训练到工程化落地
Java与Python在AI领域的分工差异:从模型训练到工程化落地

1. 从招聘JD里看出的真相:两门语言在AI赛道上的分工差异打开任何一个主流招聘平台,搜索"AI工程师",你会发现一个很有意思的现象:岗位描述里写"熟练掌握Python"的占了绝大多数,但往下翻&#xff0c… · 2026/11/2 22:43:01

C++从零实现AI Agent框架:架构设计与工程实践
C++从零实现AI Agent框架:架构设计与工程实践

1. 为什么会有这个系列:从一次性能焦虑说起去年年底我接手了一个内部工具项目,需求很明确:做一个能自动抓取数据、调用模型、执行本地脚本、再把结果整理成报告的小型智能体。团队里有人提议用 Python 快速搭一版,毕竟生态成熟、上… · 2026/11/2 22:43:01

多Agent系统组织模型设计与实践指南
多Agent系统组织模型设计与实践指南

1. 多Agent系统组织模型设计概述在复杂问题求解领域,多Agent系统(MAS)正成为突破传统集中式架构局限的关键技术范式。这种由多个自主智能体组成的分布式网络,通过角色分工与协同机制,展现出处理开放性、动态性任务的独特优势。去年某电商平台… · 2026/11/2 22:43:01

Higress 云原生网关实战:Envoy+Istio+Wasm 架构解析与性能调优
Higress 云原生网关实战:Envoy+Istio+Wasm 架构解析与性能调优

1. 从 Envoy 到 Higress:一个云原生网关的诞生逻辑第一次接触 Higress 是在一个内部服务治理项目里,当时我们面临一个很典型的问题:K8s 集群里跑着几十个微服务,南北向流量用 Nginx Ingress,东西向用 Istio&#xff0c… · 2026/11/2 22:43:01

多体系统预测控制原理与工业应用实践
多体系统预测控制原理与工业应用实践

1. 多体系统预测控制概述多体系统预测控制(Multibody Predictive Control)是近年来控制工程领域备受关注的前沿方向。我在工业机器人轨迹规划项目中首次接触这一方法时,就被其独特的优势所吸引——它能够同时处理多个相互关联的动力学系统的控… · 2026/11/2 22:42:01

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 4:42:45

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 5:17:18

雷达信号调制技术解析:从LFM到伪线性调频原理与应用
雷达信号调制技术解析:从LFM到伪线性调频原理与应用

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 1:01:15

基于MAX14431与TM4C123的工业SPI隔离电路设计全攻略
基于MAX14431与TM4C123的工业SPI隔离电路设计全攻略

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 1:01:15

PLFM_RADAR:相位线性调频雷达信号处理框架
PLFM_RADAR:相位线性调频雷达信号处理框架

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 1:01:15

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 4:42:45

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 5:17:18

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码