尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
MFC网格控件CGridCtrl实战:接入、配置与避坑指南
简介面向MFC界面开发者的可直接使用的表格控件资源提供全套源码且无需注册即可集成到项目中。相比MSFlexGrid在大数据量时容易报错该控件内置虚拟缓冲区模式实测可流畅显示2000×2000规模的虚拟表格适合数据报表、管理后台等需要高性能表格展示的Visual C项目。压缩包共90个文件约422KB包含27个头文件、23个cpp源文件以及多组工程文件覆盖VC6、VS2003、VS2008直至VS2013的示例与修改版另有图标、位图等界面资源和可直接运行的demo程序。还附带GridCtrlDemo和GridView两个完整示例工程便于对照学习控件的派生封装、单元格类型、就地编辑及视图集成方法。已有4768人学习下载适合有一定MFC基础、希望摆脱MSFlexGrid限制的开发者直接取用也可作为表格控件二次开发的起点。1. 网格控件是什么先解决 MFC 表格的“天天造轮子”问题很多人在 MFC 里做表格第一反应是拖一个 CListCtrl 进来然后写几十行代码处理列头排序、单元格编辑最后发现还要自己管理一个代理编辑框的位置和销毁越写越像在重新造一个表格控件。标题里说的“非常好用的表格控件”我理解的是开源社区里流传最广的 CGridCtrl 这类网格控件——它把行号、列头、可编辑单元格、排序、合并、虚拟模式都封装在一个 CWnd 派生类里接入对话框后开箱即用。本文按我做过的选型和接入过程讲清楚它和原生控件差在哪、怎么接、参数怎么设、有哪些坑最后告诉你哪种场景下值得为它投入。2. 控件选型原生 CListCtrl 的边界和网格控件的取舍2.1 原生 CListCtrl 的边界报告模式表格为什么总差一步先明确一个判断CListCtrl 在报告模式下做“列表”是没问题的但做“表格”会处处别扭。最典型的问题是单元格编辑。CListCtrl 本身没有编辑能力必须自己创建一个 CEdit 子窗口在 LVN_BEGINLABELEDIT 里弹出来在 LVN_ENDLABELEDIT 里收数据然后销毁它。这个流程单看一次不难但要在鼠标点击、滚动、列宽调整时同步编辑框位置代码量立刻上去。列头排序是第二个硬伤。CListCtrl 只给了 LVM_SORTITEMS 和排序回调回调里拿到的只是两个行索引你要自己去维护一个“行索引到数据对象”的映射表才能把排序后的行对应回业务数据。数据一变、行一删这个映射表就得同步维护属于典型的隐藏成本。第三个边界是样式控制。CListCtrl 想单独给某个单元格设置背景色、字体、图标必须走自绘而自绘一旦开了所有单元格的绘制逻辑都归你管。合并单元格、行头显示、按行高亮这些需求在 CListCtrl 里基本等于重写一个控件。如果你只是显示数据、不做编辑CListCtrl 完全够用一旦需求表里出现“可编辑”“合并”“排序带状态”就该换方案了。2.2 CGridCtrl 这类网格控件做了什么单元格模型和通知机制CGridCtrl 本质上是一个自绘网格窗口它把表格拆成“固定行列头、固定列行号、数据区”三部分每个单元格由 CGridCellBase 派生对象管理自己的绘制、编辑、状态。这套模型的好处是你不用维护两个界面状态一个控件显示、一个数据模型改单元格内容就是 SetItemText读内容就是 GetItemText和操作数组差不多。它“好用”的核心还在于通知机制。排序时它不自己乱排而是发出 GVN_SORTCOLUMNCLICK 通知把被点击的列号传给你数据怎么排由你决定编辑结束时发出 GVN_ENDEDIT把行列号传给你再由你把新值写回业务结构。这和 CListCtrl 的“控件主动做一半、留下一半让你猜”是完全不同的设计思路。理解这个模型后面所有配置就有章可循。2.3 选型建议什么场景值得换成网格控件需求原生 CListCtrl 报告模式CGridCtrl 类开源网格商业界面库网格纯展示、无编辑够用略重不必要需要单元格编辑自己套 CEdit维护成本高内置编辑开箱即用好用但需授权大数据量十万行级虚拟列表可撑代码复杂虚拟模式回调简单性能强列头自动排序自己处理映射通知回调逻辑清晰自带合并单元格不支持支持支持成本无开源免费按授权收费我的习惯是项目里只要出现“单元格编辑”或“合并”任意一个关键词就直接上 CGridCtrl 类网格不在 CListCtrl 上做二次开发。因为二次开发的量往往比引入一个成熟 Grid 控件更大而且后续维护都在自己头上。如果你有预算、也确实需要更复杂的打印和样式商业控件是另一个方向但就“免费、可控、社区资料多”这三个条件看CGridCtrl 这类开源网格在 MFC 项目里仍然是主流选择。3. 把网格控件接进 MFC 工程从源码到对话框的完整步骤3.1 源码接入文件加到工程和编译前的准备这个控件是以源码形式使用的所以第一步是把相关文件放进工程。常见的做法是新建一个 GridCtrl 目录把控件源码文件拷进去然后在 Visual Studio 工程里通过“添加现有项”把它们加进项目。编译之前要先确认两件事工程字符集是 Unicode 还是多字节以及是否定义了和编译器版本相关的开关宏。源码里如果有针对不同 VS 版本的宏开关按你用的版本设置即可我用 VS2013 到 VS2019 都编过没遇到必须要改代码的情况。// GridCtrl.h 中的典型声明位置 #pragma once #include GridCtrl.h // 主头文件通常在 Demo 工程里也引用它加完文件后编译一次解决头文件路径问题。常见的报错是找不到 GridCtrl.h这时在项目属性 C/C 常规 - 附加包含目录里加上源码所在目录即可。另一个容易忽略的点如果工程启用了预编译头而源码文件没有按预编译头规范写会出现强制包含头文件不匹配的警告把那些文件设置为“不使用预编译头”就行。3.2 在对话框里落地Custom Control 和 DDX 绑定接入对话框有两种常见做法一种是在资源编辑器里拖一个 Custom Control把类名改成 CGridCtrl另一种是完全在代码里动态 Create。我一般用前者因为可以在对话框模板上直接把位置拖好所见即所得。Custom Control 的类名要填 CGridCtrlID 设成一个有意义的资源 ID比如 IDC_MY_GRID。// 在对话框类的头文件里声明网格控件成员 class CMyDialog : public CDialogEx { // ... protected: CGridCtrl m_gridCtrl; // 网格控件成员变量 }; // 在 DoDataExchange 中完成绑定 void CMyDialog::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); DDX_Control(pDX, IDC_MY_GRID, m_gridCtrl); }这里 DDX_Control 的作用是把模板上那个 Custom Control 和 CGridCtrl 成员变量关联起来控件窗口创建后会自动 attach 到 m_gridCtrl。要注意它必须在 OnInitDialog 之前执行因为模板控件的 CWnd 句柄是在 DoDataExchange 阶段挂接的。之后你在 OnInitDialog 里就可以直接调用网格控件的各种方法。3.3 最小初始化SetGridDefaults 之后先设置行列结构控件窗口创建好之后第一步永远先调用 SetGridDefaults()它会设置默认字体、网格线颜色、默认行高列宽、固定行数量等内部参数。之后你再调整列数、行数、列宽都是在默认基础上覆盖。如果你跳过这一步直接 SetColumnCount部分版本的控件内部状态没有初始化会出现列头不显示或者单元格绘制异常的玄学问题。BOOL CMyDialog::OnInitDialog() { CDialogEx::OnInitDialog(); // 先初始化控件默认状态再覆盖具体配置 m_gridCtrl.SetGridDefaults(); // 表格结构5 列固定 1 行作为列头数据行 19 行 m_gridCtrl.SetColumnCount(5); m_gridCtrl.SetFixedRowCount(1); m_gridCtrl.SetRowCount(20); // 列宽按像素设置 m_gridCtrl.SetColumnWidth(0, 100); m_gridCtrl.SetColumnWidth(1, 140); m_gridCtrl.SetColumnWidth(2, 160); m_gridCtrl.SetColumnWidth(3, 80); m_gridCtrl.SetColumnWidth(4, 120); // 填充列表头文字注意行号从 0 开始 m_gridCtrl.SetItemText(0, 0, _T(设备名)); m_gridCtrl.SetItemText(0, 1, _T(状态)); m_gridCtrl.SetItemText(0, 2, _T(所在位置)); m_gridCtrl.SetItemText(0, 3, _T(数量)); m_gridCtrl.SetItemText(0, 4, _T(备注)); // 填充第一行数据 m_gridCtrl.SetItemText(1, 0, _T(循环泵)); m_gridCtrl.SetItemText(1, 1, _T(运行中)); m_gridCtrl.SetItemText(1, 2, _T(1号车间)); m_gridCtrl.SetItemText(1, 3, _T(2)); m_gridCtrl.SetItemText(1, 4, _T(备用机已就绪)); return TRUE; }这段代码里你只需要往 SetItemText 的行列索引里填数据。索引规则一定要记牢第 0 行是固定列表头所以第一条数据从第 1 行开始这个 1 偏移是新手最常见的出错点。SetColumnWidth 的单位是像素不是字符数所以当你发现列宽显示比预期窄很多时先检查是不是把它当成了字符数。列数超过实际内容宽度时控件会自动出现横向滚动条。整个初始化没有触发任何重绘因为所有配置都发生在控件可见之前。4. 让表格真正能干活排序、编辑和行列操作的参数设置4.1 列头排序用 GVN_SORTCOLUMNCLICK 通知接住点击事件CGridCtrl 默认不替你排序它只在你点击列头时发出 GVN_SORTCOLUMNCLICK 通知。这个设计一开始会让人觉得“怎么还要自己写”但实际用下来会发现这是对的你的数据存在哪、是结构体数组还是数据库结果集只有你自己知道控件无法替你决定排序算法。你要做的是在消息映射里接住这个通知然后对业务数据排序再回填到控件。// 消息映射部分添加通知响应 BEGIN_MESSAGE_MAP(CMyDialog, CDialogEx) ON_NOTIFY(GVN_SORTCOLUMNCLICK, IDC_MY_GRID, CMyDialog::OnGridSortColumn) END_MESSAGE_MAP() // 排序响应函数 void CMyDialog::OnGridSortColumn(NMHDR* pNotifyStruct, LRESULT* pResult) { NM_GRIDVIEW* pView (NM_GRIDVIEW*)pNotifyStruct; int nCol pView-iColumn; // 用户点击的列号 // 这里对你的业务数据结构做排序以 m_vecData 为例 // std::sort(m_vecData.begin(), m_vecData.end(), [nCol](...){ ... }); // 排序后重新回填到控件 RefreshGridFromData(); *pResult TRUE; // 返回 TRUE 表示排序逻辑由外部处理 }这里 NM_GRIDVIEW 结构的 iColumn 字段保存了被点击的列号。比较函数里按列号从业务对象中取出对应字段字符串转数字再比较能避免“10”排在“9”前面的字典序问题。排序完成后调用 RefreshGridFromData 统一回填。注意 *pResult 必须设为 TRUE否则部分版本会继续执行它内部的一套排序逻辑造成数据和界面顺序不一致。4.2 单元格编辑打开编辑状态和控制只读列网格控件的编辑能力默认是打开的用户双击或按 F2 就能进入单元格编辑。这个默认行为在大部分业务场景下没问题但有两种情况需要调整列表头不能被编辑某些业务字段不允许用户改。列表头因为是固定行编辑状态本身不受影响但你可以统一把固定行设成只读。// 使整个数据区域可编辑默认就是 TRUE写明是为了清晰 m_gridCtrl.SetEditable(TRUE); // 第 0 行列头全部只读防止用户误改表头 for (int col 0; col m_gridCtrl.GetColumnCount(); col) { m_gridCtrl.GetCell(0, col)-SetReadOnly(TRUE); } // 数据行的“数量”列第 3 列只读数量由后台计算得出 for (int row 1; row m_gridCtrl.GetRowCount(); row) { m_gridCtrl.GetCell(row, 3)-SetReadOnly(TRUE); }GetCell 返回的是该单元格的 CGridCellBase 指针SetReadOnly 直接控制该格的编辑状态。这个粒度比全表开关更符合真实业务大部分字段可编辑个别字段锁定。编辑结束后要接住 GVN_ENDEDIT 通知把新值同步回业务数据否则用户改了界面、程序里拿到的还是旧数据这个属于比较隐蔽的数据一致性问题后面避坑章节会展开。4.3 合并单元格与行列尺寸SetItemSpan 和动态调整合并单元格是 CListCtrl 给不了的能力做报表、排产计划这类需求时很关键。网格控件里用 SetItemSpan 实现参数是起始行、起始列、跨行数、跨列数。合并后文字只写在最左上角的单元格里其他被合并的区域保持空白否则会出现文字叠加的效果。// 把第 1 行第 0 列合并成跨 1 行 2 列的区域 m_gridCtrl.SetItemSpan(1, 0, 1, 2); m_gridCtrl.SetItemText(1, 0, _T(这是一个合并单元格)); // 注意合并区域里的第二个单元格不要再 SetItemText否则绘制会重叠行列尺寸的动态调整在日常需求里比合并更常见。程序运行中把某一行行高调大以显示完整内容或者根据内容宽度自动调整列宽这些都可以直接操作。// 动态调整行高单位是像素 m_gridCtrl.SetRowHeight(3, 30); // 根据内容宽度调整第 1 列的列宽AutoSizeColumn 传入列号 m_gridCtrl.AutoSizeColumn(1); // 禁用用户拖拽调整列宽在某些报表场景需要固定列宽 m_gridCtrl.EnableColumnResize(FALSE);SetRowHeight 和 SetColumnWidth 的参数都以像素为单位。AutoSizeColumn 会用当前所有行的内容宽度去计算但如果数据量很大这个方法的性能会随行数上升明显变慢适合行数几千以内的表格。用户拖拽列宽是默认行为报表锁定 Layout 时再关掉否则你会收到“用户把列拖没了”的求助。5. 常见问题与避坑从显示翻车到数据错乱的四条实战记录5.1 排序后滚动条位置错乱界面和数据对不上现象点击列头排序后数据顺序变了但滚动条滑块位置停在原地看起来像是“跳到了一条随机数据上”。如果此时用户再点击某一行选中行和实际数据对不上导出的数据顺序也和界面不一致。原因排序回填数据时没有把滚动条位置和当前选中行复位。控件内部的行状态在数据重填后仍然停留在排序前的坐标于是界面坐标和数据内容的映射关系错位。解决排序回调里在刷新数据前先记录当前行号刷新完成后把选中行复位再把滚动条滚到顶部或保持原行位置。int nCurRow m_gridCtrl.GetSelectedRow(); // 排序前记住选中行 int nScrollPos m_gridCtrl.GetScrollPos(SB_VERT); // 记住滚动位置 RefreshGridFromData(); // 回填排序后的数据 m_gridCtrl.SetSelectedRow(nCurRow); // 重新选中同一业务行 m_gridCtrl.SetScrollPos(SB_VERT, nScrollPos); // 恢复滚动位置SetSelectedRow 在某些版本会有选中状态闪烁属正常现象。如果你的排序是按业务主键排的建议用主键来找回原行而不是用行号因为行号在排序后已经不是原来的行号了。5.2 网格背景变成“十字准线”单元格刷新闪烁现象鼠标在网格上一移动整个表格区域频繁重绘出现明显的闪烁甚至能看出一个十字光线跟着鼠标跑。在某些低端机器或高分屏缩放下尤其严重。原因默认配置下控件会跟踪鼠标位置并高亮当前行和当前列这个“热点跟踪”功能会触发整行整列重绘。加上控件没有启用双缓冲重绘一密集闪烁就出来了。解决如果只是做数据展示直接关掉热点跟踪如果确实需要高亮选中行打开双缓冲模式。// 关闭鼠标热点跟踪减少重绘次数 m_gridCtrl.SetTrackFocusCell(FALSE); m_gridCtrl.SetHighlightSelected(TRUE); // 仅高亮选中单元格 // 打开双缓冲避免重绘闪烁 m_gridCtrl.SetDoubleBuffer(TRUE);这里要区分 SetHighlightSelected 和热点跟踪的区别前者只高亮当前选中的格子后者会时刻高亮鼠标所在的整行整列后者的绘制开销大得多。双缓冲会多占一块内存显存但在表格行列多的时候体验提升非常明显。5.3 编辑中文内容后乱码英文数字正常现象用户双击单元格输入“设备编号”四个字回车后界面显示变成乱码输入英文字母和数字一切正常。项目字符集切到 Unicode 时问题反而更隐蔽某些列乱码某些列正常。原因工程字符集设置和代码里的文本字面量不一致。多字节下用 _T 宏但文件保存为 UTF-8或 Unicode 下源码文件没有带 BOM都会造成字符串在编译期被错误解释。另一个典型来源是 GetItemText 返回的 CString 在传给数据库或串口时做了窄字节转换。解决统一工程字符集为 Unicode源码文件用 Visual Studio 默认编码保存所有字符串字面量用 _T() 包裹。从控件取文本需要转成多字节时用 CW2A 转换而不是直接强转。// 从控件取文本统一转成 UTF-8 再写入数据库 CString strItem m_gridCtrl.GetItemText(nRow, nCol); CStringA strUtf8 CW2A(strItem, CP_UTF8); // 然后把 strUtf8 交给数据层如果项目为了兼容老模块而必须用多字节字符集那所有源文件保持 ANSI 编码字符串字面量不要写中文改用资源表里的字符串。中文字符乱码的本质是“编译期字符集”“运行期代码页”“文件编码”三者不一致先统一工程字符集再检查文件编码不要靠写转换代码去掩盖问题。5.4 往表格里插入几千行数据时界面卡死现象程序启动时用循环 SetItemText 填充 5000 行、10 列的数据执行时间在调试版里超过 5 秒Release 版也有明显停顿窗口不刷新鼠标放到窗口上是转圈状态。原因每调用一次 SetItemText控件都会触发一次单元格重绘和内部状态更新。5000 行乘 10 列就是 5 万次重绘而其中绝大多数重绘对最终显示结果没有意义。这是典型的“逐格填充”造成的性能损耗不是控件本身在大数据量下不行。解决填充数据前用 SetRedraw(FALSE) 关闭重绘填充完成后统一打开重绘并刷新一次。这是 MFC 里处理批量 UI 更新的通用做法用在网格控件上同样有效。m_gridCtrl.SetRedraw(FALSE); // 开始批量填充前关闭重绘 for (int row 1; row 5000; row) { for (int col 0; col 10; col) { m_gridCtrl.SetItemText(row, col, BuildData(row, col)); } } m_gridCtrl.SetRedraw(TRUE); // 重新允许绘制 m_gridCtrl.Invalidate(); // 请求一次整体重绘把缓冲内容画上去SetRedraw(FALSE) 期间对网格控件的任何操作都只更新内部数据不画到屏幕上。Invalidate 放在 SetRedraw(TRUE) 之后保证重绘生效时拿到的是一整份完整数据。这个技巧能解决大部分“插入数据慢”的问题它的边界在十万行以上——那时就算关掉重绘控件的内部数据结构本身也会成为瓶颈需要走下一章的虚拟模式。6. 进阶虚拟模式扛住十万行数据单元格类型还能换控件虚拟模式是网格控件面对大数据量时的关键设计。开启后控件自身不再保存每个单元格的文本只在需要显示某一行时回调你来取数据。这样即使有百万行内存占用也只是列头加少量可见行的数据量。实现方式是从 CGridCtrl 派生一个新类重写 OnCellGetText 回调。// 自定义网格类头文件中声明 // class CBigDataGrid : public CGridCtrl { ... }; CString CBigDataGrid::OnCellGetText(int nRow, int nCol) { // 真正数据在外部容器里这里只做行号到数据的映射 CString strRet; strRet.Format(_T(%d), m_pData-GetValue(nRow, nCol)); return strRet; } // 在对话框中使用虚拟模式 m_gridCtrl.SetVirtualMode(TRUE); m_gridCtrl.SetRowCount(1000000); // 虚拟模式下行数只是一个声明不占内存 m_gridCtrl.SetColumnCount(10);开启虚拟模式后SetItemText 不再有意义因为控件已经把存储职能交还给了你只剩显示层。这会带来一个使用习惯上的转变不能再按“填充数据”的思路而要把所有数据访问改成按行号索引的随机取数。排序在这种模式下也要自己做数据层排序然后调用 SetRowCount 重置行数触发界面刷新。网格控件还能把单元格从“文本框”替换成其他编辑控件比如把某列变成下拉框这样可以省去自己处理子控件定位的麻烦。// 把第 3 列的所有单元格设为组合框类型 for (int row 1; row m_gridCtrl.GetRowCount(); row) { m_gridCtrl.SetCellType(row, 3, RUNTIME_CLASS(CGridComboBoxCell)); }SetCellType 的第三个参数传 RUNTIME_CLASS 指定单元格运行时类型。切换后该列在编辑时会自动弹出一个下拉框点击其他列又恢复普通显示子控件的创建、位置同步都是网格控件内部处理的。这一招在“状态”“优先级”这类固定枚举值字段上特别好用既约束了输入范围又不用为每一列单独挂子控件。我有一段印象很深的经历某次做一个排产模拟表一开始没用虚拟模式直接往里面灌 8 万行数据界面卡到拖不动后来切到虚拟模式再把数据层做上索引滚动流畅度完全变了一个档次。所以我现在接任何网格控件都会先问一句“这个表最大可能到多少行”超过 5 万行就直接走虚拟模式绝不抱着“以后再说”的心态留性能债。网格控件用得顺不顺很大程度上取决于你把它当“显示控件”还是“数据表格”来用——数据和界面分离边界划清楚坑会少很多。希望这篇笔记能帮到你。本文还有配套的精品资源点击获取
RELATED

