簡體   English   中英

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

[英]Where is the Application.DoEvents() in WPF?

我有以下示例代碼,每次按下按鈕時都會縮放:

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

output 是:

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

該怎么辦?

嘗試這樣的事情

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

好吧,我剛剛遇到了一個案例,我開始研究在 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 方法

有些人可能更喜歡在單一方法中執行相同的邏輯:

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

另見: https ://kent-boogaart.com/blog/dispatcher-frames

舊的 Application.DoEvents() 方法在 WPF 中已被棄用,而是使用Dispatcher后台工作線程來執行您所描述的處理。 請參閱有關如何使用這兩個對象的幾篇文章的鏈接。

如果您絕對必須使用 Application.DoEvents(),那么您可以簡單地將 system.windows.forms.dll 導入您的應用程序並調用該方法。 但是,確實不建議這樣做,因為您將失去 WPF 提供的所有優勢。

如果您只需要更新窗口圖形,最好像這樣使用

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

似乎也有效。

兩種提議的方法的一個問題是它們需要空閑的 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()));

由於引入了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 Store 上,當集合上有一個不錯的主題轉換時,效果是非常理想的。

盧克

  • 看評論。 當我快速寫下我的答案時,我正在考慮獲取同步代碼塊然后將線程放回其調用者的行為,其效果使代碼塊異步。 我不想完全改寫我的答案,因為這樣讀者就看不到我和 Servy 在爭吵什么。

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

為我工作好!

推架:

using System.Windows.Threading;
...
var frame = new DispatcherFrame();
Dispatcher.PushFrame(frame);

退出幀:

frame.Continue = false;

以下示例顯示如何使用 DispatcherFrame 來實現與 Windows Forms DoEvents 方法類似的結果。

https://docs.microsoft.com/dotnet/api/system.windows.threading.dispatcher.pushframe

回答最初的問題:DoEvents 在哪里?

我認為 DoEvents 是 VBA。 而且VBA好像沒有Sleep功能。 但是 VBA 有辦法獲得與睡眠或延遲完全相同的效果。 在我看來,DoEvents 相當於 Sleep(0)。

在 VB 和 C# 中,您正在處理 .NET。 原來的問題是一個 C# 問題。 在 C# 中,您將使用 Thread.Sleep(0),其中 0 是 0 毫秒。

你需要

using System.Threading.Task;

在文件的頂部,以便使用

Sleep(100);

在你的代碼中。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM