如何强制Visual Studio更新UI?VS扩展开发UI更新问题
Visual Studio扩展UI更新问题
问题描述
我正在开发一款Visual Studio扩展,调用ToggleEnabled()函数时会触发OnToggleEnabled事件,进而执行OnToggleEnabledHandler。但在private void OnToggleEnabledHandler()里尝试的几种WPF无效化方法都没用,UI并未得到更新。我希望触发LayoutChangedHandler函数,请问如何强制Visual Studio更新其UI?
流程说明
- 按下VS快捷键会触发
Execute(AsyncPackage package)函数,进而调用ToggleEnabled()函数:
public class ImageAdornmentManager : ITagger<ErrorTag>, IDisposable { private readonly IAdornmentLayer _layer; private readonly IWpfTextView _view; private readonly VariableExpander _variableExpander; private string _contentTypeName; public static bool Enabled { get; set; } public static event Action OnToggleEnabled; static ImageAdornmentManager() { Enabled = true; } public static void ToggleEnabled() { ThreadHelper.ThrowIfNotOnUIThread(); Enabled = !Enabled; UIMessage.Show($"Comment: {Enabled}. Scroll editor window to update."); OnToggleEnabled?.Invoke(); } public ImageAdornmentManager(IWpfTextView view) { _view = view; _layer = view.GetAdornmentLayer("ImageCommentLayer"); _view.LayoutChanged += LayoutChangedHandler; _contentTypeName = view.TextBuffer.ContentType.TypeName; _view.TextBuffer.ContentTypeChanged += contentTypeChangedHandler; OnToggleEnabled += OnToggleEnabledHandler; } private void OnToggleEnabledHandler() { Console.Write("OnToggleEnabledHandler"); _view.VisualElement.InvalidateMeasure(); _view.VisualElement.InvalidateArrange(); _view.VisualElement.InvalidateVisual(); _view.LayoutChanged += (sender, e) => { }; } }
解决方案
1. 直接触发LayoutChangedHandler
最简单的方式是直接调用目标处理函数,传入模拟的布局变化参数:
private void OnToggleEnabledHandler() { Console.Write("OnToggleEnabledHandler"); // 先清理现有装饰,避免重复渲染 _layer.RemoveAllAdornments(); // 手动触发布局处理逻辑 var layoutArgs = new TextViewLayoutChangedEventArgs(_view.Layout, _view.Layout); LayoutChangedHandler(_view, layoutArgs); }
2. 强制编辑器刷新视口
通过IEditorOperations接口触发编辑器视口刷新,间接触发LayoutChanged事件:
using Microsoft.VisualStudio.Text.Operations; private void OnToggleEnabledHandler() { Console.Write("OnToggleEnabledHandler"); _layer.RemoveAllAdornments(); // 获取编辑器操作服务 var editorOps = _view.GetService<IEditorOperations>(); // 刷新视口触发布局更新 editorOps?.RefreshViewport(); }
3. 模拟滚动操作触发布局
如果上述方法无效,可以模拟编辑器滚动操作,强制VS更新布局:
private void OnToggleEnabledHandler() { Console.Write("OnToggleEnabledHandler"); _layer.RemoveAllAdornments(); // 将当前光标所在行滚动到视口顶部,触发布局变化 _view.DisplayTextLineContainingBufferPosition( _view.Caret.Position.BufferPosition, 0.0, ViewRelativePosition.Top ); }
4. 确保操作在UI线程执行
虽然ToggleEnabled已检查线程,但如果事件触发在非UI线程,需手动切换:
private void OnToggleEnabledHandler() { ThreadHelper.JoinableTaskFactory.Run(async () => { await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); Console.Write("OnToggleEnabledHandler"); _layer.RemoveAllAdornments(); LayoutChangedHandler(_view, new TextViewLayoutChangedEventArgs(_view.Layout, _view.Layout)); }); }
说明
之前调用InvalidateMeasure/InvalidateArrange/InvalidateVisual无效,是因为Visual Studio编辑器有独立的布局更新逻辑,直接操作WPF元素的无效化方法无法触发装饰层的更新,必须通过编辑器专属API或直接调用目标处理函数来实现。
内容的提问来源于stack exchange,提问作者Cesar
相关产品推荐
相关产品推荐

