C# 不一致的调度程序。调用行为
我为一个服务台团队创建了一个墙板应用程序,前端使用WPF,后端使用Cisco手机数据库。该应用程序由两个显示不同信息的屏幕组成,这些屏幕显示在同一屏幕上,并通过C# 不一致的调度程序。调用行为,c#,wpf,dispatcher,C#,Wpf,Dispatcher,我为一个服务台团队创建了一个墙板应用程序,前端使用WPF,后端使用Cisco手机数据库。该应用程序由两个显示不同信息的屏幕组成,这些屏幕显示在同一屏幕上,并通过System.Timers.Timer 应用程序是这样创建的:如果WindowA可见,则显示WindowB,然后隐藏WindowA。当其中一个窗口变为可见时,该窗口的计时器将再次激活,从而恢复数据库调用,而另一个窗口的计时器将被禁用: private static void InterfaceChanger_Elapsed(object
System.Timers.Timer
应用程序是这样创建的:如果
WindowA
可见,则显示WindowB
,然后隐藏WindowA
。当其中一个窗口变为可见时,该窗口的计时器将再次激活,从而恢复数据库调用,而另一个窗口的计时器将被禁用:
private static void InterfaceChanger_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
if (WindowA.Visibility == Visibility.Visible)
{
WindowAEnabled = false;
ChangeVisibility(Visibility.Visible, WindowB);
WindowBEnabled = true;
WindowB_Elapsed(null, null); // force the call of the timer's callback
ChangeVisibility(Visibility.Collapsed, WindowA);
}
else
{
WindowBEnabled = false;
ChangeVisibility(Visibility.Visible, WindowA);
WindowAEnabled = true;
WindowA_Elapsed(null, null); // force the call of the timer's callback
ChangeVisibility(Visibility.Collapsed, WindowB);
}
}
private static void ChangeVisibility(Visibility visibility, Window window)
{
window.Dispatcher.Invoke(DispatcherPriority.Normal, (SendOrPostCallback)delegate
{
window.Visibility = visibility;
}, null);
}
问题是,这是完美的工作。。。最多90%的时间。问题是,有时,如果WindowA的
可见性更改为可见,而WindowB的
可见性更改为折叠,WindowB
折叠,但WindowA
需要2-3秒才能变为可见,大多数情况下,WindowA
是可见的,而当WindowB
折叠时,它是看不见的。这(当它不工作时)会导致桌面可见,而不是应用程序可见。我最初使用的是
DispatcherPriority.Background
,但这导致屏幕变换器在70-80%的时间内工作,因此我决定将其更改为DispatcherPriority.Normal
(DispatcherPriority.Send
结果基本上与正常情况相同)
问题:
//WindowA:
<Application x:Class="MyNamespace.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="WindowA.xaml">
//WindowA class:
public static WindowA WindowAInstance;
public WindowA()
{
// unnecessary code hidden
WindowAInstance = this;
WindowB b = new WindowB;
}
// WindowB class
public static WindowB WindowBInstance;
public WindowB()
{
// unnecessary code hidden
WindowBInstance = this;
}
// this is the code that starts the timers
public static void StartTimersHandling()
{
Database.RemoveAgents();
InterfaceChangerTimer = new System.Timers.Timer();
InterfaceChangerTimer.Interval = ApplicationArguments.InterfaceChangerTime;
InterfaceChangerTimer.Elapsed += InterfaceChanger_Elapsed;
InterfaceChangerTimer.AutoReset = true;
InterfaceChangerTimer.Start();
WindowATimer = new System.Timers.Timer();
WindowATimer.Interval = 1000;
WindowATimer.Elapsed += WindowATimer_Elapsed;
WindowATimer.AutoReset = true;
WindowATimer.Start();
WindowBTimer = new System.Timers.Timer();
WindowBTimer.Interval = 1000;
WindowBTimer.Elapsed += WindowBTimer_Elapsed;
WindowBTimer.AutoReset = true;
WindowBTimer.Start();
}
//WindowA:
//WindowA类:
公共静态WindowA WindowA状态;
公共窗口a()
{
//隐藏不必要的代码
WindowAInstance=this;
WindowB=新的WindowB;
}
//WindowB类
公共静态WindowB WindowBinInstance;
公共窗口b()
{
//隐藏不必要的代码
WindowBInstance=这个;
}
//这是启动计时器的代码
公共静态void StartTimersHandling()
{
Database.removagents();
InterfaceChangerTimer=新的System.Timers.Timer();
InterfaceChangerTime.Interval=ApplicationArguments.InterfaceChangerTime;
InterfaceChangerTimer.Appead+=InterfaceChanger\u Appead;
InterfaceChangerTimer.AutoReset=true;
InterfaceChangerTimer.Start();
WindowATimer=new System.Timers.Timer();
WindowATimer.Interval=1000;
WindowATimer.appeased+=WindowATimer\u appeased;
WindowATimer.AutoReset=true;
WindowATimer.Start();
WindowBTimer=new System.Timers.Timer();
窗口时间间隔=1000;
WindowB计时器已用+=WindowB计时器已用;
WindowBTimer.AutoReset=true;
WindowBTimer.Start();
}
尝试使用Dispatcher.CurrentDispatcher
而不是窗口。Dispatcher
并开始激活:
已更新
将计时器切换到Dispatchermer:
听起来你在写一个信息亭应用程序(即全屏,非交互式)。如果是这样的话,我想你最好有一个单独的窗口,并在其中切换视图,而不是在两个单独的窗口之间切换。此外,还需要将数据库查询工作与窗口内容的刷新分开。此外,我认为如果视图之间互不了解会有所帮助:目前,第一个窗口与第二个窗口紧密耦合,这不是一个好主意 在我看来,如果你稍微改变一下你的架构,你现在遇到的很多问题都会消失。以下是我的建议: 首先,只需要一个窗口。创建两个用户控件(项目>添加用户控件),并将XAML布局从现有窗口移动到这两个新控件中。然后使主窗口看起来像这样:
<Window x:Class="StackOverflow.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my="clr-namespace:StackOverflow"
WindowState="Maximized" WindowStyle="None">
<Grid>
<my:UserControl1 x:Name="_first" Panel.ZIndex="1" />
<my:UserControl2 Panel.ZIndex="0" />
</Grid>
<Window.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard AutoReverse="True" RepeatBehavior="Forever">
<ObjectAnimationUsingKeyFrames BeginTime="0:0:5" Duration="0:0:5"
Storyboard.TargetName="_first"
Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0:0:0"
Value="{x:Static Visibility.Hidden}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Window.Triggers>
</Window>
正如您所看到的,它只是一个TextBlock
元素,其Text
属性绑定到用户控件的代码隐藏中定义的Number
属性。我对两个用户控件使用了相同的XAML,只是改变了文本的垂直对齐方式,这样我就可以知道在任何给定时间哪个控件是可见的
后面的代码如下所示(除类名外,两者都相同):
它基本上包含一个由“worker”方法递增的Number
属性(实现INotifyPropertyChanged
)。worker方法由计时器调用:在这里,我使用的是dispatchermer
,但由于我没有直接更改任何UI元素,.NET计时器都会这样做
工作线程计划使用
Task.run
在线程池上运行,然后异步运行。我正在模拟一个长时间运行的作业,使用Task.Delay
等待一段时间。这个辅助方法就是调用数据库查询的地方。通过设置计时器的Interval
属性,可以改变连续查询之间的间隔。没有什么可以说查询之间的间隔需要与UI的刷新间隔相同(即两个视图切换的速度);事实上,由于查询所需的时间不一,因此同步这两个数据将非常棘手。尝试使用Dispatcher.CurrentDispatcher而不是window.Dispatcher和priority DispatcherPriority.DataBind。那么,当WindowA的可见性发生变化时,会发生什么情况,是查询DB来更新它的视图吗?可能是db调用的问题,所以在收到db的响应之前不会显示windows?@mkonvisar在我使所有数据库调用异步之前都是如此
timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(5) };
timer.Tick += (sender, args) => InterfaceChanger_Elapsed();
timer.Start();
<Window x:Class="StackOverflow.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my="clr-namespace:StackOverflow"
WindowState="Maximized" WindowStyle="None">
<Grid>
<my:UserControl1 x:Name="_first" Panel.ZIndex="1" />
<my:UserControl2 Panel.ZIndex="0" />
</Grid>
<Window.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard AutoReverse="True" RepeatBehavior="Forever">
<ObjectAnimationUsingKeyFrames BeginTime="0:0:5" Duration="0:0:5"
Storyboard.TargetName="_first"
Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0:0:0"
Value="{x:Static Visibility.Hidden}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Window.Triggers>
</Window>
<UserControl x:Class="StackOverflow.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Background="White" Padding="40">
<TextBlock Text="{Binding Number}" FontSize="60"
TextAlignment="Center" VerticalAlignment="Top" />
</UserControl>
using System;
using System.ComponentModel;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Threading;
namespace StackOverflow
{
public partial class UserControl1 : UserControl, INotifyPropertyChanged
{
public UserControl1()
{
InitializeComponent();
DataContext = this;
_timer = new DispatcherTimer
{ Interval = TimeSpan.FromSeconds(5), IsEnabled = true };
_timer.Tick += (sender, e) => Task.Run(async () => await DoWorkAsync());
}
readonly DispatcherTimer _timer;
readonly Random _random = new Random();
public event PropertyChangedEventHandler PropertyChanged;
public int Number
{
get
{
return _number;
}
private set
{
if (_number != value)
{
_number = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Number"));
}
}
}
}
int _number;
async Task DoWorkAsync()
{
// Asynchronous code started on a thread pool thread
Console.WriteLine(GetType().Name + " starting work");
_timer.IsEnabled = false;
try
{
await Task.Delay(TimeSpan.FromSeconds(_random.Next(4, 12)));
Number++;
}
finally
{
_timer.IsEnabled = true;
}
Console.WriteLine(GetType().Name + " finished work");
}
}
}