WPF中的Application.DoEvents()在哪里?
我有下面的示例代码,每次按下一个button时缩放:
XAML:
<Window x:Class="WpfApplication12.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <Canvas x:Name="myCanvas"> <Canvas.LayoutTransform> <ScaleTransform x:Name="myScaleTransform" /> </Canvas.LayoutTransform> <Button Content="Button" Name="myButton" Canvas.Left="50" Canvas.Top="50" Click="myButton_Click" /> </Canvas> </Window>
*的.cs
public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void myButton_Click(object sender, RoutedEventArgs e) { Console.WriteLine("scale {0}, location: {1}", myScaleTransform.ScaleX, myCanvas.PointToScreen(GetMyByttonLocation())); myScaleTransform.ScaleX = myScaleTransform.ScaleY = myScaleTransform.ScaleX + 1; Console.WriteLine("scale {0}, location: {1}", myScaleTransform.ScaleX, myCanvas.PointToScreen(GetMyByttonLocation())); } private Point GetMyByttonLocation() { return new Point( Canvas.GetLeft(myButton), Canvas.GetTop(myButton)); } }
输出是:
scale 1, location: 296;315 scale 2, location: 296;315 scale 2, location: 346;365 scale 3, location: 346;365 scale 3, location: 396;415 scale 4, location: 396;415
你可以看到,有一个问题,我想通过使用Application.DoEvents();
但是…它在.NET 4中并不存在。
该怎么办?
在WPF中,旧的Application.DoEvents()方法已被弃用,而使用分派器或后台工作线程来执行处理,如上所述。 查看一些关于如何使用这两个对象的文章的链接。
如果您绝对必须使用Application.DoEvents(),那么您可以简单地将system.windows.forms.dll导入到您的应用程序中并调用该方法。 但是,这不是build议的,因为你失去了WPF提供的所有优点。
尝试这样的事情
public static void DoEvents() { Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new Action(delegate { })); }
那么,我刚刚遇到一个情况,我开始在分派器线程上运行的方法的工作,它需要阻止,而不阻止UI线程。 事实certificate,MSDN解释了如何实现基于Dispatcher本身的DoEvents():
public void DoEvents() { DispatcherFrame frame = new DispatcherFrame(); Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(ExitFrame), frame); Dispatcher.PushFrame(frame); } public object ExitFrame(object f) { ((DispatcherFrame)f).Continue = false; return null; }
(取自Dispatcher.PushFrame方法 )
myCanvas.UpdateLayout();
似乎也工作。
所提出的两种方法的一个问题是它们占用CPU空闲(根据我的经验高达12%)。 在某些情况下,这是次优的,例如,当使用这种技术实现模态UI行为时。
下面的变化引入了使用定时器的帧之间的最小延迟(注意,这里用Rx写入,但可以用任何常规定时器来实现):
var minFrameDelay = Observable.Interval(TimeSpan.FromMilliseconds(50)).Take(1).Replay(); minFrameDelay.Connect(); // synchronously add a low-priority no-op to the Dispatcher's queue Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new Action(() => minFrameDelay.Wait()));
自引入async
并await
它现在可能通过使用Task.Delay
的(以前)*同步代码块中途放弃UI线程,例如
private async void myButton_Click(object sender, RoutedEventArgs e) { Console.WriteLine("scale {0}, location: {1}", myScaleTransform.ScaleX, myCanvas.PointToScreen(GetMyByttonLocation())); myScaleTransform.ScaleX = myScaleTransform.ScaleY = myScaleTransform.ScaleX + 1; await Task.Delay(1); // In my experiments, 0 doesn't work. Also, I have noticed // that I need to add as much as 100ms to allow the visual tree // to complete its arrange cycle and for properties to get their // final values (as opposed to NaN for widths etc.) Console.WriteLine("scale {0}, location: {1}", myScaleTransform.ScaleX, myCanvas.PointToScreen(GetMyByttonLocation())); }
我会说实话,我没有用上面的确切代码尝试过,但是当我将很多项目放入一个ItemsControl
模板的ItemsControl
时,我使用它在紧密的循环中,有时会增加一个小的延迟在UI上的其他东西更多的时间。
例如:
var levelOptions = new ObservableCollection<GameLevelChoiceItem>(); this.ViewModel[LevelOptionsViewModelKey] = levelOptions; var syllabus = await this.LevelRepository.GetSyllabusAsync(); foreach (var level in syllabus.Levels) { foreach (var subLevel in level.SubLevels) { var abilities = new List<GamePlayingAbility>(100); foreach (var g in subLevel.Games) { var gwa = await this.MetricsRepository.GetGamePlayingAbilityAsync(g.Value); abilities.Add(gwa); } double PlayingScore = AssessmentMetricsProcessor.ComputePlayingLevelAbility(abilities); levelOptions.Add(new GameLevelChoiceItem() { LevelAbilityMetric = PlayingScore, AbilityCaption = PlayingScore.ToString(), LevelCaption = subLevel.Name, LevelDescriptor = level.Ordinal + "." + subLevel.Ordinal, LevelLevels = subLevel.Games.Select(g => g.Value), }); await Task.Delay(100); } }
在Windowsapp store中,当collections集上有一个很好的主题转换时,效果非常理想。
卢克
- 看评论。 当我很快写出我的答案时,我正在考虑采取同步代码块的行为,然后将线程放回给调用者,其效果使得代码块asynchronous。 我不想完全地重述我的答案,因为那时读者不能看到Servy和我在争吵什么。