Где находится Application.DoEvents () в WPF? - PullRequest
76 голосов
/ 21 декабря 2010

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

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

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();, но ... она не существует a priori в .NET 4.

Что делать?

Ответы [ 7 ]

124 голосов
/ 21 декабря 2010

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

public static void DoEvents()
{
    Application.Current.Dispatcher.Invoke(DispatcherPriority.Background,
                                          new Action(delegate { }));
}
52 голосов
/ 10 августа 2012

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

23 голосов
/ 21 декабря 2010

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

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

6 голосов
/ 19 марта 2018

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

public static void DoEvents()
{
    Application.Current.Dispatcher.Invoke(DispatcherPriority.Render,
                                          new Action(delegate { }));
}
5 голосов
/ 21 декабря 2010
myCanvas.UpdateLayout();

вроде бы тоже работает.

3 голосов
/ 05 августа 2014

Одна проблема с обоими предлагаемыми подходами заключается в том, что они влекут за собой использование процессора вхолостую (до 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()));
1 голос
/ 03 ноября 2014

С появлением 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, когда в коллекции есть хороший переход к теме, эффект весьма желателен.

Люк

  • см. Комментарии. Когда я быстро писал свой ответ, я думал о том, чтобы взять синхронный блок кода и затем вернуть поток обратно его вызывающей стороне, в результате чего блок кода становится асинхронным. Я не хочу полностью перефразировать мой ответ, потому что тогда читатели не смогут увидеть, из-за чего мы с Серви спорили.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...