带进度条的 WPF 启动画面

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22026209/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 11:01:24  来源:igfitidea点击:

WPF SplashScreen with ProgressBar

wpfsplash-screen

提问by user3042410

I have a WPF project, with a splash screen added by the Project Wizard. On the same splash screen, I want to add a progress bar style meter. Does someone have any idea how to do this?

我有一个 WPF 项目,项目向导添加了一个启动画面。在同一个启动画面上,我想添加一个进度条样式表。有人知道如何做到这一点吗?

回答by Ali Adlavaran

Here is my scheme for doing this. My motivation for doing it this way is that I don't want to have the initialization code running on the UI Thread, and normally I want the initialization code on my App class (not a Splash screen).

这是我这样做的方案。我这样做的动机是我不想让初始化代码在 UI 线程上运行,通常我希望在我的 App 类(而不是启动画面)上运行初始化代码。

Basically, I set the App StartupUrito my splash screen, which gets the ball rolling.

基本上,我将应用程序设置StartupUri为我的启动画面,这让球滚动起来。

On the splash screen, I invoke a delegate back on the application. This is run on a worker thread. In the splash screen, I handle the EndInvoke, and close the Window.

在初始屏幕上,我在应用程序上调用了一个委托。这是在工作线程上运行的。在启动画面中,我处理EndInvoke,并关闭窗口。

In the application initialization delegate, I do the work, and at the end, create and open the normal main Window. During the work load, I also have a method on Slash that allows me to update progress.

在应用程序初始化委托中,我完成了工作,最后,创建并打开了正常的主窗口。在工作负载期间,我还有一个关于 Slash 的方法,可以让我更新进度。

OK,the code is fairly short, and doesn't include the main window code (which is unaffected by all this), but it ducks and dives with anonymous delegates, so read it carefully, and ideally play with it in the debugger.

好的,代码相当短,并且不包括主窗口代码(不受所有这些影响),但是它与匿名代表一起躲避和潜水,因此请仔细阅读,最好在调试器中使用它。

Here is the code....

这是代码....

<Application x:Class="SplashScreenDemo.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    StartupUri="Splash.xaml">
    <Application.Resources>

    </Application.Resources>
</Application>

app code behind...

应用程序代码背后...

internal delegate void Invoker();
public partial class App : Application
{
    public App()
    {
        ApplicationInitialize = _applicationInitialize;
    }
    public static new App Current
    {
        get { return Application.Current as App; }
    }
    internal delegate void ApplicationInitializeDelegate(Splash splashWindow);
    internal ApplicationInitializeDelegate ApplicationInitialize;
    private void _applicationInitialize(Splash splashWindow)
    {
        // fake workload, but with progress updates.
        Thread.Sleep(500);
        splashWindow.SetProgress(0.2);

        Thread.Sleep(500);
        splashWindow.SetProgress(0.4);

        Thread.Sleep(500);
        splashWindow.SetProgress(0.6);

        Thread.Sleep(500);
        splashWindow.SetProgress(0.8);

        Thread.Sleep(500);
        splashWindow.SetProgress(1);

        // Create the main window, but on the UI thread.
        Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Invoker)delegate
        {
            MainWindow = new Window1();
            MainWindow.Show();
        });           
    }
}

splash xaml(actually, nothing too interesting here...)

飞溅xaml(实际上,这里没什么有趣的......)

<Window x:Class="SplashScreenDemo.Splash"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Splash" Height="300" Width="300">
    <Grid>
        <TextBlock Height="21" Margin="91,61,108,0" VerticalAlignment="Top">Splash Screen</TextBlock>
        <ProgressBar Name="progBar" Margin="22,122,16,109" Minimum="0" Maximum="1"/>
    </Grid>
</Window>

splash code-behind...

飞溅代码隐藏...

public partial class Splash : Window
{
    public Splash()
    {
        InitializeComponent();
        this.Loaded += new RoutedEventHandler(Splash_Loaded);
    }

    void Splash_Loaded(object sender, RoutedEventArgs e)
    {
        IAsyncResult result = null;

        // This is an anonymous delegate that will be called when the initialization has COMPLETED
        AsyncCallback initCompleted = delegate(IAsyncResult ar)
        {
            App.Current.ApplicationInitialize.EndInvoke(result);

            // Ensure we call close on the UI Thread.
            Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Invoker)delegate { Close(); });
        };

        // This starts the initialization process on the Application
        result = App.Current.ApplicationInitialize.BeginInvoke(this, initCompleted, null);
    }

    public void SetProgress(double progress)
    {
        // Ensure we update on the UI Thread.
        Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Invoker)delegate { progBar.Value = progress; });           
    }
}

As the work is done on a worker thread, the progress bar will update nicely, and any animation you have on the splash screen will keep the entertainment rolling.

由于工作是在工作线程上完成的,进度条会很好地更新,并且您在启动屏幕上的任何动画都会使娱乐活动持续进行。

回答by mihails.kuzmins

I used .NET Core and when there was a new Thread Application.OnExit()was fired. So the only way how I managed to make it work is:

我使用了 .NET Core,当有一个新线程Application.OnExit()被触发时。所以我设法让它工作的唯一方法是:

Application.cs:

应用程序.cs:

protected override void OnStartup(StartupEventArgs e)
{
    base.OnStartup(e);
    _mySplashScreenWindow.Show();


    var myTask = new Task(() =>
    {
        for (var i = 1; i <= 20; i++)
        {
            _mySplashScreenWindow.Dispatcher.Invoke(() =>
            {
                _mySplashScreenWindow.SomeTextBlock.Text = i.ToString();
                _mySplashScreenWindow.Progress = i; // or i / 20 for %
            });
            Thread.Sleep(250);
        }
    });

    myTask.ContinueWith(_ =>
    {
        MainWindow = null;

        var mainWindow = new MainWindow();
        // Or if you have access to the SplashScreen in the MainWindow, you can subscribe there
        mainWindow.Loaded += (sender, args) =>
        {
            _mySplashScreenWindow.Close();
            _mySplashScreenWindow = null;
        }

        MainWindow = mainWindow;
        mainWindow.ShowDialog();
    }, TaskScheduler.FromCurrentSynchronizationContext());

    myTask.Start();
}