WPF中的Application.DoEvents()在哪里?

时间:2010-12-21 17:18:53

标签: c# .net wpf xaml

我有以下示例代码,每次按下按钮时都会缩放:

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中不存在先验

怎么办?

9 个答案:

答案 0 :(得分:121)

尝试这样的事情

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

答案 1 :(得分:50)

好吧,我刚刚开始研究一个在Dispatcher线程上运行的方法的情况,它需要在不阻塞UI线程的情况下阻塞。事实证明,msdn解释了如何基于Dispatcher本身实现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 Method

答案 2 :(得分:23)

旧的Application.DoEvents()方法已在WPF中弃用,支持使用DispatcherBackground Worker Thread进行处理,如您所述。有关如何使用这两个对象的文章,请参阅链接。

如果您绝对必须使用Application.DoEvents(),那么您只需将system.windows.forms.dll导入您的应用程序并调用该方法即可。但是,我们不建议这样做,因为您正在失去WPF提供的所有优势。

答案 3 :(得分:5)

myCanvas.UpdateLayout();

似乎也有效。

答案 4 :(得分:5)

如果您只需更新窗口图形,请更好地使用

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

答案 5 :(得分:3)

两种提议方法的一个问题是它们需要空闲的CPU使用率(根据我的经验,高达12%)。在某些情况下,这是次优的,例如,当使用此技术实现模态UI行为时。

以下变化引入了使用定时器的帧之间的最小延迟(请注意,此处使用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()));

答案 6 :(得分:1)

asyncawait引入后,现在可以使用Task.Delay在(以前)*同步代码块中途放弃UI线程,例如

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时,我会在紧凑的循环中使用它,这有一个昂贵的项目模板,有时候添加一个小延迟,以便为UI上的其他内容提供更多时间。

例如:

        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应用商店中,当集合上有一个很好的主题转换时,效果非常理想。

  • 见评论。当我快速写出我的答案时,我正在思考采用同步代码块然后将线程放回其调用者的行为,其效果使得代码块异步。我不想完全改写我的答案,因为那时读者看不到Servy和我在争吵。

答案 7 :(得分:0)

在WPF中制作您的DoEvent():

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

为我工作好!

答案 8 :(得分:-2)

回答原始问题:DoEvents在哪里?

我认为DoEvents是VBA。而且VBA似乎没有睡眠功能。但是VBA可以达到与“睡眠”或“延迟”完全相同的效果。在我看来,DoEvents等同于Sleep(0)。

在VB和C#中,您正在使用.NET。最初的问题是C#问题。在C#中,您将使用Thread.Sleep(0),其中0是0毫秒。

您需要

targert=start2

位于文件顶部以便使用

using System.Threading.Task;

在您的代码中。

相关问题