60

アプリケーションの読み込み中にスプラッシュ画面を表示したい。システムトレイコントロールが関連付けられたフォームがあります。このフォームの読み込み中にスプラッシュ画面を表示したいのですが、WebサービスAPIにアクセスしてドロップダウンを入力するため、少し時間がかかります。また、ロードする前に依存関係の基本的なテストを行いたいと思います(つまり、Webサービスが利用可能であり、構成ファイルが読み取り可能です)。起動の各フェーズが進むにつれて、進行状況に応じてスプラッシュ画面を更新したいと思います。

私はスレッドについてたくさん読んでいますが、これをどこから制御すべきか(main()方法は?)に迷っています。また、どのようにApplication.Run()機能するのかがわかりません。これは、このためのスレッドを作成する必要がある場所ですか?さて、システムトレイコントロールを備えたフォームが「生きている」フォームである場合、スプラッシュはそこから来る必要がありますか?とにかくフォームが完成するまでロードされませんか?

私はコードの配布物を探しているのではなく、アルゴリズム/アプローチの詳細を探しているので、これを一度だけ理解することができます:)

4

12 に答える 12

46

さて、過去にデプロイしたClickOnceアプリでは、Microsoft.VisualBasic名前空間を使用してスプラッシュ画面のスレッド化を処理しました。Microsoft.VisualBasic.NET 2.0のC#からアセンブリを参照して使用でき、多くの優れたサービスを提供します。

  1. メインフォームから継承するMicrosoft.VisualBasic.WindowsFormsApplicationBase
  2. 次のように「OnCreateSplashScreen」メソッドをオーバーライドします。

    protected override void OnCreateSplashScreen()
    {
        this.SplashScreen = new SplashForm();
        this.SplashScreen.TopMost = true;
    }
    

非常に簡単です。読み込み中にSplashForm(作成する必要があります)が表示され、メインフォームの読み込みが完了すると自動的に閉じます。

これにより、作業が非常に簡単になります。VisualBasic.WindowsFormsApplicationBaseもちろん、Microsoftによって十分にテストされており、100%C#のアプリケーションであっても、Winformsでの作業を大幅に楽にする多くの機能があります。

結局のところ、それはすべてILであり、bytecodeとにかく、それを使用してみませんか?

于 2008-09-08T01:28:03.933 に答える
46

秘訣は、スプラッシュ画面の表示を担当する別のスレッドを作成することです。
app .netを実行すると、メインスレッドが作成され、指定された(メイン)フォームが読み込まれます。ハードワークを隠すために、ロードが完了するまでメインフォームを非表示にすることができます。

Form1-がメインフォームであり、SplashFormがトップレベルであると仮定すると、素敵なスプラッシュフォームに隣接します。

private void Form1_Load(object sender, EventArgs e)
{
    Hide();
    bool done = false;
    ThreadPool.QueueUserWorkItem((x) =>
    {
        using (var splashForm = new SplashForm())
        {
            splashForm.Show();
            while (!done)
                Application.DoEvents();
            splashForm.Close();
        }
    });

    Thread.Sleep(3000); // Emulate hardwork
    done = true;
    Show();
}
于 2008-09-08T00:53:09.557 に答える
14

GoogleとSO全体で解決策を探した後、これが私のお気に入りです:http: //bytes.com/topic/c-sharp/answers/277446-winform-startup-splash-screen

FormSplash.cs:

public partial class FormSplash : Form
{
    private static Thread _splashThread;
    private static FormSplash _splashForm;

    public FormSplash() {
        InitializeComponent();
    }

    /// <summary>
    /// Show the Splash Screen (Loading...)
    /// </summary>
    public static void ShowSplash()
    {
        if (_splashThread == null)
        {
            // show the form in a new thread
            _splashThread = new Thread(new ThreadStart(DoShowSplash));
            _splashThread.IsBackground = true;
            _splashThread.Start();
        }
    }

    // called by the thread
    private static void DoShowSplash()
    {
        if (_splashForm == null)
            _splashForm = new FormSplash();

        // create a new message pump on this thread (started from ShowSplash)
        Application.Run(_splashForm);
    }

    /// <summary>
    /// Close the splash (Loading...) screen
    /// </summary>
    public static void CloseSplash()
    {
        // need to call on the thread that launched this splash
        if (_splashForm.InvokeRequired)
            _splashForm.Invoke(new MethodInvoker(CloseSplash));

        else
            Application.ExitThread();
    }
}

Program.cs:

static class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main(string[] args)
    {
        // splash screen, which is terminated in FormMain
        FormSplash.ShowSplash();

        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        // this is probably where your heavy lifting is:
        Application.Run(new FormMain());
    }
}

