尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
MFC单文档自绘标题栏:NC区绘制、命中测试与DPI适配
简介面向 Windows C 桌面开发者的 MFC 单文档窗口自绘实践示例重点在于覆盖系统默认的非客户区样式通过继承 CFrameWnd、重写 OnPaint 与 OnEraseBkgnd 等消息处理函数来绘制窗口边框、标题栏与关闭按钮并兼顾按钮高亮、按下状态切换以及不同分辨率和 DPI 下的兼容处理。压缩包共 32 个文件、约 95KB以 h、cpp 源码为主体配合 ico、bmp 图标位图资源以及 rc、vcproj、sln 等工程配置并附可运行的 exe 示例便于直接对照编译调试。已有 394 人学习下载。读者可从中获得非客户区事件响应、GDI 绘图与皮肤资源加载的完整实现思路理解鼠标点击区域判定与重绘时机适合希望突破 MFC 默认界面外观、提升应用视觉体验的中级开发者参考。1. 标题栏不是画出来的MFC 单文档窗口真正难处理的地方在 NC 区很多人在 MFC 单文档程序里第一次尝试修改边框标题栏时都会掉进同一个误区在OnDraw或者OnPaint里画了一条漂亮的横条运行起来却发现它老老实实待在客户区顶部系统标题栏该在还在、该丑还丑。原因不复杂——标题栏在 Win32 的坐标体系里根本不属于你。它属于非客户区Non-Client Area简称 NC 区由系统的窗口管理器在WM_NCPAINT消息里绘制客户区的任何绘制指令都覆盖不到它。所以自己画标题栏这件事的真实含义是完成两件事接管 NC 区的绘制权WM_NCPAINT并接管该区域的命中测试权WM_NCHITTEST。只接管绘制权你得到的是一个外观漂亮但所有按钮都点不动的假标题栏只接管命中测试权你得到一个能拖动但外观仍然由系统控制的普通窗口。MFC 的CFrameWnd在这条链路上几乎没有封装可依赖的现成逻辑只能回到 Win32 消息层自己处理。这也正是这个机制值得深入学习的原因它是理解窗口边界到底由谁定义的最短路径。2. WM_NCPAINT 的调用链系统标题栏究竟在什么时候被画出来要接管一件事先要知道它什么时候发生。窗口边框和标题栏的绘制并不是窗口创建时调用一次绘制函数这种朴素模型它由一组消息协作完成其中任何一环被改变最终呈现的结果就会不一样。2.1 一条 SetWindowPos 引发的四连消息改变窗口大小、改变激活状态、恢复最小化最终都会走到同一个流程里。以一次SetWindowPos为例消息顺序大致是这样的先发WM_WINDOWPOSCHANGING让程序有机会修改目标位置然后进入尺寸计算阶段系统发WM_NCCALCSIZE问客户区该从哪开始、到哪结束算完之后发WM_WINDOWPOSCHANGED接着才是两块绘制相关的消息——WM_ERASEBKGND负责擦背景WM_NCPAINT负责画非客户区。激活状态变化是另一条独立路径窗口被点中或失去焦点时发WM_NCACTIVATEwParam为TRUE表示变为激活态。默认处理里系统会重画一遍标题栏把标题文字从灰变黑或者反过来。如果你在自绘逻辑中重写了OnNcActivate却直接return TRUE就等于告诉系统我已经处理好了但实际上什么都没画结果就是窗口切走再切回来标题栏颜色毫无变化——这是自绘 NC 区最常见的假死现象之一。有一个容易忽略的细节WM_NCPAINT的wParam名义上是需要重绘的 NC 区域句柄但实际运行时绝大多数情况下传的是1表示整块 NC 区都要重画。指望靠这个wParam做局部刷新是不现实的真想优化就得自己用GetUpdateRgn拿区域。2.2 CFrameWnd 没有替你封装的部分CWnd里确实存在OnNcPaint、OnNcActivate、OnNcHitTest这几个消息处理函数但它们的默认实现几乎都是Default()——把控制权原样交还给系统的窗口过程。CFrameWnd也没有在它们之上加任何逻辑。这意味着自绘标题栏在 MFC 里是一个零起点任务消息映射要自己挂绘制要自己写连什么时候该重画都得自己判断。消息映射的挂法本身很朴素但漏掉一个就会导致整套逻辑时不时失效// MainFrm.cpp BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) ON_WM_NCPAINT() // WM_NCPAINT - OnNcPaint ON_WM_NCACTIVATE() // WM_NCACTIVATE - OnNcActivate ON_WM_NCHITTEST() // WM_NCHITTEST - OnNcHitTest ON_WM_NCMOUSEMOVE() // 按钮悬停高亮要用 ON_WM_NCLBUTTONDOWN() // 自绘按钮的按下反馈要用 END_MESSAGE_MAP()这五条消息构成一个最小闭环WM_NCPAINT负责画WM_NCACTIVATE负责在焦点变化时触发重画WM_NCHITTEST负责把鼠标位置翻译成区域语义后两条负责按钮的视觉反馈。少任何一条交互都会出现看起来能用、用起来别扭的状态。2.3 GetWindowDC 与坐标系原点NC 区的绘制必须用GetWindowDC拿设备上下文不能用GetDC。区别在于GetDC返回的 DC 原点在客户区左上角而GetWindowDC返回的 DC 原点在整个窗口含边框和标题栏的左上角。用错了你画出来的东西会整体偏移一个标题栏加边框的距离而且随着边框宽度变化而漂移。GetWindowDC拿到的 DC 还带着一个裁剪区只允许在 NC 区域内绘制。这个裁剪区是好事能防止你意外画到客户区但如果你打算用双缓冲把整窗一次性贴上去就得先SelectClipRgn(NULL)或者干脆用内存 DC 拼好再BitBlt。void CMainFrame::OnNcPaint() { // 关键点这里绝不能调用 CFrameWnd::OnNcPaint() // 它的实现是 Default()会把系统标题栏原样画回去覆盖你的成果 CDC* pDC CDC::FromHandle(::GetWindowDC(GetSafeHwnd())); if (pDC nullptr) return; CRect rcWindow; GetWindowRect(rcWindow); // GetWindowDC 的原点在窗口左上角所以要把屏幕坐标平移回来 rcWindow.OffsetRect(-rcWindow.left, -rcWindow.top); CRect rcTitle(rcWindow); rcTitle.bottom rcTitle.top GetTitleBarHeight(); // 高度按 DPI 计算见 3.3 DrawTitleBar(pDC, rcTitle, m_bActive); ::ReleaseDC(GetSafeHwnd(), pDC-GetSafeHdc()); // 必须成对释放否则 DC 泄漏 }这段代码里三个参数值得说明。GetSafeHwnd()是 MFC 对窗口句柄的安全包装窗口未创建时返回NULL可以省掉一次判空。GetWindowRect返回的是屏幕坐标而GetWindowDC的原点在窗口自身左上角所以必须做一次OffsetRect这个平移是新手最容易漏的一步漏了之后标题栏会画到屏幕左上角去。最后的ReleaseDC必须用::ReleaseDC这个全局版本配套::GetWindowDC不能和CDC::ReleaseDC混用。3. 样式裁剪与坐标计算让系统把标题栏的位置让出来绘制只是表面真正决定你能不能画出想要的效果的是窗口样式和 NC 区尺寸的计算。这两块决定了系统留给你多少可用的空间。3.1 WS_CAPTION 其实是两个样式的和很多人以为WS_CAPTION是一个独立的窗口样式实际上它在winuser.h里就是这么定义的WS_CAPTION WS_BORDER | WS_DLGFRAME。这个定义解释了一个常见困惑——为什么去掉标题栏之后边框也跟着没了。因为标题栏和细边框共享同一个样式位。想保留可缩放的粗边框而只去掉标题栏正确做法是清掉WS_CAPTION再补上WS_THICKFRAMEBOOL CMainFrame::PreCreateWindow(CREATESTRUCT cs) { if (!CFrameWnd::PreCreateWindow(cs)) return FALSE; // WS_CAPTION 等价于 WS_BORDER | WS_DLGFRAME // 直接清掉会连边框一起清掉所以要显式补回 WS_THICKFRAME cs.style ~WS_CAPTION; cs.style | WS_THICKFRAME | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX; // MFC 的 FWS_ADDTOTITLE 会把文档名自动拼到窗口标题后面 // 自绘标题栏时文字由自己控制这个行为必须关掉 cs.style ~FWS_ADDTOTITLE; return TRUE; }WS_SYSMENU、WS_MINIMIZEBOX、WS_MAXIMIZEBOX这三个要保留因为自绘标题栏的关闭、最小化、最大化按钮需要HTCLOSE、HTMINBUTTON、HTMAXBUTTON这几个命中码而这些命中码只有在对应样式存在时才有意义。FWS_ADDTOTITLE是 MFC 特有的扩展样式默认开启作用是让框架窗口标题自动跟随当前文档名变化——自绘标题栏时它会把你精心设置的文字覆盖掉属于典型的查了半天以为是绘制 bug实际是框架在替你做事。3.2 WM_NCCALCSIZE客户区边界由谁决定WM_NCCALCSIZE是整条链路上最容易被低估的消息。它的wParam决定lParam的解释方式wParam为TRUE时lParam指向NCCALCSIZE_PARAMS里面有三个矩形分别是建议的新窗口矩形改之前的窗口矩形改之前的客户区矩形wParam为FALSE时lParam就是一个普通的RECT表示客户区矩形。返回值决定后续行为返回0表示按我改过的矩形来不要再重画返回WVR_REDRAW表示按我改过的矩形来但请重画整个窗口。后者会引发一次额外的擦除加绘制视觉上就是一道白闪。自绘标题栏常见两种做法。第一种是保留系统边框在OnNcCalcSize里返回0让系统照常计算标题栏高度然后你在这块区域上重画。这种做法改动小边框缩放、窗口阴影都由系统负责缺点是标题栏高度受系统主题牵制做不出很窄或者很高的异形标题栏。第二种是客户区铺满整窗把rgrc[0]直接改成整个窗口矩形NC 区宽度归零然后自己画完整的一圈边框和标题栏。这种做法自由度高但代价很直接——NC 区没了系统就再也收不到边缘的命中测试窗口缩放会立刻失效必须在OnNcHitTest里自己补回HTLEFT、HTTOPRIGHT这些返回值。自由度是靠工作量换来的。3.3 三个矩形与 DPI 缩放想在代码里正确算出标题栏矩形必须同时掌握三个坐标系GetWindowRect给的是屏幕坐标的整窗矩形GetClientRect给的是客户区坐标的宽高原点永远是(0,0)ClientToScreen负责两者之间的换算。标题栏高度在理论上等于窗口高度 − 客户区高度 − 上下边框但更稳妥的做法是直接查系统度量。在 DPI 感知开启的程序里GetSystemMetrics(SM_CYCAPTION)返回的是 96 DPI 下的逻辑值直接拿去用在 150% 缩放的屏幕上会偏小。应该改用按窗口 DPI 取值的版本int CMainFrame::GetTitleBarHeight() const { // 按窗口所在显示器的 DPI 取标题栏高度避免高缩放下偏小 UINT dpi ::GetDpiForWindow(GetSafeHwnd()); int h ::GetSystemMetricsForDpi(SM_CYCAPTION, dpi); // 标题文字行高 h ::GetSystemMetricsForDpi(SM_CYSIZEFRAME, dpi); // 上边框 h ScaleForDpi(4, dpi); // 自定义留白同样要缩放 return h; }这里的ScaleForDpi是自己写的辅助函数把设计稿上的像素值按dpi / 96.0换算。所有手写的间距、圆角半径、图标尺寸都得走这个函数否则在 125% 缩放下会出现图标和文字垂直方向不居中这种看起来像玄学的问题。4. 命中测试为什么你的自绘按钮点不动绘制决定窗口长什么样命中测试决定窗口怎么响应。这两件事在 NC 区是完全解耦的系统只认WM_NCHITTEST的返回值不看你画了什么。4.1 返回值就是一张区域地图WM_NCHITTEST的lParam打包了鼠标的屏幕坐标低位是 x高位是 y可以用GET_X_LPARAM/GET_Y_LPARAM拆出来也可以直接用一个CPoint接住。返回值才是关键它告诉系统这个点属于哪块区域返回值语义系统后续行为HTCLIENT客户区后续鼠标消息发给客户区HTCAPTION标题栏支持拖动、双击最大化、拖到屏幕边缘吸附HTLEFT/HTTOPRIGHT等边框八个方向进入缩放光标和缩放流程HTCLOSE关闭按钮点击后转换为WM_SYSCOMMANDSC_CLOSEHTMINBUTTON/HTMAXBUTTON最小化 / 最大化同上走系统命令HTNOWHERE无归属鼠标事件被丢弃最典型的失败方式是鼠标坐标是屏幕坐标而自己记录的按钮矩形是窗口坐标两套坐标直接比较结果永远不匹配。这类问题的表现很有迷惑性——按钮画得好好的就是点不动换个分辨率或者把窗口拖到别的位置又偶尔能点中很容易被误判成消息没收到。4.2 拖动和边缘吸附该交给系统处理一个常见的实现冲动是自己处理WM_LBUTTONDOWN然后SetCapture、跟着鼠标MoveWindow。这条路能走通但代价很大多显示器不同 DPI 下坐标换算会漂移窗口最大化时拖动还原的逻辑要自己写拖到屏幕边缘的吸附效果完全没有。正确做法是让WM_NCHITTEST对标题栏区域返回HTCAPTION剩下的全交给系统。系统内部处理拖动时会自动处理这些边界情况包括拖动过程中的半透明预览和释放后的窗口落位。这是那种自己实现三天交给系统三行的典型场景。UINT CMainFrame::OnNcHitTest(CPoint point) { // point 是屏幕坐标这点必须先确认否则后面的比较全是错的 CRect rcWindow; GetWindowRect(rcWindow); CRect rcTitle GetTitleBarRect(); // 窗口坐标下的标题栏 rcTitle.OffsetRect(rcWindow.TopLeft()); // 换算到屏幕坐标再比较 if (!rcTitle.PtInRect(point)) return CFrameWnd::OnNcHitTest(point); // 边缘缩放等仍走系统默认 CPoint ptClient point - rcWindow.TopLeft(); // 转成窗口坐标 for (const auto btn : m_buttons) { if (btn.rect.PtInRect(ptClient)) return btn.hitCode; // HTCLOSE / HTMINBUTTON / HTMAXBUTTON } return HTCAPTION; // 其余部分作为标题栏拖动与双击最大化由系统接管 }这段代码里OffsetRect和point - rcWindow.TopLeft()是两个方向相反的换算前者把窗口矩形搬到屏幕坐标后者把屏幕点搬回窗口坐标。写成同一种会立刻出错而且错得很隐蔽——窗口在屏幕左上角附近时看着正常一拖远就失灵。4.3 按钮的悬停反馈与 WM_NCACTIVATE 的联动返回HTCLOSE之后点击能关闭窗口但鼠标移上去没有任何高亮体验上仍然是半成品。NC 区的悬停反馈需要自己维护在WM_NCMOUSEMOVE里根据wParam就是上一步返回的命中码判断当前鼠标落在哪个按钮上和上一次的状态比较变了就重画 NC 区。重画动作要克制。直接调Invalidate()会把客户区一起标记为脏整个视图跟着重绘一遍视觉上就是明显的闪。应该用RedrawWindow只刷新框架部分void CMainFrame::OnNcMouseMove(UINT nHitTest, CPoint point) { if (nHitTest ! m_nHotButton) // 只有悬停目标变了才重画 { m_nHotButton nHitTest; // 只刷 NC 区不碰客户区避免整窗闪烁 RedrawWindow(NULL, NULL, RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW); } CFrameWnd::OnNcMouseMove(nHitTest, point); }RDW_FRAME是这里的关键标志它把刷新范围限制在非客户区RDW_UPDATENOW让重画立即发生而不是排队等到空闲否则快速划过按钮时高亮会明显滞后。WM_NCACTIVATE的默认处理也要改不能简单return TRUE应该先把激活状态存下来再触发一次 NC 区重画否则焦点切换后标题栏颜色不变。5. 避坑与排查自绘 NC 区最容易翻车的五个现场5.1 绘制类问题闪烁与残影现象拖动窗口边缘改变大小时标题栏出现明显的白闪或者上一次画的文字残留在原地没被擦掉。原因调用CFrameWnd::OnNcPaint()或者Default()让系统先用自己的背景色擦了一遍再画你的内容另一种情况是WM_NCCALCSIZE返回了WVR_REDRAW系统会额外插入一次擦除。残影则通常是因为绘制前没有填满整个目标矩形FillSolidRect只盖住了文字区域。解决OnNcPaint里完全不调父类实现OnNcCalcSize返回0而不是WVR_REDRAW绘制函数第一行就用背景色填满整个标题栏矩形再往上叠元素。5.2 绘制类问题失活后标题栏不刷新现象从别的窗口切回来标题栏颜色和按钮高亮状态没有跟着变看起来像卡住了。原因重写了OnNcActivate并直接return TRUE等于告诉系统这活我接了但实际上没写任何重画逻辑。解决在OnNcActivate里先记录bActive到成员变量然后调RedrawWindow刷 NC 区最后再决定返回值。如果希望系统也参与比如保留窗口阴影可以调用基类实现后再补一次自己的绘制。5.3 交互类问题窗口拖不动或者边缘不能缩放现象标题栏区域按住鼠标完全没反应或者边框看着在鼠标移上去不变缩放光标。原因OnNcHitTest对所有点都返回了HTCLIENT或者只处理了标题栏内圈、把外圈漏给了默认处理在客户区铺满整窗的方案里NC 区宽度归零系统根本收不到边缘的命中测试。解决确认标题栏判断用的是屏幕坐标在铺满方案里显式补上八个方向的缩放判断按距离窗口边缘的像素宽度返回HTLEFT、HTTOPLEFT等用调试输出把所有命中码打一遍直观看出地图对不对。5.4 尺寸类问题最大化后边框超出屏幕现象窗口最大化时自绘的右边框和下边框跑到屏幕外被任务栏遮住一截。原因最大化状态下GetWindowRect返回的矩形本身就比显示器工作区大一圈多出来的是系统预留的不可见边框。直接拿它当绘制基准边框自然越界。解决在最大化状态下改用MonitorFromWindow拿到当前显示器再用GetMonitorInfo取rcWork工作区矩形作为绘制基准而不是GetWindowRect。同时注意WM_NCCALCSIZE在最大化时也会被调用里面的矩形同样需要校正。5.5 DPI 类问题一换缩放比例就错位现象在 100% 缩放下完美换到 150% 之后文字偏小、图标和文字垂直方向不居中、按钮点击区域和视觉区域对不上。原因所有尺寸都写成了硬编码像素或者用了GetSystemMetrics而不是按 DPI 取值的版本再或者绘制时按 DPI 缩放了命中测试时忘了同步缩放导致视觉和热区错位。解决把设计稿像素 → 实际像素的换算收敛到一个函数里绘制和命中测试都调它窗口收到WM_DPICHANGED时重新计算标题栏高度并触发一次 NC 区重画。这类问题在真机上不测很难发现属于典型的血泪经验。6. 设计取舍完全自绘 NC 区值不值得把标题栏接管过来之后收益很直观配色能跟产品主题走标题栏高度、按钮样式、图标间距完全可控做深色主题时不用再忍受系统标题栏那一块突兀的浅色。但代价清单比想象中长——你在重新实现操作系统已经做了很多年的一件事。系统默认标题栏背后挂着一整套行为拖到屏幕边缘的吸附分屏、拖到顶部最大化、拖动时的半透明预览、窗口阴影、打开关闭动画、贴靠布局、触摸和手写笔的命中容差、屏幕阅读器对按钮名称的朗读、任务栏缩略图和切换器里的预览。自绘之后这些要么由系统继续负责当你返回HTCAPTION和HTCLOSE时要么就得自己补上。上面这套方案之所以坚持让WM_NCHITTEST返回系统语义码而不是自己处理鼠标就是在尽量把这些行为留在系统手里。如果需求只是标题栏颜色跟着主题变其实存在一条工作量小得多的路径保留系统标题栏通过桌面窗口管理器提供的属性接口直接改标题栏和边框的颜色。这条路不需要处理WM_NCPAINT、WM_NCCALCSIZE、WM_NCHITTEST中的任何一个也不需要重算 DPI代价是可定制程度有限做不出异形标题栏和自定义按钮布局。选择哪条路取决于你是想要换个颜色还是完全掌控。一个可以落地的判断方法是列出需求清单逐条问系统默认实现能不能满足。只有颜色、只有暗色模式、只需要在标题栏加一行副标题——选轻量方案。需要自定义按钮大小和位置、需要标题栏内嵌搜索框、需要完全无边框的卡片式窗口——才值得走完全自绘。中间地带最危险为了改一个背景色就接管整个 NC 区最后被迫维护一套永远追不上系统版本的交互逻辑。从那以后我每次评估这类需求都会强制先写一版最小验证只接管绘制、不接管命中测试跑一遍拖动、双击最大化、边缘吸附、多显示器不同 DPI、任务栏遮挡这五个场景看系统默认行为还剩多少。这五个场景全过再往里加自定义按钮只要有一个过不去就说明这套方案的成本被我低估了。这份资源里的代码骨架正好可以当作这个验证的起点把OnNcPaint、OnNcCalcSize、OnNcHitTest三处改造成自己要的样子先跑通再谈优化。希望帮到你。本文还有配套的精品资源点击获取
RELATED

