You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何强制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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 14:57:11