Где Application.DoEvents () в WPF?

У меня есть следующий пример кода, который масштабируется каждый раз при нажатии кнопки:

XAML:

     

* .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.

Что делать?

Старый метод Application.DoEvents () устарел в WPF в пользу использования диспетчера или фонового рабочего streamа для выполнения обработки, как вы описали. См. Ссылки на несколько статей о том, как использовать оба объекта.

Если вы абсолютно должны использовать Application.DoEvents (), вы можете просто импортировать system.windows.forms.dll в свое приложение и вызвать метод. Однако это действительно не рекомендуется, так как вы теряете все преимущества, предоставляемые WPF.

Попробуйте что-то вроде этого

 public static void DoEvents() { Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new Action(delegate { })); } 

Ну, я просто ударил случай, когда начал работу над методом, который работает в streamе Dispatcher, и ему нужно блокировать, не блокируя stream пользовательского интерфейса. Оказывается, что msdn объясняет, как реализовать 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(); 

похоже, тоже работает.

Одна из проблем с обоими предлагаемыми подходами заключается в том, что они влекут за собой неиспользуемое использование ЦП (до 12% в моем опыте). В некоторых случаях это неоптимально, например, когда модальное поведение пользовательского интерфейса реализовано с использованием этой методики.

Следующая вариация вводит минимальную задержку между кадрами с использованием таймера (обратите внимание, что она написана здесь с 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())); 

Если вам нужно просто обновить графику windows, лучше используйте это

 public static void DoEvents() { Application.Current.Dispatcher.Invoke(DispatcherPriority.Render, new Action(delegate { })); } 

Начиная с введения async и await что теперь можно отказаться от streamа пользовательского интерфейса частично через (ранее) * синхронный блок кода, используя Task.Delay , например

 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 которого есть дорогой шаблон, иногда добавляя небольшую задержку, чтобы дать другие вещи в пользовательском интерфейсе больше времени.

Например:

  var levelOptions = new ObservableCollection(); 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(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, когда есть приятный переход темы в коллекцию, эффект весьма желателен.

Люк

  • см. комментарии. Когда я быстро писал свой ответ, я думал о том, что вы делаете синхронный блок кода, а затем отбрасываете stream обратно своему вызывающему, эффект которого делает блок кода асинхронным. Я не хочу полностью перефразировать мой ответ, потому что тогда читатели не видят, что мы с Серви спорили.
  • Взаимоисключающие элементы меню?
  • GridView с двумя столбцами, шириной заливки
  • Как прокручивать в нижней части ScrollViewer автоматически с помощью Xaml и привязки?
  • Как связать перечисление с элементом управления combobox в WPF?
  • Обратный вызов, когда свойство зависимостей получает изменение xaml
  • Связывание видимости для DataGridColumn в WPF
  • XAML GridView ItemTemplate не привязывается к управлению
  • Есть ли ansible чит-лист WPF?
  • Изменение цвета ячейки DataGrid на основе значений
  • Связывание RelativeSource с помощью ToolTip или ContextMenu
  • Получить разрешение экрана в приложении Win10 UWP
  • Давайте будем гением компьютера.