Saya memiliki kode contoh berikut yang memperbesar setiap kali tombol ditekan:
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));
}
}
outputnya adalah:
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
seperti yang Anda lihat, ada masalah, yang saya pikir bisa diselesaikan dengan menggunakan Application.DoEvents();
tapi ... tidak ada apriori di .NET 4.
Apa yang harus dilakukan?
Jawaban:
Metode Application.DoEvents () yang lama sudah tidak digunakan lagi di WPF karena menggunakan Dispatcher atau Background Worker Thread untuk melakukan pemrosesan seperti yang telah Anda jelaskan. Lihat tautan untuk beberapa artikel tentang cara menggunakan kedua objek.
Jika Anda benar-benar harus menggunakan Application.DoEvents (), Anda cukup mengimpor system.windows.forms.dll ke dalam aplikasi Anda dan memanggil metode tersebut. Namun, ini sebenarnya tidak disarankan, karena Anda kehilangan semua keuntungan yang disediakan WPF.
sumber
Cobalah sesuatu seperti ini
public static void DoEvents() { Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new Action(delegate { })); }
sumber
public static void DoEvents(this Application a)
Application.Current
terkadang nol ... jadi mungkin itu tidak cukup setara.Nah, saya baru saja menemukan kasus di mana saya mulai mengerjakan metode yang berjalan pada utas Dispatcher, dan perlu memblokir tanpa memblokir UI Thread. Ternyata msdn menjelaskan cara mengimplementasikan DoEvents () berdasarkan Dispatcher itu sendiri:
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; }
(diambil dari Metode Dispatcher.PushFrame )
Beberapa mungkin lebih suka dalam satu metode yang akan menerapkan logika yang sama:
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); }
sumber
Jika Anda hanya perlu memperbarui grafik jendela, lebih baik gunakan seperti ini
public static void DoEvents() { Application.Current.Dispatcher.Invoke(DispatcherPriority.Render, new Action(delegate { })); }
sumber
myCanvas.UpdateLayout();
sepertinya bekerja dengan baik.
sumber
Satu masalah dengan kedua pendekatan yang diusulkan adalah bahwa mereka memerlukan penggunaan CPU yang menganggur (hingga 12% menurut pengalaman saya). Ini tidak optimal dalam beberapa kasus, misalnya ketika perilaku UI modal diimplementasikan menggunakan teknik ini.
Variasi berikut memperkenalkan penundaan minimum antar frame menggunakan pengatur waktu (perhatikan bahwa ini ditulis di sini dengan Rx tetapi dapat dicapai dengan pengatur waktu reguler apa pun):
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()));
sumber
Sejak pengenalan
async
danawait
sekarang dimungkinkan untuk melepaskan thread UI sebagian melalui blok kode sinkron (sebelumnya) * menggunakanTask.Delay
, misalnyaprivate 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())); }
Jujur saja, saya belum mencobanya dengan kode persis di atas, tetapi saya menggunakannya dalam putaran ketat ketika saya menempatkan banyak item ke dalam
ItemsControl
template item yang mahal, terkadang menambahkan sedikit penundaan untuk memberikan yang lain. lebih banyak waktu di UI.Sebagai contoh:
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); } }
Di Windows Store, ketika ada transisi tema yang bagus pada koleksi, efeknya cukup diinginkan.
Luke
sumber
await
akan menyebabkan kompilator untuk mendaftarkan metode async lainnya sebagai kelanjutan dari tugas yang ditunggu. Kelanjutan tersebut akan terjadi di thread UI (konteks sinkronisasi yang sama). Kontrol kemudian kembali ke pemanggil dari metode async, yaitu subsistem eventing WPF, di mana event akan berjalan sampai kelanjutan yang dijadwalkan berjalan beberapa saat setelah periode penundaan berakhir.Jadikan DoEvent () Anda di WPF:
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();
Bekerja dengan baik untukku!
sumber
Menjawab pertanyaan awal: Dimana DoEvents?
Saya pikir DoEvents adalah VBA. Dan VBA tampaknya tidak memiliki fungsi Tidur. Tapi VBA memiliki cara untuk mendapatkan efek yang sama persis dengan Sleep atau Delay. Menurut saya, DoEvents setara dengan Sleep (0).
Di VB dan C #, Anda berurusan dengan .NET. Dan pertanyaan aslinya adalah pertanyaan C #. Di C #, Anda akan menggunakan Thread.Sleep (0), di mana 0 adalah 0 milidetik.
Anda membutuhkan
using System.Threading.Task;
di bagian atas file untuk digunakan
Sleep(100);
dalam kode Anda.
sumber