VSTO自定义任务窗格在多DPI多显示器系统中重复显示问题
解决VSTO自定义任务窗格在多DPI显示器下重复绘制的问题
我之前也踩过VSTO开发Office加载项时的这个多DPI坑,那种任务窗格内容叠两层、只有小版本能响应操作的情况确实让人头疼。结合我当时的排查和解决经验,给你几个实际有效的方案:
从应用清单入手,强制进程级Per-Monitor DPI感知
光在代码里调设置经常不管用,得在应用程序清单里明确声明DPI感知模式。右键你的VSTO项目添加「应用程序清单文件」,找到<application>节点,替换成下面的配置:<application xmlns="urn:schemas-microsoft-com:asm.v3"> <windowsSettings> <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True/PM</dpiAware> <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness> </windowsSettings> </application>注意Office 2016及以上版本对PerMonitorV2支持更好,旧版本可以只保留
PerMonitor。调整任务窗格初始化时机,手动处理DPI变化事件
别在ThisAddIn_Startup里直接创建任务窗格,等Office完全加载后再初始化更稳妥。另外,通过消息过滤器监听DPI变化,强制任务窗格重新布局:private void ThisAddIn_Startup(object sender, System.EventArgs e) { // 用Dispatcher延迟初始化,避免Office加载未完成时的冲突 System.Windows.Threading.Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() => { SetupCustomTaskPane(); })); } private Microsoft.Office.Tools.CustomTaskPane _customTaskPane; private void SetupCustomTaskPane() { var taskPaneControl = new MyCustomUserControl(); _customTaskPane = CustomTaskPanes.Add(taskPaneControl, "我的自定义任务窗格"); _customTaskPane.Visible = true; // 添加消息过滤器处理DPI变化 System.Windows.Forms.Application.AddMessageFilter(new DpiChangeMessageFilter(_customTaskPane)); } // 自定义消息过滤器捕获WM_DPICHANGED消息 private class DpiChangeMessageFilter : System.Windows.Forms.IMessageFilter { private readonly Microsoft.Office.Tools.CustomTaskPane _targetPane; public DpiChangeMessageFilter(Microsoft.Office.Tools.CustomTaskPane pane) => _targetPane = pane; public bool PreFilterMessage(ref System.Windows.Forms.Message m) { const int WM_DPICHANGED = 0x02E0; if (m.Msg == WM_DPICHANGED) { // 强制控件重新布局并刷新 _targetPane.Control.PerformLayout(); _targetPane.Control.Invalidate(); } return false; } }禁用AutoScaleMode,手动实现DPI缩放逻辑
既然AutoScaleMode的所有选项都没效果,不如直接设为None,然后根据当前显示器的DPI手动缩放控件和字体:public MyCustomUserControl() { InitializeComponent(); AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; AdjustControlsForCurrentDpi(); } private void AdjustControlsForCurrentDpi() { using (var graphics = CreateGraphics()) { var dpiScaleFactor = graphics.DpiX / 96f; // 96是标准DPI基准值 // 缩放控件整体尺寸 Size = new System.Drawing.Size((int)(Size.Width * dpiScaleFactor), (int)(Size.Height * dpiScaleFactor)); // 遍历子控件缩放字体 foreach (var ctrl in Controls) { if (ctrl is System.Windows.Forms.Control control) { control.Font = new System.Drawing.Font(control.Font.FontFamily, control.Font.Size * dpiScaleFactor); } } } } // 窗口移动到不同显示器时重新调整 private void MyCustomUserControl_Resize(object sender, System.EventArgs e) { AdjustControlsForCurrentDpi(); }检查Office程序的DPI兼容性设置
有时候系统的高DPI缩放替代会和VSTO的逻辑冲突。右键Office主程序(比如Excel.exe)→ 属性 → 兼容性,取消勾选「高DPI缩放替代」,或者把缩放行为改成「应用程序」模式试试。
内容的提问来源于stack exchange,提问作者Daniel Hilgarth
相关产品推荐
相关产品推荐