FormMain.cs

    ...

    public FormMain()
    {
        InitializeComponent();            

        // bunch of database access, form loading, etc
        // this is where you could do the heavy lifting of "loading" the app
        PullDataFromDatabase();
        DoLoadingWork();            

        // ready to go, now close the splash
        FormSplash.CloseSplash();
    }

解決策に問題がありましたMicrosoft.VisualBasic-XPで検索を実行しましたが、Windows 2003ターミナルサーバーでは、メインのアプリケーションフォームがバックグラウンドで(スプラッシュ画面の後に)表示され、タスクバーが点滅しました。そして、コードの前景/フォーカスにウィンドウをもたらすことは、Google/SOが利用できる他のすべてのワームの缶です。

于 2011-02-14T17:01:42.570 に答える
10

これは古い質問ですが、アニメーションを含めることができる WPF のスレッド化されたスプラッシュ スクリーン ソリューションを見つけようとしているときに、何度も出くわしました。

これが私が最終的にまとめたものです:

App.XAML:

<Application Startup="ApplicationStart" …

App.XAML.cs:

void ApplicationStart(object sender, StartupEventArgs e)
{
        var thread = new Thread(() =>
        {
            Dispatcher.CurrentDispatcher.BeginInvoke ((Action)(() => new MySplashForm().Show()));
            Dispatcher.Run();
        });
        thread.SetApartmentState(ApartmentState.STA);
        thread.IsBackground = true;
        thread.Start();

        // call synchronous configuration process
        // and declare/get reference to "main form"

        thread.Abort();

        mainForm.Show();
        mainForm.Activate();
  }
于 2010-07-21T04:44:53.703 に答える
9

aku からの回答Activate();の最後に直接電話することをお勧めします。Show();

MSDNを引用:

フォームをアクティブにすると、アクティブなアプリケーションの場合はフォームが前面に表示され、アクティブなアプリケーションでない場合はウィンドウのキャプションが点滅します。このメソッドが有効になるには、フォームが表示されている必要があります。

メイン フォームをアクティブにしないと、開いている他のウィンドウの背後に表示され、見栄えが悪くなります。

于 2010-01-22T12:16:12.017 に答える
6

akuGuyのような方法を使用するのが良い方法だと思いますが、特定の例からいくつかのことを取り除く必要があります。

  1. 基本的な前提は、できるだけ早く別のスレッドにスプラッシュを表示することです。それは私が最もよく知っている方法であるため、akuが示したものと同様に私が傾く方法です。私はガイが言及したVB機能を知りませんでした。そして、それがVBライブラリだとしても、彼は正しいです-結局はすべてILです。だから、たとえそれが汚れていると感じても、それはそれほど悪くはありません!:) VBがそのオーバーライドで別のスレッドを提供するか、自分でスレッドを作成するかを確認する必要があると思います。間違いなくそれを調べてください。

  2. このスプラッシュを表示するために別のスレッドを作成すると仮定すると、クロススレッドUIの更新に注意する必要があります。更新の進捗状況についておっしゃっていたので、これを取り上げます。基本的に、安全のために、デリゲートを使用してスプラッシュフォームで(作成した)更新関数を呼び出す必要があります。そのデリゲートをスプラッシュ画面のフォームオブジェクトのInvoke関数に渡します。実際、スプラッシュフォームを直接呼び出して、進行状況/ UI要素を更新すると、.Net 2.0 CLRで実行している場合、例外が発生します。経験則として、フォーム上のUI要素は、それを作成したスレッドによって更新する必要があります。これがForm.Invokeが保証するものです。

最後に、コードのメインメソッドでスプラッシュを作成することを選択する可能性があります(VBオーバーロードを使用しない場合)。私にとってこれは、メインフォームにオブジェクトの作成を実行させ、それに非常に緊密にバインドするよりも優れています。そのアプローチを採用する場合は、スプラッシュ画面が実装する単純なインターフェイス(IStartupProgressListenerなど)を作成することをお勧めします。このインターフェイスは、メンバー関数を介して起動の進行状況の更新を受け取ります。これにより、必要に応じてどちらかのクラスを簡単にスワップイン/スワップアウトでき、コードを適切に分離できます。スプラッシュフォームは、起動が完了したときに通知すると、いつ閉じるかを知ることもできます。

于 2008-09-08T02:18:28.663 に答える
5

簡単な方法の1つは、次のようなものをmain()として使用することです。

<STAThread()> Public Shared Sub Main()

    splash = New frmSplash
    splash.Show()

    ' Your startup code goes here...

    UpdateSplashAndLogMessage("Startup part 1 done...")

    ' ... and more as needed...

    splash.Hide()
    Application.Run(myMainForm)
End Sub

