C# 线程繁忙时WPF重画窗口
下面是我问题的简短示例。 在函数开始时,我想更改一些视觉参数,例如 隐藏窗口,使其变为红色(“我的示例”),在函数末尾,我想将其放回原处。 一些东西,如鼠标,等待光标的长期功能 在按钮单击功能结束之前,有没有优雅的方法使按钮变红- 允许窗口消息循环并行处理请求Background=Red并立即重画窗口的方法C# 线程繁忙时WPF重画窗口,c#,wpf,C#,Wpf,下面是我问题的简短示例。 在函数开始时,我想更改一些视觉参数,例如 隐藏窗口,使其变为红色(“我的示例”),在函数末尾,我想将其放回原处。 一些东西,如鼠标,等待光标的长期功能 在按钮单击功能结束之前,有没有优雅的方法使按钮变红- 允许窗口消息循环并行处理请求Background=Red并立即重画窗口的方法 private void OnButtonClick(object sender, RoutedEventArgs e) { System.W
private void OnButtonClick(object sender, RoutedEventArgs e)
{
System.Windows.Media.Brush br = ((Button)sender).Background;
((Button)sender).Background = System.Windows.Media.Brushes.Red;
Mouse.OverrideCursor = Cursors.Wait;
Function(); //long-term function
Mouse.OverrideCursor = Cursors.Arrow;
((Button)sender).Background = br;
}
您可以在新线程中运行它,可以是后台工作线程,也可以使用任务并行库(见下文)
如果您使用的是.NET 4.5,也可以使用Async/Await关键字来完成此操作。有一种简单的方法来完成此操作。
xaml:
您的问题是什么?将
函数()
放在另一个线程中,例如使用backgroundworker
谢谢,但是这不是我想要的,或者可能是另一个问题:我不希望用户单击另一个按钮并与窗口交互。长时间运行的函数没有那么长,最初我只使用等待光标,现在我只想给用户一个关于繁忙窗口的更好的视觉信息。对于后台任务,用户并不是很忙。您的问题要求以“并行”方式运行函数()。老实说,我认为这个答案完美地回答了这个问题。如果您有不同的问题,请开始新的问题,而不是在人们花时间回答后更改此问题。@user2136076,然后您可以尝试此扩展方法:Benjamin,我感谢Task的想法,尤其是您为帮助我们所做的努力。但我试过了,它并没有完全符合我的要求。QEstion是以并行方式更改颜色的,而不是运行函数()。第二个问题并不是第一个问题的改变,而是为了你,如果你知道如何继续解决任务解决方案的不足。是我的错,这个问题问得不好。Bolu引导我找到了我一直在寻找的解决方案。谢谢大家。这个解决方案很好,但可能会遇到时间问题。我认为最好使用Invoke代替BeginInvoke,以确保它立即运行。感谢@MarkTravis的建议,我已经更新了答案。@MarkTravisInvoke
不能保证立即启动委托,但它会阻止调用线程,直到调度程序线程为委托提供服务为止-它是同步运行的。
private void OnButtonClick(object sender, RoutedEventArgs e)
{
System.Windows.Media.Brush br = ((Button)sender).Background;
((Button)sender).Background = System.Windows.Media.Brushes.Red;
Mouse.OverrideCursor = Cursors.Wait;
// Run function in a new thread.
Task.Factory.StartNew(() =>
{
Function(); // Long running function.
})
.ContinueWith((result) =>
{
// Runs when Function is complete...
Mouse.OverrideCursor = Cursors.Arrow;
((Button)sender).Background = br;
});
}
<Button Command="{Binding TestCommand}" Content="Test" Focusable="False" Background="Green">
<Button.Resources>
<Style TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding Loading}" Value="true">
<Setter Property="Background" Value="Red"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Resources>
</Button>
public class VM : INotifyPropertyChanged
{
public bool Loading
{
get { return _loading; }
private set
{
if (value.Equals(_loading)) return;
_loading = value;
OnPropertyChanged("Loading");
}
}
public RelayCommand TestCommand
{
get { return _testCommand; }
}
void Test(object parameter)
{
Dispatcher.CurrentDispatcher.BeginInvoke( (Action) (() => Loading = true));
//or if you want to do it with more responsive UI then use
Dispatcher.CurrentDispatcher.Invoke( (Action) (() => Loading = true));
doSomething(); //this could be replaced with BackgroundWorker DoWork function
//or this code could be the DoWork function.
Loading = false;
}
}