Самый чистый способ реализовать отложенное действие в потоке интерфейса - PullRequest
0 голосов
/ 22 июня 2011

У меня есть действие, которое нужно выполнить примерно через 3 секунды после запуска моего приложения.Я реализовал это следующим образом:

internal static class Entry
{
    private static SplashScreen splashScreen;

    [STAThread]
    internal static void Main()
    {
        ShowSplashScreen();
        StartApp();
    }

    private static void ShowSplashScreen()
    {
        splashScreen = new SplashScreen("Splash.png");
        splashScreen.Show(false, true);
    }

    private static void StartApp()
    {
        var app = new App();

        //this, in particular, is ugly and more difficult to comprehend than I'd like
        var dispatcherTimer = new DispatcherTimer();
        dispatcherTimer.Interval = TimeSpan.FromSeconds(3);
        dispatcherTimer.Tick += delegate
        {
            CloseSplashScreen();
            dispatcherTimer.Stop();
        };
        dispatcherTimer.Start();

        app.Run();
    }

    private static void CloseSplashScreen()
    {
        splashScreen.Close(TimeSpan.FromSeconds(1));
    }
}

Я нахожу код StartApp() довольно некрасивым, но не смог придумать более точную альтернативу.Есть ли здесь общая идиома, по которой я скучаю?

PS.Да, я знаю, SplashScreen имеет функцию автоматического закрытия.Я не хочу использовать это главным образом потому, что он начинает закрываться сразу после загрузки приложения, чего я не хочу делать.

Ответы [ 4 ]

0 голосов
/ 22 июня 2011

Это лучшее из того, что я мог придумать:

internal static class Entry
{
    private static SplashScreen splashScreen;
    private static App app;

    [STAThread]
    internal static void Main()
    {
        ShowSplashScreen();
        CreateApp();
        PumpDispatcherUntilPriority(DispatcherPriority.Loaded);
        PumpDispatcherFor(TimeSpan.FromSeconds(2));
        CloseSplashScreen();
        PumpDispatcherUntilAppExit();
    }

    private static void ShowSplashScreen()
    {
        splashScreen = new SplashScreen("Splash.png");
        splashScreen.Show(false, true);
    }

    private static void CloseSplashScreen()
    {
        splashScreen.Close(TimeSpan.FromSeconds(0.5));
    }

    private static void CreateApp()
    {
        app = new App();
    }

    private static void PumpDispatcherUntilPriority(DispatcherPriority dispatcherPriority)
    {
        var dispatcherFrame = new DispatcherFrame();
        Dispatcher.CurrentDispatcher.BeginInvoke((ThreadStart)(() => dispatcherFrame.Continue = false), dispatcherPriority);
        Dispatcher.PushFrame(dispatcherFrame);
    }

    private static void PumpDispatcherFor(TimeSpan timeSpan)
    {
        var dispatcherFrame = new DispatcherFrame();

        using (var timer = new Timer(o => dispatcherFrame.Continue = false, null, (long)timeSpan.TotalMilliseconds, Timeout.Infinite))
        {
            Dispatcher.PushFrame(dispatcherFrame);
        }
    }

    private static void PumpDispatcherUntilAppExit()
    {
        var dispatcherFrame = new DispatcherFrame();
        app.Exit += delegate
        {
            dispatcherFrame.Continue = false;
        };
        Dispatcher.PushFrame(dispatcherFrame);
    }
}

Я играл с методами расширения для Dispatcher, но в итоге нашел их менее интуитивными.Это потому, что PushFrame() равно static, поэтому любые методы расширения фактически не выполняются для Dispatcher, к которому они вызваны.YMMV.

Обратите внимание, что вы также можете позвонить app.Run() вместо PumpDispatcherUntilAppExit(), но я просто сделал это для согласованности.

0 голосов
/ 22 июня 2011

Вот что-то похожее, что вас может заинтересовать:

Как мы выполняем простую обработку в приложении WPF?

Это не совсем то, что вы ищетепотому что он закроет ваше окно, как только ваше приложение будет бездействовать, но вы можете подумать о том, чтобы запустить задержку после того, как ваше приложение бездействует.Вы можете найти эту ссылку полезной, чем.

0 голосов
/ 22 июня 2011

У вас нет определенного состояния, когда приложение запускается? Обычно вы хотите, чтобы ваш SplashScreen закрывался, когда ваше приложение готово обрабатывать пользовательский ввод, вместо произвольных 3 секунд. Поэтому я бы предложил закрыть ваш SplashScreen.

0 голосов
/ 22 июня 2011

На самом деле не имеет значения, является ли он уродливым, вы можете просто преобразовать его в метод, который, например, принимает Action в качестве параметра, и это не будет большой проблемой.

Как и некрасиво вы, вероятно, имели в виду, что это выглядит как плохой код, я бы предложил использовать нормальный поток (с Thread.Sleep перед вашим действием), который использует Dispatcher.Invokeвместо.Я, например, не знаю какой-либо наилучшей практики в этом отношении, хотя.Это также может быть красиво переработано в простой метод, который принимает Action.

Если вы хотите неблокирующее ожидание , то здесь также есть вопрос .

...