C# 具有延迟更新的visual studio图形装饰
我正在制作一个Visual Studio装饰扩展。我想更新装饰,如果没有用户输入至少2秒。所以我构建了一个worker并试图移除和添加装饰,但是VS说它无法更新,因为非ui线程调用了它。因此,我在没有线程的情况下等待,然后我的编辑器变得非常滞后(因为ui线程等待) 我想知道是否有一种方法可以用延迟更新来更新装饰。 绘制装饰是通过调用addAdorment()完成的,我找不到如何调用ui线程来绘制 下面是我的代码C# 具有延迟更新的visual studio图形装饰,c#,visual-studio,visual-studio-extensions,C#,Visual Studio,Visual Studio Extensions,我正在制作一个Visual Studio装饰扩展。我想更新装饰,如果没有用户输入至少2秒。所以我构建了一个worker并试图移除和添加装饰,但是VS说它无法更新,因为非ui线程调用了它。因此,我在没有线程的情况下等待,然后我的编辑器变得非常滞后(因为ui线程等待) 我想知道是否有一种方法可以用延迟更新来更新装饰。 绘制装饰是通过调用addAdorment()完成的,我找不到如何调用ui线程来绘制 下面是我的代码 internal async void OnLayoutChanged(ob
internal async void OnLayoutChanged(object sender, TextViewLayoutChangedEventArgs e)
{
Print("OnLayoutChanged Called");
task = Task.Factory.StartNew(() =>
{
Print("task Started");
if (e.NewSnapshot != e.OldSnapshot)
{
parseStopwatch.Restart();
shouldParse = true;
}
ParseWork(e);
});
await task;
}
private async void ParseWork(object param)
{
var e = (TextViewLayoutChangedEventArgs)param;
if (e == null)
{
shouldParse = false;
parseStopwatch.Stop();
CsharpRegionParser.ParseCs(this.view.TextSnapshot);
DrawRegionBox();
return;
}
while (shouldParse)
{
Task.Delay(10);
if ((shouldParse && parseStopwatch.ElapsedMilliseconds > 2000) || parseStopwatch.ElapsedMilliseconds > 5000)
{
break;
}
}
shouldParse = false;
parseStopwatch.Stop();
CsharpRegionParser.ParseCs(this.view.TextSnapshot);
DrawRequest(e);
return;
}
代码中使用的
Task.Delay
返回延迟时完成的任务。如果你这样称呼它,却忽略了结果,那么它并没有做你认为它做的事情。您可能想要的不是调用Task.Factory.StartNew,而是希望:
var cancellationTokenSource = new CancellationTokenSource();
Task.Delay(2000, cancellationTokenSource.Token).ContinueWith(() => DoWork(), cancellationTokenSource.Token, TaskScheduler.Current).
这意味着“启动一个等待2秒的计时器,然后在它完成后在UI线程上运行DoWork方法。如果发生更多键入,则可以调用cancellationTokenSource.Cancel(),然后再次运行
另外,我必须询问您的类型“CSharpRegionParser”“。如果您需要区域信息,并且您使用的是Visual Studio 2015,那么您可以从Roslyn获得语法树,并且您应该关注工作区更改事件,而不是挂接布局更改。你最好将你的系统构建成一对标记器/装饰管理器,因为这样写起来可能更清楚…我不清楚为什么你要在LayoutChanged中解析逻辑,因为LayoutChanged是在可视化布局过程中发生的事情,包括滚动、调整大小等。我不确定你为什么被否决,尤其是在处理扩展时,这是一个有趣的问题 因此,对于您的第一个问题:VisualStudio与WPF具有相同的要求(由于其COM依赖性而增加了一些复杂性)。当您不在主(UI)线程上时,无法更新UI元素。不幸的是,如果您直接切入,并使用WPF使用的策略来处理它,您将经历一个完全不同的问题世界(主要是死锁) 首先,介绍一下如何在VisualStudioExtensionLand中处理从后台到UI线程的切换。我发现这有助于解释 我不得不用一个昂贵的解析操作来做类似的事情。这很直截了当 我的解析器作为
IViewModelTagger
实例的一部分执行,并使用以下序列(大致):
async void
事件处理程序订阅ITextBuffer.ChangedLowPriority
事件CancellationToken.Cancel()
取消正在进行的任何解析操作。取消令牌被传递到支持它的所有东西中(在Roslyn中,它在您希望它出现的任何地方都受到支持)李>
Task.Delay(200,m_cancellationToken)
调用。基于我的打字速度和Roslyn的操作对任何昂贵的东西都有CancellationToken
重载(我的解析工作也很轻)这一事实,我选择了200毫秒。YMMVIViewModelTagger
和IWpfTextViewListener
中。它们足够轻量级,我可以跳过异步,但在非常大的类上,它们可以挂起UI
为了处理这个问题,我做了以下工作:
异步void
事件处理程序进行订阅Task.Run()
首先执行昂贵的操作,防止UI被阻塞等待ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync()
以获取UI线程Task.Run
works以及ThreadHelper.JoinableTaskFactory.Run
)。在我的回答前面链接的Andrew Arnott帖子解释了所有的选择。您需要充分理解这一点,因为根据任务的不同,有理由使用某些方法而不是其他方法
希望有帮助 在后台任务中等待,调用主线程进行更新我尝试了,但失败了。我需要的是一个调度员。