尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

基于VC++与BCGControlBar的MFC现代化界面开发实战指南

基于VC++与BCGControlBar的MFC现代化界面开发实战指南 1. 项目概述与核心价值在桌面应用开发领域尤其是基于微软技术栈的工业、医疗、金融等专业软件中一个稳定、高效且美观的用户界面UI往往是产品成功的关键。很多开发者特别是从C或MFCMicrosoft Foundation Classes技术栈入行的朋友可能都有过这样的经历面对一个功能强大但界面陈旧的遗留系统想要进行现代化改造却苦于MFC原生控件库的简陋和开发效率的低下。手动绘制每一个按钮、定制每一个列表控件不仅耗时耗力而且难以保证跨窗口、跨DPI的视觉一致性更别提实现类似Visual Studio、Office那样的标签式文档界面、可停靠面板、Ribbon工具栏等高级特性了。这正是“基于VC与BCGControlBar的精美用户界面开发实战”这个项目标题所直击的痛点。VC作为微软经典的C开发环境承载了无数大型、高性能桌面应用的开发任务其稳定性和对Windows系统的深度集成能力无可替代。而BCGControlBarBusiness Components Gallery ControlBar则是一个业界知名的、专门为MFC应用程序提供现代化UI组件的商业库。它不是一个独立的框架而是MFC的“超级增强包”能将你的传统MFC程序从“Windows 95风格”瞬间升级到“Visual Studio 2019风格”。这个实战项目的核心价值在于它提供了一条清晰、可行的路径让我们在不抛弃成熟的VC/MFC技术栈和现有业务逻辑代码的前提下以相对较低的迁移成本和较高的开发效率打造出具有专业质感的现代化桌面应用界面。这不仅仅是“换皮肤”而是从框架层面引入了一套完整的、经过商业验证的UI解决方案涵盖了从基础控件美化、高级布局管理到专业主题支持的全方位需求。对于需要维护或升级既有MFC项目同时又面临用户对界面体验要求越来越高的开发团队来说掌握这套技术组合无疑是一把打开局面、提升产品竞争力的金钥匙。2. BCGControlBar核心组件与设计思路解析2.1 BCGControlBar是什么为什么选择它BCGControlBar本质上是一个C类库它通过继承和扩展MFC原有的控件类如CButton、CListCtrl和框架类如CFrameWnd、CControlBar为它们注入了全新的外观和行为。你可以把它理解为一套强大的“MFC UI主题引擎”和“高级控件工具箱”。它的设计哲学是“无缝集成”这意味着你不需要重写你的应用架构只需要将项目中的MFC基类指针替换为BCG提供的派生类并在初始化时调用一些库函数就能立刻看到界面效果的巨大变化。选择BCGControlBar而非其他UI框架如Qt、WPF通常基于以下几个关键考量遗产代码兼容性这是最重要的原因。对于数百万行乃至上千万行代码的成熟MFC应用推倒重来使用Qt或WPF的成本和风险是巨大的。BCGControlBar允许渐进式改造可以逐个模块、逐个对话框地进行UI升级业务逻辑代码基本不受影响。性能与资源占用BCGControlBar基于原生Win32/MFC没有额外的运行时或虚拟机开销执行效率高内存占用相对较小非常适合对性能有苛刻要求的工业控制、数据采集等应用。与Visual Studio生态深度集成BCGControlBar提供了Visual Studio的插件支持资源编辑器可视化设计Ribbon、工具栏等大大提升了开发效率。其API设计也与MFC一脉相承MFC开发者学习曲线平缓。功能全面性与专业性它提供的不仅仅是美化控件。其核心价值在于一系列MFC原生不具备的高级UI框架组件这些组件构成了现代专业软件界面的骨架。2.2 核心组件架构与选型逻辑BCGControlBar的组件体系非常庞大但在实战中我们通常围绕几个核心模块来构建界面。理解每个模块的职责和选用场景是设计好界面的第一步。2.2.1 框架与布局管理CBCGPFrameWnd,CBCGPMDIFrameWnd,CBCGPDockingControlBar这是整个应用的“骨架”。BCG提供了对应单文档SDI、多文档MDI的框架窗口类它们替代了MFC的CFrameWnd和CMDIFrameWnd。这些新框架窗口内置了对可停靠面板Docking Pane、自动隐藏面板、标签式文档界面Tabbed Document Interface的原生支持。选型逻辑如果你的应用需要类似VS或Eclipse那样灵活的工作区布局让用户能自由拖拽、组合、停靠各种功能面板如属性窗口、解决方案资源管理器、输出窗口那么必须使用CBCGPMDIFrameWnd对于MDI应用并搭配CBCGPDockingControlBar。这是从“传统窗口”迈向“现代化IDE风格”界面的关键一步。2.2.2 功能区界面Ribbon UI -CBCGPRibbonBarRibbon界面自Office 2007以来已成为专业软件的标准。BCG的Ribbon实现非常完整支持快速访问工具栏、标签页、上下文标签、Backstage视图文件菜单等。选型逻辑当你的应用命令众多需要良好的组织性和可发现性时Ribbon是比传统菜单工具栏更好的选择。它尤其适合功能模块清晰、操作流程化的软件如设计类、办公类应用。需要注意的是Ribbon会占用较大的垂直空间在屏幕高度有限的场景下需谨慎评估。2.2.3 控件与对话框美化CBCGPButton,CBCGPListCtrl,CBCGPDialog这是最直接的“颜值提升”部分。BCG为几乎所有标准MFC控件提供了美化版本支持圆角、渐变、阴影、各种状态悬停、按下、禁用的视觉反馈。选型逻辑这部分几乎是必选项。通过将资源编辑器里创建的控件关联变量类型从CButton改为CBCGPButton从CListCtrl改为CBCGPListCtrl并将对话框基类从CDialog改为CBCGPDialog你的整个对话框就会自动获得统一的现代化风格。这是投入产出比最高的改造方式。2.2.4 图表与数据可视化CBCGPChartBCGControlBar Professional版本内置了强大的图表控件支持折线图、柱状图、饼图、金融图表等多种类型且样式可高度定制。选型逻辑如果你的应用有数据展示、报表生成的需求使用内置的CBCGPChart可以避免引入第三方图表库如TeeChart带来的依赖和集成复杂度并能保证与整体UI风格的一致。2.2.5 属性网格CBCGPPropList用于创建类似Visual Studio属性窗口那样的分类属性编辑器支持各种数据类型文本、数字、颜色、下拉列表、文件路径等的编辑。选型逻辑在开发工具、配置软件或任何需要让用户编辑复杂对象参数的场景中属性网格是极佳的选择。它比一堆零散的编辑框更清晰、更专业。实操心得组件选型的“二八定律”在实际项目中不必追求一次性用上BCG的所有功能。建议采用“核心框架先行控件美化跟进高级功能按需”的策略。首先用CBCGPMDIFrameWnd和停靠面板搭建好主界面骨架然后用美化控件更新主要对话框。Ribbon和图表等功能可以在后续迭代中逐步加入。这样既能快速见到界面改观又能控制项目风险。3. 开发环境搭建与项目初始化实战3.1 环境准备与BCG库集成假设我们使用Visual Studio 2019进行开发。首先你需要从BCGSoft官网获取BCGControlBar Professional的安装包或源代码。商业项目需要购买许可证但官网也提供功能完整的评估版供试用。步骤1安装与目录结构运行安装程序将BCG库安装到本地例如C:\BCGControlBarPro。安装后的目录通常包含BCGCBPro核心源代码和头文件。Redist编译好的静态库.lib和动态库.dll针对不同VS版本和字符集Unicode/ANSI以及调试Debug/发布Release版本。Samples大量的示例程序这是最好的学习资料。Help离线帮助文档。步骤2在Visual Studio中配置项目创建或打开项目创建一个新的MFC应用程序项目或者打开你现有的MFC项目。设置包含目录在项目属性 - C/C - 常规 - 附加包含目录中添加BCG的头文件路径如C:\BCGControlBarPro\BCGCBPro。设置库目录在项目属性 - 链接器 - 常规 - 附加库目录中添加BCG的库文件路径如C:\BCGControlBarPro\Redist\2019\X64根据你的VS版本和平台选择。添加预处理器定义在项目属性 - C/C - 预处理器 - 预处理器定义中添加BCGCBPRO_INCLUDE_。这个宏会告诉BCG头文件激活商业版功能。链接静态库在项目属性 - 链接器 - 输入 - 附加依赖项中添加对应的静态库文件名。例如对于64位Release版本的Unicode项目你需要添加BCGCBPRO2490U.lib版本号2490可能因版本而异U代表Unicode。更稳妥的做法是使用#pragma comment(lib, ...)在stdafx.h中链接。复制运行时DLL如果使用动态库如果你链接的是动态库.dll需要将对应版本的BCGCBPRO2490U.dll复制到你的可执行文件输出目录。步骤3修改应用程序类初始化这是集成BCG的关键一步。在你的MFC应用类通常是CXXXApp派生自CWinApp的InitInstance()函数开头添加BCG库的初始化代码。BOOL CMyApp::InitInstance() { // 1. 初始化BCG库必须在所有其他初始化之前 CBCGPVisualManager::SetDefaultManager(RUNTIME_CLASS(CBCGPVisualManagerVS2019)); // 设置VS2019主题 CBCGPDockManager::SetDockingMode(DT_SMART); // 设置智能停靠模式 CBCGPPopupMenu::SetAnimationType(CBCGPPopupMenu::SLIDE); // 设置菜单动画 // 2. 调用BCG的全局初始化函数 if (!CBCGPWorkspace::InitWorkspace()) { return FALSE; } // 3. 原有的MFC初始化代码... CWinApp::InitInstance(); // ... 其他初始化 // 4. 创建主窗口时使用BCG的框架窗口类 CMainFrame* pMainFrame new CMainFrame; // CMainFrame 现在应派生自 CBCGPMDIFrameWnd if (!pMainFrame-LoadFrame(IDR_MAINFRAME)) { return FALSE; } m_pMainWnd pMainFrame; pMainFrame-ShowWindow(m_nCmdShow); pMainFrame-UpdateWindow(); return TRUE; }3.2 主框架窗口改造实战现在我们需要将原有的主框架窗口类改为从BCG的类派生。修改继承关系在MainFrm.h中将CMainFrame的父类从CMDIFrameWnd或CFrameWnd改为CBCGPMDIFrameWnd或CBCGPFrameWnd。// 修改前 class CMainFrame : public CMDIFrameWnd // 修改后 class CMainFrame : public CBCGPMDIFrameWnd修改消息映射在MainFrm.cpp中将BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd)中的父类也相应修改。BEGIN_MESSAGE_MAP(CMainFrame, CBCGPMDIFrameWnd)启用可视化主题在CMainFrame::OnCreate函数中创建完基础窗口后调用函数启用主题。int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CBCGPMDIFrameWnd::OnCreate(lpCreateStruct) -1) return -1; // 启用Windows主题支持对于XP及以上 CBCGPVisualManager::SetDefaultManager(RUNTIME_CLASS(CBCGPVisualManagerVS2019)); // 创建并初始化Ribbon Bar如果使用 if (!CreateRibbonBar()) { TRACE0(Failed to create ribbon bar\n); return -1; } // 启用停靠功能 EnableDocking(CBRS_ALIGN_ANY); // 启用自动隐藏面板功能 EnableAutoHidePanes(CBRS_ALIGN_ANY); // ... 创建工具栏、状态栏等原有代码 return 0; }创建可停靠面板创建一个新的类例如CMyDockingPane继承自CBCGPDockingControlBar。在这个类里你可以像在普通对话框上一样放置控件。// 在CMainFrame的某个地方如OnCreate创建并停靠面板 CMyDockingPane* pPane new CMyDockingPane; if (!pPane-Create(_T(我的面板), this, CRect(0,0,300,400), TRUE, ID_VIEW_MYPANE, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CBRS_LEFT | CBRS_FLOAT_MULTI)) { AfxMessageBox(_T(Failed to create docking pane)); delete pPane; } else { pPane-EnableDocking(CBRS_ALIGN_ANY); DockPane(pPane); }注意事项资源ID冲突BCG库内部使用了大量的资源ID位图、字符串等。在集成时务必检查你的项目资源ID是否与BCG的ID范围冲突通常BCG的资源ID从某个大数开始如0xF000。避免冲突的最好方法是在创建新资源时使用Visual Studio资源编辑器提供的“新建ID”功能或者在自己的ID定义文件中规划好范围。4. 核心界面元素实现与深度定制4.1 Ribbon功能区设计与开发Ribbon是现代化界面的标志。在BCG中创建Ribbon通常通过资源编辑器和代码结合的方式。步骤1设计Ribbon资源在VS资源视图中右键点击项目资源 - 添加资源 - 选择“BCGSoft Ribbon (XML)”。这会生成一个XML文件。更直观的方式是使用BCG提供的“Ribbon Designer”可视化编辑器如果已安装插件。你可以像画对话框一样拖拽按钮、组合框、颜色选择器等元素到Ribbon标签页上。设计完成后会生成一个IDR_RIBBON类型的资源。你需要为每个按钮、命令指定唯一的ID。步骤2在代码中创建并加载Ribbon在CMainFrame::CreateRibbonBar()函数中该函数在OnCreate中被调用BOOL CMainFrame::CreateRibbonBar() { if (!m_wndRibbonBar.Create(this)) { return FALSE; } // 1. 设置Ribbon为应用主菜单 m_wndRibbonBar.SetApplicationButton(ID_APPLICATION_BUTTON, _T(文件)); // 2. 从资源文件加载Ribbon布局 if (!m_wndRibbonBar.LoadFromResource(IDR_RIBBON)) { return FALSE; } // 3. 添加快速访问工具栏项 m_wndRibbonBar.AddToQuickAccessToolBar(ID_FILE_SAVE); m_wndRibbonBar.AddToQuickAccessToolBar(ID_EDIT_UNDO); // 4. 将Ribbon栏停靠在框架顶部 m_wndRibbonBar.EnableDocking(CBRS_ALIGN_TOP); DockPane(m_wndRibbonBar); return TRUE; }步骤3处理Ribbon命令Ribbon按钮的命令处理与普通菜单/工具栏按钮完全一样。你只需要在框架窗口或视图类中为对应的命令ID添加ON_COMMAND和ON_UPDATE_COMMAND_UI消息映射和处理函数即可。BCG会自动处理按钮的显示状态启用/禁用、按下/弹起。4.2 美化控件与对话框的实战技巧将普通对话框升级为BCG风格对话框效果立竿见影。更改对话框基类在对话框类的头文件和实现文件中将基类从CDialog改为CBCGPDialog。// .h 文件 class CMySettingsDlg : public CBCGPDialog // .cpp 文件 BEGIN_MESSAGE_MAP(CMySettingsDlg, CBCGPDialog)更改控件关联变量类型这是最繁琐但必要的一步。对于每个你想美化的控件需要将其关联的成员变量类型从MFC类改为BCG类。CEdit-CBCGPEditCButton-CBCGPButtonCComboBox-CBCGPComboBoxCListCtrl-CBCGPListCtrlCStatic-CBCGPStatic...注意你需要手动在头文件中修改变量声明并在DoDataExchange函数中将DDX_Control调用改为对应的BCG版本如果存在或者直接删除DDX_Control改为在OnInitDialog中手动子类化SubclassWindow。// 在对话框头文件中 CBCGPButton m_btnOK; CBCGPListCtrl m_wndList; // 在OnInitDialog中手动关联如果DDX不支持 BOOL CMySettingsDlg::OnInitDialog() { CBCGPDialog::OnInitDialog(); // 假设IDC_BUTTON_OK是对话框上的按钮ID m_btnOK.SubclassDlgItem(IDC_BUTTON_OK, this); m_wndList.SubclassDlgItem(IDC_LIST_DATA, this); // 进一步设置列表样式 m_wndList.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES); // ... 插入列等操作 return TRUE; }使用BCG控件特有的属性BCG控件提供了丰富的扩展样式和方法。例如你可以轻松设置按钮的图标、工具提示、鼠标悬停效果或者为列表控件启用分组、复选框、平铺视图等。4.3 高级主题与视觉管理器定制BCG的强大之处在于其可换肤能力。通过CBCGPVisualManager派生类你可以全局改变整个应用程序的配色方案和控件绘制风格。切换内置主题一行代码即可切换整个应用的主题。// 切换到Office 2007 Blue主题 CBCGPVisualManager::SetDefaultManager(RUNTIME_CLASS(CBCGPVisualManager2007)); CBCGPVisualManager::GetInstance()-SetStyle(BCGP_VISUAL_MANAGER_2007_BLUE); // 切换到VS 2012 Light主题 CBCGPVisualManager::SetDefaultManager(RUNTIME_CLASS(CBCGPVisualManagerVS2012)); CBCGPVisualManager::GetInstance()-SetStyle(BCGP_VISUAL_MANAGER_VS2012_LIGHT);你可以在应用启动时设置也可以提供选项让用户动态切换。自定义视觉管理器如果你需要品牌化的配色可以创建自己的视觉管理器类。class CMyCustomVisualManager : public CBCGPVisualManagerVS2019 { public: virtual void OnFillBarBackground(CDC* pDC, CBCGPBaseControlBar* pBar, ...) { // 自定义工具栏背景填充逻辑 if (pBar-IsKindOf(RUNTIME_CLASS(CBCGPRibbonBar))) { // 为Ribbon栏绘制自定义渐变背景 CBCGPDrawManager dm(*pDC); dm.FillGradient(rect, RGB(10, 40, 100), RGB(60, 120, 200), TRUE); } else { CBCGPVisualManagerVS2019::OnFillBarBackground(pDC, pBar, ...); } } virtual COLORREF GetToolbarButtonTextColor(CBCGPToolbarButton* pButton, ...) { // 自定义工具栏按钮文字颜色 return RGB(255, 255, 255); // 白色文字 } // ... 重写其他虚函数以定制各种元素的绘制 };然后在应用初始化时使用你的自定义管理器CBCGPVisualManager::SetDefaultManager(RUNTIME_CLASS(CMyCustomVisualManager));。实操心得控件子类化的“坑”与技巧将大量现有对话框中的控件变量类型逐个修改非常耗时。一个高效的技巧是不必一次性全部修改。可以先只修改对话框基类为CBCGPDialog这样对话框背景、边框等会先得到美化。然后只对当前重点修改或新加的控件进行BCG子类化。对于旧的、不常访问的控件可以暂时保持原样BCG对话框对标准MFC控件也有一定的兼容渲染。此外可以编写一个辅助函数利用CWnd::SubclassDlgItem和运行时类型信息RTTI批量对对话框内指定类型的控件进行自动子类化但这需要更精细的控制。5. 性能优化、调试与常见问题排查5.1 性能考量与优化建议引入一个功能丰富的UI库性能是必须关注的点。静态链接 vs 动态链接静态链接将BCG代码编译进你的EXE。生成文件大但部署简单一个EXE搞定。适合中小型项目或对启动速度有要求的项目。动态链接使用BCG的DLL。EXE文件小多个应用可共享DLL节省内存但部署时需要携带DLL。适合大型套件或需要频繁更新UI库的情况。建议在开发阶段使用静态链接的Debug版便于调试发布时根据需求选择。资源管理BCG的Ribbon、工具栏等使用大量位图资源。务必确保使用合适尺寸的位图如32位带Alpha通道的PNG避免过大。在不需要时及时销毁自定义创建的图像列表CImageList或GDI对象。利用BCG提供的CBCGPToolBarImages类来高效管理和绘制工具栏图像。UI更新优化避免在频繁调用的函数如OnTimer或大数据循环中进行耗时的UI操作如清空并重新填充一个巨大的列表控件。对于频繁更新的数据考虑使用虚拟列表CBCGPListCtrl支持LVS_OWNERDATA风格只在需要时请求数据。禁用不必要的特效如果对性能极其敏感可以在初始化时关闭一些动画和视觉效果。CBCGPVisualManager::GetInstance()-SetStyle(BCGP_VISUAL_MANAGER_STYLE_DEFAULT); // 使用最简洁的风格 CBCGPPopupMenu::SetAnimationType(CBCGPPopupMenu::NO_ANIMATION); // 禁用菜单动画5.2 典型问题与调试技巧实录即使按照指南操作集成过程中也难免会遇到问题。下面是一些常见“坑”及其解决方案。问题1编译时出现“未解析的外部符号”链接错误。原因这是最常见的问题意味着链接器找不到BCG库的实现。可能的原因有附加依赖项中库文件名写错或版本不匹配。库目录路径配置错误。项目平台Win32/x64与所链接的库平台不一致。使用了Debug/Release不匹配的库。排查检查项目属性中的“平台工具集”和“目标平台”是否与BCG库的编译平台一致。打开BCG的Redist目录仔细核对库文件名。通常命名规则为BCGCBPRO{版本号}{字符集}.lib例如BCGCBPRO2490U.lib表示版本24.90Unicode版本。在“附加依赖项”中使用$(Configuration)宏来区分Debug和Release。例如BCGCBPRO2490U$(ConfigurationName).lib如果库文件确实按此命名。最根本的方法将BCG源代码BCGCBPro目录加入你的项目解决方案直接编译源代码。这样可以确保百分百的兼容性也便于调试和定制但会显著增加编译时间。问题2程序运行时崩溃错误发生在BCG库内部。原因通常是资源管理不当或对象生命周期问题。排查检查资源ID冲突这是导致随机崩溃的元凶之一。确保你的资源ID特别是位图、字符串没有与BCG内部使用的ID范围重叠。BCG通常使用0xF000以上的ID。检查窗口对象过早销毁确保BCG控件如CBCGPDockingControlBar的窗口在父窗口销毁前不会被意外销毁。使用调试器查看调用堆栈找到崩溃点的代码。确认运行时库一致性确保你的项目和BCG库使用相同版本的C运行时库如/MD或/MT。在项目属性 - C/C - 代码生成 - 运行时库中设置。问题3界面显示异常比如黑色方块、控件错位。原因主题管理器未正确初始化或控件子类化失败。排查确保CBCGPVisualManager::SetDefaultManager(...)在创建任何UI元素之前被调用最好就在CWinApp::InitInstance()的最开头。对于显示为黑色的控件检查其关联的BCG变量是否成功通过SubclassDlgItem或DDX_Control与对话框上的控件窗口关联。可以在OnInitDialog中调用GetSafeHwnd()检查窗口句柄是否有效。检查是否在非UI线程中直接操作了UI控件。所有UI更新都应在主线程UI线程中进行可以使用PostMessage或Invoke等方式跨线程通信。问题4高DPI显示器上界面模糊或布局错乱。原因MFC应用对高DPI支持先天不足BCG库在一定程度上改善了这一点但仍需正确配置。解决方案在应用程序清单文件.manifest中声明DPI感知。最简单的方法是在stdafx.h中添加#pragma comment(linker,/manifestdependency:\typewin32 nameMicrosoft.Windows.Common-Controls version6.0.0.0 processorArchitecture* publicKeyToken6595b64144ccf1df language*\)并确保你的EXE嵌入了一个清单指定dpiAwaretrue/dpiAware。BCGControlBar Pro版本提供了自动DPI缩放功能。确保你使用的版本支持该功能并在初始化时启用CBCGPVisualManager::GetInstance()-EnableDPIAwareness();对于自定义绘制的部分需要使用CBCGPGraphicsManagerGDI封装进行与DPI无关的绘图而不是直接使用GDI的CDC。问题5键盘快捷键Accelerator失效。原因当使用Ribbon后传统的加速器表Accelerator Table可能无法正常工作因为Ribbon框架有自己的快捷键处理逻辑。解决方案将快捷键定义在Ribbon按钮的命令ID上。BCG会自动处理这些快捷键的显示在按钮提示中和响应。如果仍需使用独立的加速器表需要在主框架窗口的PreTranslateMessage函数中优先调用Ribbon栏的翻译函数。BOOL CMainFrame::PreTranslateMessage(MSG* pMsg) { if (m_wndRibbonBar.PreTranslateMessage(pMsg)) { return TRUE; } // 然后再尝试传统的加速器表 if (CMDIFrameWnd::PreTranslateMessage(pMsg)) { return TRUE; } return FALSE; }调试心得利用BCG示例程序当你遇到一个BCG控件不知如何使用或行为不符合预期时第一反应不应该是去网上漫无目的地搜索而是去研究BCG自带的示例程序。在Samples目录下有覆盖几乎所有功能的示例。找到对应的示例运行它观察效果然后对照它的源代码学习。这是最直接、最准确的学习方式。例如想知道如何实现一个带有自定义绘制的按钮就去BCGControlsSample或RibbonGadgetsSample里找答案。
返回列表