我需要每60秒执行一次方法,直到应用程序退出,而又不使用C#WPF降低GUI的速度。
每60秒执行一次的方法将检查数据库表中的布尔值。如果值为true,则应用程序继续运行;如果值为false,则应用程序退出。
我看过一些使用System.Threading.Timers,后台工作人员和任务的帖子。但是我不知道哪个是最好的选择,这样GUI仍然是交互式的
任何建议将不胜感激
答案 0 :(得分:0)
以下是使用DispatchTimer
和async
/ await
的示例。这是大多数 WPF应用程序的首选计时器。在某些情况下,您可能需要其他计时器,但是该计时器将处理大多数WPF任务。
DispatchTimer
允许代码在UI线程上运行,因此您不必担心手动分派跨线程操作。
通过async
/ await
调用,数据库操作(此处与对Task.Delay
的调用类似)不会阻塞UI。您应该使用适当的异步DB操作替换Task.Delay
,例如,如果使用的是Entity Framework,请使用DbContext.SomeDbSet.FirstOrDefaultAsync()
。
XAML仅显示一个复选框,该复选框允许应用程序在选中时退出,并带有动画标签,以证明UI线程未被阻止。
BetterWindow.xaml.cs:
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;
namespace asyncTest
{
public partial class BetterWindow : Window
{
public BetterWindow()
{
InitializeComponent();
var timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromSeconds(10);
timer.Tick += Timer_Tick;
timer.Start();
}
private async void Timer_Tick(object sender, EventArgs e)
{
// "async void" is generally frowned upon, but it is acceptable for event handlers.
if (await ShouldExit())
{
Close();
}
}
private async Task<bool> ShouldExit()
{
Debug.WriteLine("Checking the DB");
//Simulate a long DB operation
await Task.Delay(TimeSpan.FromSeconds(5));
return chkAllowClose.IsChecked.GetValueOrDefault(false);
}
}
}
BetterWindow.xaml:
<Window x:Class="asyncTest.BetterWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:asyncTest"
mc:Ignorable="d"
Title="BetterWindow" Height="450" Width="800">
<DockPanel>
<CheckBox Content="Allow Close" Name="chkAllowClose" DockPanel.Dock="Top"></CheckBox>
<Grid>
<Label Content="The UI isn't locked!" RenderTransformOrigin="0.5, 0.5" Width="200" Height="200">
<Label.RenderTransform>
<RotateTransform x:Name="noFreeze" />
</Label.RenderTransform>
<Label.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="(Label.RenderTransform).(RotateTransform.Angle)"
To="-360" Duration="0:0:10" RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Label.Triggers>
</Label>
</Grid>
</DockPanel>
</Window>
答案 1 :(得分:-1)
我们不在这里使用实时操作系统,因此讨论计时器的精确性没有意义-它们永远都不是绝对精确的。但是,如果我们谈论的是60秒这样的时间粒度,那么无论如何都没关系。
因此可以选择一个计时器 ,但我更喜欢另一种方式:
public
MainWindow()
{
InitializeComponent();
var cts = new CancellationTokenSource();
var task = CheckToClose(cts.Token);
Closing += delegate { cts.Cancel(); };
}
async
Task CheckToClose(CancellationToken ct)
{
while (!ct.IsCancellationRequested)
{
try { await Task.Delay(TimeSpan.FromSeconds(60), ct); } catch (TaskCanceledException) { return; }
var keepRunning = await ShouldKeepRunning(ct);
if (!keepRunning)
{
Close();
return;
}
}
}
Task<bool> ShouldKeepRunning(CancellationToken ct) =>
// it's important here to take a thread pool task here so that working with the DB
// doesn't block the UI
Task.Run<bool>(
delegate
{
// here you ask the DB whether you keep running
// if working with DB is potentially too slow,
// then take the CancellationToken into account
});