[英]Where is the Application.DoEvents() in WPF?
我有以下示例代碼,每次按下按鈕時都會縮放:
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));
}
}
output 是:
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 中不存在先驗。
該怎么辦?
嘗試這樣的事情
public static void DoEvents()
{
Application.Current.Dispatcher.Invoke(DispatcherPriority.Background,
new Action(delegate { }));
}
好吧,我剛剛遇到了一個案例,我開始研究在 Dispatcher 線程上運行的方法,它需要在不阻塞 UI 線程的情況下阻塞。 原來,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;
}
有些人可能更喜歡在單一方法中執行相同的邏輯:
public static void DoEvents()
{
var frame = new DispatcherFrame();
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
new DispatcherOperationCallback(
delegate (object f)
{
((DispatcherFrame)f).Continue = false;
return null;
}),frame);
Dispatcher.PushFrame(frame);
}
另見: https ://kent-boogaart.com/blog/dispatcher-frames
舊的 Application.DoEvents() 方法在 WPF 中已被棄用,而是使用Dispatcher或后台工作線程來執行您所描述的處理。 請參閱有關如何使用這兩個對象的幾篇文章的鏈接。
如果您絕對必須使用 Application.DoEvents(),那么您可以簡單地將 system.windows.forms.dll 導入您的應用程序並調用該方法。 但是,確實不建議這樣做,因為您將失去 WPF 提供的所有優勢。
如果您只需要更新窗口圖形,最好像這樣使用
public static void DoEvents()
{
Application.Current.Dispatcher.Invoke(DispatcherPriority.Render,
new Action(delegate { }));
}
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
時,我會在緊密循環中使用它,有時會增加一點延遲來給出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);
}
}
在 Windows Store 上,當集合上有一個不錯的主題轉換時,效果是非常理想的。
盧克
在 WPF 中創建你的 DoEvent():
Thread t = new Thread(() => {
// do some thing in thread
for (var i = 0; i < 500; i++)
{
Thread.Sleep(10); // in thread
// call owner thread
this.Dispatcher.Invoke(() => {
MediaItem uc = new MediaItem();
wpnList.Children.Add(uc);
});
}
});
t.TrySetApartmentState(ApartmentState.STA); //for using Clipboard in Threading
t.Start();
為我工作好!
推架:
using System.Windows.Threading;
...
var frame = new DispatcherFrame();
Dispatcher.PushFrame(frame);
退出幀:
frame.Continue = false;
以下示例顯示如何使用 DispatcherFrame 來實現與 Windows Forms DoEvents 方法類似的結果。
https://docs.microsoft.com/dotnet/api/system.windows.threading.dispatcher.pushframe
回答最初的問題:DoEvents 在哪里?
我認為 DoEvents 是 VBA。 而且VBA好像沒有Sleep功能。 但是 VBA 有辦法獲得與睡眠或延遲完全相同的效果。 在我看來,DoEvents 相當於 Sleep(0)。
在 VB 和 C# 中,您正在處理 .NET。 原來的問題是一個 C# 問題。 在 C# 中,您將使用 Thread.Sleep(0),其中 0 是 0 毫秒。
你需要
using System.Threading.Task;
在文件的頂部,以便使用
Sleep(100);
在你的代碼中。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.