我有一个wpf应用程序,它执行非常繁重的操作,用户需要在应用程序“思考”时等待。
我想要做的是,当应用程序的主线程正在考虑时,另一个线程将禁用整个窗口并给它一种浅灰色,并且屏幕中间会出现一个圆形进度条。 / p>
这是一个很大的问题,我并不需要整个代码来做这个只是一般的想法。
感谢您的帮助......
答案 0 :(得分:12)
除了上述建议(后台工作者,调度员) - 是的,这些是获得所需内容的正确技巧,但让我讨论您在问题中请求的UI效果。如果您正在使用MVVM模式,则可以创建一些“我很忙”UI并绑定到视图模型中的IsBusy属性以显示和隐藏UI。例如:
public class MyViewModel : INotifyPropertyChanged
{
// Bind to this property any UI you want to
// show/hide during long running updates
public bool IsBusy
{
get { return _isBusy; }
set
{
_isBusy = true;
OnPropertyChanged("IsBusy");
}
}
private void OnPropertyChanged(string prop)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(prop));
}
}
// Note: This is intended to be called on a background thread
private void DoLongRunningOperationOnABackgroundThread()
{
try
{
IsBusy = true;
// do your work
}
finally
{
IsBusy = false;
}
}
}
然后在UI中使用此xaml(或类似的)
<UserControl:MyControl x:Class="MyApp.MyControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="boolToVis"/>
</UserControl.Resources>
<Grid>
<!-- your UI code goes here -->
<!-- Below this, at higher Z-Order place a control to gray out the screen when IsBusy = true -->
<Border Background="#55000000" BorderThickness="0" Visibility="{Binding IsBusy, Converter={StaticResource boolToVis}}">
<TextBlock Text="I AM BUSY!" Font-Size="32" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="White"/>
</Border>
<Grid>
</UserControl>
当您使用后台工作程序或线程池调用viewmodel中的DoLongRunningOperation函数时,将产生净效果,Xaml中定义的边框将在操作开始/停止时显示/隐藏。您不需要调度程序在此处调用,因为WPF会为您处理线程编组。
在网络上实现繁忙的控件以及whirlygig动画等......也可以增加UI的功能。
致以最诚挚的问候,
答案 1 :(得分:3)
使用Dispatcher.BeginInvoke更改UI组件的Enable-Property并显示/隐藏工作线程中的进度条
对于工作线程,您可以使用BackgroundWorker-Class
答案 2 :(得分:2)
将重击操作卸载到新线程并在主线程上执行UI内容(禁用,灰显和进度条)。请参阅BackgroundWorker and Dispatcher。
可以使用新线程作为UI内容,但不能使用现有的Window。 UI控件(Dispatcher)可以由其所属的线程在线使用/调用。但是,您可以创建一个新线程并使用带有新Dispatcher的新窗口来执行UI操作。然后,您必须将新窗口放在原始窗口上。不像我的第一个建议那么容易。如果您不知道何时执行繁重操作,则可能是一个选项。请参阅here,here和here。
答案 3 :(得分:2)
看一下这个样本:
public void DoHeavyWork()
{
mainDispatcher = Dispatcher.CurrentDispatcher;
DisableWindow();
workDelegate.BeginInvoke(EnableWindowCallBack, null);
}
private void EnableWindowCallBack(IAsyncResult asyncResult)
{
workDelegate.EndInvoke(asyncResult);
mainDispatcher.InvokeIfRequired(() => { EnableWindow(); });
}
当你致电DoHeavyWork
时,我假设我们在可以访问UI的线程上,这应该是常见的。 DisableWindow
显示动画或工作时应显示的内容。然后,您调用将在新线程上运行的预定义委托workDelegate
,一旦完成,回调应该恢复视图。
请注意,必须在有权访问UI的线程上调用EnableWindow
。