相关推荐

Qwen语音模型全栈更新与Kyutai强化学习语音推理技术解析

Qwen语音模型全栈更新与Kyutai强化学习语音推理技术解析

1. 语音模型扎堆更新背后的技术路线之争九月底的云栖大会,Qwen 团队一口气把语音模型系列全端上来了。实时翻译、全模态交互、全双工语音,三个方向同时推进,这个节奏在开源语音圈里相当少见。同一天,Kyutai 也放出了 Voice of Rea…

📅 2026/11/2 22:50:24
稀疏矩阵排序与AMD算法优化实践

稀疏矩阵排序与AMD算法优化实践

1. 稀疏矩阵排序问题的背景与挑战在科学计算和工程仿真领域,稀疏矩阵(Sparse Matrix)的处理效率直接决定了整个数值模拟过程的性能。这类矩阵的特点是绝大多数元素为零,非零元素通常只占5%甚至更少。以有限元分析为例,…

📅 2026/11/2 22:50:24
语音模型新突破:实时翻译、全双工与全模态交互技术解析

语音模型新突破:实时翻译、全双工与全模态交互技术解析

语音模型这一波更新密度确实有点吓人。我盯着云栖大会那几场分享的实录翻了两遍,又去把 Kyutai 那篇 Voice of Reason 的论文啃了一下,最大的感受是:语音这条线正在从"能听清、能转写"往"能边听边说、能边想边算"的方向猛…