相关推荐

大模型落地指南:从模型选型到Agent工程化与评测实践

大模型落地指南:从模型选型到Agent工程化与评测实践

1. 本周行业观察:大模型竞赛进入“冷静高效期”这周翻遍各家的动态,我最大的感受是:行业节奏变了。前几年大家比的是谁把模型参数堆得更高、谁先发布新版本,而到了2026年10月这个时间窗口,头部玩家明显都把精力从“造发…

📅 2026/10/16 13:29:31
C++粒子滤波实战:实时嵌入式定位的内存优化与传感器接入

C++粒子滤波实战:实时嵌入式定位的内存优化与传感器接入

简介:本资源是一份基于C实现的粒子滤波算法完整源码包,面向机器人定位、目标跟踪与传感器融合等领域的算法工程师及高年级本科生,解决非线性非高斯动态系统状态估计的实际编程落地问题。压缩包共15个文件,含7个头文件(…

📅 2026/10/16 13:29:31
MATLAB实现跳频信号调制解调:从原理到硬件部署

MATLAB实现跳频信号调制解调:从原理到硬件部署

简介:本资源是一份面向通信工程专业学生、无线通信方向初学者及MATLAB实践者的跳频通信系统仿真教学材料,聚焦于跳频信号调制与解调的核心原理与代码实现。资源提供完整的MATLAB可运行脚本(.m文件),涵盖数据生成、伪随…

📅 2026/10/16 13:29:31
MORE NEWS

更多资讯

📰

Linux审计事件实时分发:audispd原理与高可靠管道实践

1. 项目概述:audit日志的“管道工”——audispd到底在干啥你有没有遇到过这种场景:Linux系统里启用了auditd,/var/log/audit/audit.log里日志哗哗地写,但你想实时抓取其中特定事件——比如某个进程调用了execve、某用户修改了/etc…

📰

Qt调用MATLAB生成DLL实战:跨平台算法集成方案

1. 项目概述:为什么Qt要和MATLAB生成的DLL打交道在工业软件、科研工具链和嵌入式上位机开发中,我经常遇到一个现实矛盾:Qt写界面快、跨平台稳、生态成熟,但做复杂数学建模、信号处理、图像识别或控制系统仿真时,硬啃C数…

📰

LEAP-1A26发动机性能建模实战:数据准备、稳态匹配与调参避坑

简介:面向飞行模拟与航空发动机建模开发者,这份资料围绕CFM LEAP-1A26发动机及FADEC控制展开,聚焦物理/理论建模与参数分析。包内共114个文件,以Python脚本(25个py)、图表(14个png)、…

📰

Starrc寄生提取与后仿偏差归因实战指南

简介:本资源是一份面向IC后端设计工程师与高校微电子专业学习者的StarRC寄生参数提取与后仿真全流程实践指南,聚焦物理验证后关键的寄生建模与电路性能闭环验证环节。文档以SMIC 40nm工艺库为背景,系统梳理了从LVS规则修改、CCI接口配置、Cal…

📰

计算机考研复试英语口语:高频题型与答题框架全攻略

最近有好几位准备考研复试的学弟学妹来找我,问的都是同一个问题:计算机与软件工程专业的研究生复试英语口语,到底会考什么?很多人准备了厚厚一叠自我介绍,对着镜子背得滚瓜烂熟,结果我一问“你毕设用到的那…

📰

版本地狱实录:Wine、FEX-Emu、DXMT 三方版本不匹配就是一场白折腾

版本地狱实录:Wine、FEX-Emu、DXMT 三方版本不匹配就是一场白折腾 【免费下载链接】Madeira Run x86-64 Windows PC games on jailed iOS via FEX-Emu Wine DXMT 项目地址: https://gitcode.com/GitHub_Trending/mad/Madeira 在 Apple Silicon 上跑 Window…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

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

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

📞 💬