GetFourthColor频繁触发导致性能下降,如何使其仅在点击toggle按钮时执行?
解决方案
问题根源
你遇到的频繁触发问题核心来自两个错误用法:
- 不应该在
get*系列回调(GetPressedRestricted、GetFourthColor都属于这类)中调用InvalidateControl方法。Office Ribbon的机制是:只要调用Invalidate/InvalidateControl,就会触发对应控件所有get前缀的回调,你在回调内又触发刷新,直接形成了无限循环调用链,导致方法反复执行。 - 没有对回调返回值做缓存,每次触发都要执行读注册表、生成位图、业务逻辑判断等重操作,放大了性能损耗。
修复步骤
- 将所有
Invalidate相关调用从get*回调中移除,仅在需要变更状态的场景(比如点击按钮、切换文档、配置变更)下才调用刷新。 - 为
get回调的返回值添加缓存,只有状态真的发生变更时才重新计算返回值,平时直接返回缓存结果。 - 控制
Invalidate的调用范围,仅刷新真正需要更新的控件,不要调用全局ribbon.Invalidate()。
修复后代码示例
首先新增缓存字段
// 缓存GetFourthColor返回的位图 private Bitmap _cachedFourthColor; // 缓存GetPressedRestricted返回的选中状态 private bool? _cachedPressedRestricted; // 标记状态是否过期,需要重新计算缓存 private bool _isStateExpired = false;
调整GetFourthColor方法
public Bitmap GetFourthColor(IRibbonControl control) { // 仅缓存为空或者状态过期时才重新生成位图 if (_cachedFourthColor == null || _isStateExpired) { // 注意:如果旧位图存在需要先释放,避免内存泄漏 _cachedFourthColor?.Dispose(); _cachedFourthColor = ColorHelper.GetBitmapColor(RegeditHelper.GetClassficationTag(4)); } return _cachedFourthColor; }
调整GetPressedRestricted方法
public bool GetPressedRestricted(IRibbonControl control) { // 移除所有Invalidate相关调用 if (_cachedPressedRestricted == null || _isStateExpired) { if (durum) { _cachedPressedRestricted = MetaDataControl(RegeditHelper.GetClassficationTag(4)); } else { _cachedPressedRestricted = false; } } return _cachedPressedRestricted.Value; }
调整点击事件PublicRestricted
public void PublicRestricted(IRibbonControl control, bool pressed) { documentService.SetActiveBook(Globals.ThisAddIn.Application.ActivePresentation); documentService.AddorUpdateWorkbookProperty("Tag", RegeditHelper.GetClassficationTag(4)); // 标记状态过期,下次回调会重新生成缓存 _isStateExpired = true; // 仅在点击时刷新需要更新状态的其他控件 ribbon.InvalidateControl("textButtonpublic"); ribbon.InvalidateControl("textButton2internal"); ribbon.InvalidateControl("textButton2confidential"); // 刷新当前按钮本身 ribbon.InvalidateControl(control.Id); // 重置过期标记 _isStateExpired = false; RibbonInvalidateAndDeleteControl(control, pressed); }
额外补充
如果存在其他需要更新按钮状态的场景(比如切换当前打开的文档、注册表配置变更),只需要在对应事件触发时,先把_isStateExpired设为true,再调用对应控件的InvalidateControl即可,不需要修改回调逻辑。
内容的提问来源于stack exchange,提问作者Mehmet Ceylan
相关产品推荐
相关产品推荐

