Где Application.DoEvents () в WPF?
У меня есть следующий пример кода, который масштабируется каждый раз при нажатии кнопки:
XAML:
* .cs
- Как вы передаете параметры из xaml?
- Как открыть всплывающее окно WPF при нажатии другого элемента управления, используя только разметку XAML?
- Переход на видимость сетки XAML?
- Доступ к ScrollViewer из ListBox из C #
- WPF TreeView HierarchicalDataTemplate - привязка к объекту с несколькими дочерними коллекциями
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.
Что делать?
- Как связать TextBlock с ресурсом, содержащим форматированный текст?
- WPF: привязка ContextMenu к команде MVVM
- Приложение WPF, в котором есть только значок в трее
- Как получить доступ к определенному элементу в списке с помощью DataTemplate?
- Установка свойства с помощью EventTrigger
- Что такого особенного в Generic.xaml?
- Как установить событие MouseOver / триггер для границы в XAML?
- Этот тип CollectionView не поддерживает изменения в SourceCollection из streamа, отличного от streamа Dispatcher
Старый метод 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 обратно своему вызывающему, эффект которого делает блок кода асинхронным. Я не хочу полностью перефразировать мой ответ, потому что тогда читатели не видят, что мы с Серви спорили.