У меня есть следующий пример кода, который увеличивает масштаб при каждом нажатии кнопки:
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.
Что делать?
Ответы:
Старый метод Application.DoEvents () объявлен устаревшим в WPF в пользу использования Dispatcher или Background Worker Thread для выполнения описанной вами обработки. По ссылкам вы найдете пару статей о том, как использовать оба объекта.
Если вам абсолютно необходимо использовать Application.DoEvents (), вы можете просто импортировать system.windows.forms.dll в свое приложение и вызвать метод. Однако это действительно не рекомендуется, поскольку вы теряете все преимущества, которые предоставляет WPF.
источник
Application.DoEvents
после увеличения myScaleTransform.ScaleX. Не знаю, возможно ли это с Диспетчером.Попробуйте что-то вроде этого
public static void DoEvents() { Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new Action(delegate { })); }
источник
public static void DoEvents(this Application a)
Application.Current
иногда имеет значение null ... так что, возможно, это не совсем эквивалент.Ну, я просто попал в случай, когда я начинаю работать над методом, который работает в потоке Dispatcher, и ему нужно заблокировать, не блокируя поток пользовательского интерфейса. Оказывается, msdn объясняет, как реализовать DoEvents () на основе самого Dispatcher:
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 )
Некоторые могут предпочесть это в одном методе, который будет применять ту же логику:
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); }
источник
Если вам нужно просто обновить графику окна, лучше используйте это
public static void DoEvents() { Application.Current.Dispatcher.Invoke(DispatcherPriority.Render, new Action(delegate { })); }
источник
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()));
источник
С момента появления
async
иawait
теперь стало возможным отказаться от потока пользовательского интерфейса на полпути через (ранее) * синхронный блок кода, используя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<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, когда в коллекции есть красивый переход темы, эффект желателен.
Люк
источник
await
заставит компилятор зарегистрировать оставшуюся часть асинхронного метода в качестве продолжения ожидаемой задачи. Это продолжение будет происходить в потоке пользовательского интерфейса (тот же контекст синхронизации). Затем управление возвращается вызывающей стороне асинхронного метода, то есть подсистеме обработки событий WPF, где события будут выполняться до тех пор, пока не будет запущено запланированное продолжение через некоторое время после истечения периода задержки.Сделайте DoEvent () в 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();
Хорошо работай для меня!
источник
Отвечая на исходный вопрос: где находится DoEvents?
Я думаю, что DoEvents - это VBA. И VBA, похоже, не имеет функции сна. Но у VBA есть способ добиться того же эффекта, что и у сна или задержки. Мне кажется, что DoEvents эквивалентно Sleep (0).
В VB и C # вы имеете дело с .NET. И исходный вопрос - это вопрос C #. В C # вы должны использовать Thread.Sleep (0), где 0 - это 0 миллисекунд.
Тебе нужно
using System.Threading.Task;
в верхней части файла, чтобы использовать
Sleep(100);
в вашем коде.
источник