📅 2026/11/2 22:50:24
MORE NEWS

更多资讯

📰

awesome-agent-skills 完全对比:Agent Skills vs MCP vs 微调,智能体能力增强到底怎么选?

【免费下载链接】awesome-agent-skills The definitive resource for Agent Skills - modular capabilities revolutionizing AI agent architecture 项目地址: https://gitcode.com/gh_mirrors/awes/awesome-agent-skills 点击查看 免费下载 Agent Skills&#xf…

📰

九联UNT402A刷Armbian一次学会: S905L3机顶盒变身7×24小时Linux家用服务器

九联UNT402A刷Armbian一次学会: S905L3机顶盒变身724小时Linux家用服务器 【免费下载链接】amlogic-s9xxx-armbian Supports running Armbian on Amlogic, Allwinner, and Rockchip devices. Support a311d, s922x, s905x3, s905x2, s912, s905d, s905x, s905w, s905, s905l, r…

📰

123.Python——OpenCV实现视频窗口画中画的效果

这里把实现方法的具体说明拿出来给大家看看。1、先把主画面那边的摄像头视频给打开。然后再把显示这个视频的主窗口拿出来呈现给你看。2、与此同时, 把那个子画面里面的视频文件给打开了, 当然呢, 你也可以去打开任意一个视频流。3、先对子画面的大小进行缩放处理, 然后利用图像…

📰

金融科技FDE:连接产品与客户的桥梁,小白程序员必备的进阶之路

本文深入探讨了金融科技领域FDE(前线部署工程师)的核心价值与必备技能。FDE是弥合产品能力与客户真实需求、监管要求的桥梁,需具备软件工程、咨询及金融领域知识。文章解析了金融科技FDE的独特性,如合规约束、遗留系统集成、监管截…

📰

Java FRQ 001 - If condition

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

📰

PLFM雷达原理与10.5GHz相控阵开源实现

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

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