.NET CLRがアプリケーションを起動すると、「メイン」スレッドが作成され、そのスレッドでmain()の実行が開始されます。最後のApplication.Run(myMainForm)は、次の2つのことを行います。

  1. main()を実行しているスレッドをGUIスレッドとして使用して、Windowsの「メッセージポンプ」を起動します。
  2. 「メインフォーム」をアプリケーションの「シャットダウンフォーム」として指定します。ユーザーがそのフォームを閉じると、Application.Run()が終了し、制御がmain()に戻ります。ここで、必要なシャットダウンを実行できます。

スプラッシュウィンドウを処理するためにスレッドを生成する必要はありません。実際、これは悪い考えです。これは、main()からスプラッシュの内容を更新するためにスレッドセーフな手法を使用する必要があるためです。

アプリケーションでバックグラウンド操作を行うために他のスレッドが必要な場合は、main()からそれらを生成できます。Thread.IsBackgroundをTrueに設定することを忘れないでください。そうすれば、メイン/GUIスレッドが終了したときにそれらが停止します。それ以外の場合は、他のすべてのスレッドを自分で終了するように調整する必要があります。そうしないと、メインスレッドが終了したときにアプリケーションが存続します(ただし、GUIはありません)。

于 2008-09-08T00:51:53.787 に答える
4
private void MainForm_Load(object sender, EventArgs e)
{
     FormSplash splash = new FormSplash();
     splash.Show();
     splash.Update();
     System.Threading.Thread.Sleep(3000);
     splash.Hide();
}

これをどこかのインターネットから入手しましたが、再び見つけることができないようです。シンプルだけど効果的。

于 2011-07-19T08:08:12.283 に答える
4

codeproject にアプリへのスプラッシュ画面の組み込みに関する記事を投稿しました。マルチスレッドであり、あなたの興味があるかもしれません

C# のさらに別のスプラッシュ スクリーン

于 2010-01-22T12:41:42.363 に答える
3

Aku の回答はとても気に入っていますが、ラムダ関数を使用しているため、コードは C# 3.0 以降用です。C# 2.0 のコードを使用する必要がある人のために、ラムダ関数の代わりに匿名デリゲートを使用するコードを次に示します。formSplashで呼び出される最上位の winform が必要ですFormBorderStyle = None。フォームのTopMost = Trueパラメーターは重要です。スプラッシュ スクリーンは、最上位にない場合、表示されてからすぐに消えるように見える可能性があるためです。StartPosition=CenterScreenまた、プロのアプリがスプラッシュ スクリーンで行うように見えるように選択します。さらにクールな効果が必要な場合は、TrasparencyKeyプロパティを使用して不規則な形状のスプラッシュ スクリーンを作成できます。

private void formMain_Load(object sender, EventArgs e)
  {

     Hide();
     bool done = false;
     ThreadPool.QueueUserWorkItem(delegate
     {
       using (formSplash splashForm = new formSplash())
       {
           splashForm.Show();
           while (!done)
              Application.DoEvents();
           splashForm.Close();
       }
     }, null);

     Thread.Sleep(2000);
     done = true;
     Show();
  }
于 2011-11-22T21:27:34.640 に答える
2

を推奨する他の回答には同意しませんWindowsFormsApplicationBase。私の経験では、アプリが遅くなる可能性があります。正確には、フォームのコンストラクターをスプラッシュ スクリーンと並行して実行している間、フォームの Shown イベントを延期します。

1 秒かかるコンストラクターと 2 秒かかる Shown のイベント ハンドラーを持つアプリ (スプラッシュ画面なし) を考えてみましょう。このアプリは 3 秒後に使用可能になります。

しかし、 を使用してスプラッシュ スクリーンをインストールするとします WindowsFormsApplicationBaseMinimumSplashScreenDisplayTime3 秒が賢明で、アプリの速度が低下しないと考えるかもしれません。しかし、試してみてください。アプリの読み込みに 5 秒かかるようになりました。


class App : WindowsFormsApplicationBase
{
    protected override void OnCreateSplashScreen()
    {
        this.MinimumSplashScreenDisplayTime = 3000; // milliseconds
        this.SplashScreen = new Splash();
    }

    protected override void OnCreateMainForm()
    {
        this.MainForm = new Form1();
    }
}

public Form1()
{
    InitializeComponent();
    Shown += Form1_Shown;
    Thread.Sleep(TimeSpan.FromSeconds(1));
}

void Form1_Shown(object sender, EventArgs e)
{
    Thread.Sleep(TimeSpan.FromSeconds(2));
    Program.watch.Stop();
    this.textBox1.Text = Program.watch.ElapsedMilliseconds.ToString();
}

結論:WindowsFormsApplicationBaseアプリに Slown イベントのハンドラーがある場合は使用しないでください。スプラッシュをコンストラクターと Shown イベントの両方と並行して実行する、より優れたコードを作成できます。

于 2013-08-22T09:30:07.337 に答える