如果单击一个按钮,我的功能大约需要5秒钟才能完成。如果单击该按钮,我想显示某种通知,以指示正在处理按钮,如
<Button Click="OnButtonClick" Content="Process Input" />
<Border x:Name="NotificationBorder" Opacity="0" IsHitTestVisible="False"
Width="500" Height="100" Background="White">
<TextBlock Text="Your input is being processed" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
在按钮上的代码隐藏中点击:
private void OnButtonClick(Object sender, RoutedEventArgs e)
{
DoubleAnimation da = new DoubleAnimation
{
From = 5,
To = 0,
Duration = TimeSpan.FromSeconds(2.5),
};
// Making border visible in hopes that it's drawn before animation kicks in
NotificationBorder.Opacity = 1;
da.Completed += (o, args) => NotificationBorder.Opacity = 0;
NotificationBorder.UpdateLayout(); //Doesn't do anything
UpdateLayout(); // Doesn't do anything
NotificationBorder.BeginAnimation(OpacityProperty, da);
// Simulate calculationheavy functioncall
Thread.Sleep(5000);
}
某种程度上UpdateLayout()
的渲染速度不够快,通知只会在Thread.Sleep
的5秒结束后显示。
Dispatcher.Invoke((Action)(() => NotificationBorder.Opacity = 1), DispatcherPriority.Render);
也不起作用。
此外,我不能让Thread.Sleep
在一个单独的工作线程中运行 - 在实际应用程序中,它需要从Dispatcher拥有的对象读取数据并(重新)构建UI的一部分。
在调用Thread.Sleep()
之前有没有办法让它可见?
答案 0 :(得分:1)
问题是主线程(UI线程)上的任何长活动都会导致UI冻结,因此您将看不到任何动画。
您需要在单独的线程中进行计算。我需要访问Dispatcher拥有的对象,您可以使用uiObject.Dispatcher.BeginInvoke或Invoke。 BackgroundWorker可以帮助您执行一些长计算并订阅事件Completed,这将由BackgroundWorker自动在UI线程上触发。
如果您对UI冻结没问题,请使用Dispatcher.BeginInvoke(DispatcherPriority.Background,...)更改一些属性和其他内容,例如
NotificationBorder.Opacity = 1; Dispatcher.BeginInvoke(DispatcherPriority.Background,(Action)(()=&gt; Thread.Sleep(5000)));
答案 1 :(得分:1)
如果您希望更新UI,则需要将长时间运行的操作更改为异步,这意味着正确地将UI依赖关系从任何逻辑或者IO绑定操作中解放出来。如果你不这样做,那么长时间运行的操作中发生的任何UI更新都不会实际反映在用户界面中,直到完成为止。
如果您使用的是.NET 4.5,这是我首选的模式:
public async Task DoComplexStuff()
{
//Grab values from UI objects that must be accessed on UI thread
//Run some calculations in the background that don't depend on the UI
var result = await Task.Run((Func<string>)DoComplexStuffInBackground);
//Update UI based on results
//Possibly do more stuff in the background, etc.
}
private string DoComplexStuffInBackground()
{
return "Stuff";
}
这有点“倒退”。从旧式的BackgroundWorker方法,但我发现它往往导致更清晰的逻辑。您可以通过Task.Run()
的显式后台工作调用编写UI代码,而不是使用显式的UI更新调用来编写后台代码。这也倾向于导致以UI为中心的代码与底层逻辑代码之间的自然分离,因为您需要将逻辑重构为可以这种方式调用的方法。无论如何,这对于长期可维护性来说是一个好主意。
答案 2 :(得分:0)
因此,您可以将操作(UI和计算)分配到调度程序中,它们将在一行中执行
Dispatcher.BeginInvoke(do ui things);
Dispatcher.BeginInvoke(do your logic things);
但您最好将计算移到后台主题中。