Where is the Application.DoEvents() in WPF?

2019-01-03 10:10发布

I have the following sample code that zooms each time a button is pressed:

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));
    }
}

the output is:

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

as you can see, there is a problem, that I thought solve by using Application.DoEvents(); but... it does not exist a priori in .NET 4.

What to do?

标签: c# .net wpf xaml
7条回答
倾城 Initia
2楼-- · 2019-01-03 10:22

Try something like this

public static void DoEvents()
{
    Application.Current.Dispatcher.Invoke(DispatcherPriority.Background,
                                          new Action(delegate { }));
}
查看更多
该账号已被封号
3楼-- · 2019-01-03 10:30

Well, I just hit a case where I start work on a method that runs on the Dispatcher thread, and it needs to block without blocking the UI Thread. Turns out that msdn explains how to implement a DoEvents() based on the Dispatcher itself:

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;
}

(taken from Dispatcher.PushFrame Method)

查看更多
beautiful°
4楼-- · 2019-01-03 10:31

The old Application.DoEvents() method has been deprecated in WPF in favor of using a Dispatcher or a Background Worker Thread to do the processing as you have described. See the links for a couple of articles on how to use both objects.

If you absolutely must use Application.DoEvents(), then you could simply import the system.windows.forms.dll into your application and call the method. However, this really isn't recommended, since you're losing all the advantages that WPF provides.

查看更多
疯言疯语
5楼-- · 2019-01-03 10:33

If you need just update window graphic, better use like this

public static void DoEvents()
{
    Application.Current.Dispatcher.Invoke(DispatcherPriority.Render,
                                          new Action(delegate { }));
}
查看更多
Viruses.
6楼-- · 2019-01-03 10:36

Since the introduction of async and await its now possible to relinquish the UI thread partway through a (formerly)* synchronous block of code using Task.Delay, e.g.

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()));
}

I'll be honest, I've not tried it with the exact code above, but I use it in tight loops when I'm placing many items into an ItemsControl which has an expensive item template, sometimes adding a small delay to give the other stuff on the UI more time.

For example:

        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);
            }
        }

On Windows Store, when there's a nice theme transition on the collection, the effect is quite desirable.

Luke

  • see comments. When I was quickly writing my answer, I was thinking about the act of taking a synchronous block of code and then relinquishing the thread back to its caller, the effect of which makes the block of code asynchronous. I don't want to completely rephrase my answer because then readers can't see what Servy and I were bickering about.
查看更多
太酷不给撩
7楼-- · 2019-01-03 10:39
myCanvas.UpdateLayout();

seems to work as well.

查看更多
登录 后发表回答