SplashScreen.Close(Timespan.FromMilliseconds(int)) : Is there an Event dispatched at Timespan Complete?
Asked Answered
H

5

9

C# WPF Application

I have a SplashScreen being displayed at startup for a minimum amount of time by using

Thread.Sleep(int); //int = milliseconds to display splash screen

When that sleep time is reached, the code resumes and the SplashScreen fades out to close by using

SplashScreen.Close(Timespan.FromMilliseconds(int)); //int = milliseconds fade-out

I would like to pause at this point to wait until the SplashScreen has become 100% transparent and is fully closed, then continue with other tasks, I.E. Writiting to the Console or displaying a MainWindow.

Is there an event fired when the (TimeSpan.FromMilliseconds(int)) is complete? Any other suggestions?

namespace StartupSplash
{
    public class SplashScreenStartup
    {
        //IMPORTANT:set image property to Resource and NOT Splash Screen
        private SplashScreen Splash = new SplashScreen("Resources/SplashScreen.png");

        public void SplashScreenStartUp()
        {
            Splash.Show(false, true);
            Thread.Sleep(3000); // Pause code, display splash screen 3 seconds
            Splash.Close(TimeSpan.FromMilliseconds(3000)); // 3 second splash fade-out
            // I want to wait until splash screen fadeOut has completed before
            // this next console output is performed.
            Console.WriteLine("Executes before Splash fadeOut completes.");
        }

    }
Handcuff answered 3/11, 2012 at 21:22 Comment(8)
Why don't you do this within a thread and just wait until the thread finishes, should get you the results you want, and pretty simply to implement.Crasis
I am having no luck implementing that suggestion. Could you post an example by chance? I keep finding myself getting into background Tasks... I'll keep digging though. ThanksHandcuff
Why not just add another Thread.Sleep(3000); right after Splash.Close(TimeSpan.FromMilliseconds(3000));Microorganism
Because the Thread.Sleep stops the fadeOut.Handcuff
It should be not an edit of question but answer to it with modified code. Please fix. Thanks!Revamp
I tried but could not add that many characters to the reply box.?. Sorry, I'm learning... I'll try to fix it.Handcuff
I think what abatishchev meant was, it would be better if you posted an ANSWER to your own question, rather than going back and editing the code in your question (in place), because it will make the question confusing to anybody down-the-track who has a similar problem, and finds this thread using a search-engine. Half the value of SO is as a "community wiki".Andraandrade
I understand. The SO newb in me was reluctant to classify my code as an answer, but I get it now, after seeing how it posted.Handcuff
H
0

I never did find an event to listen for upon completion of the TimeSpan. Also, after deciding to Not stop the threads, I chose to use DispatcherTimers instead.

(I have thinned and contained the logic into this one class for reference purposes)

using System;
using System.Windows;
using System.Windows.Threading;


namespace StartupSplash2
{

public partial class MainWindow : Window
{
    private DispatcherTimer visibleTimer;
    private DispatcherTimer fadeoutTimer;
    private SplashScreen splash;
    private int visibleTime = (4000); //milliseconds of splash visible time
    private int fadeoutTime = (1500); //milliseconds of splash fadeout time

    public MainWindow()
    {   
        //hide this MainWindow window until splash completes
        this.Visibility = Visibility.Hidden; 
        InitializeComponent();
        splashIn(); //start the splash
    }

    private void splashIn()
    {
        splash = new SplashScreen("Resources/SplashScreen.png"); //ensure image property is set to Resource and not screen saver
        visibleTimer = new DispatcherTimer(); //timer controlling how long splash is visible
        visibleTimer.Interval = TimeSpan.FromMilliseconds(visibleTime);
        visibleTimer.Tick += showTimer_Tick; //when timer time is reached, call 'showTimer_Tick" to begin fadeout
        splash.Show(false, true); //display splash
        visibleTimer.Start();
    }

    private void showTimer_Tick(object sender, EventArgs e)
    {
        visibleTimer.Stop();
        visibleTimer = null; //clear the unused timer
        fadeoutTimer = new DispatcherTimer();
        fadeoutTimer.Interval = TimeSpan.FromMilliseconds(fadeoutTime); //a timer that runs while splash fades out and controlls when main window is displayed
        fadeoutTimer.Tick += fadeTimer_Tick; //when fadeout timer is reached, call 'fadeTimer_Tick' to show main window
        splash.Close(TimeSpan.FromMilliseconds(fadeoutTime)); //begin splash fadeout to close
        fadeoutTimer.Start();
    }

    private void fadeTimer_Tick(object sender, EventArgs e)
    {
        fadeoutTimer.Stop();
        fadeoutTimer = null; //clear the unused timer
        splash = null; //clear the splash var
        MainWindowReady(); //call method to display main window
    }

    public void MainWindowReady()
    {
        this.Visibility = Visibility.Visible;
        //Here is the start of the Main Window Code
        this.Content = "Ok, the app is ready to roll";
    }

  }
}
Handcuff answered 12/11, 2012 at 4:2 Comment(0)
I
3

Maybe this code can help you. Using the backgroundworker class:

BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += (o, ea) => 
{
   // Dispatcher.Invoke commands the dispatcher to do something
   Dispatcher.Invoke((Action)(() => Splash.Close(TimeSpan.FromMilliseconds(3000)));
   // Sleeps this worker but NOT the UI
   Thread.Sleep(3000);
};
worker.RunWorkerCompleted += (o, ea) =>
{
    // Open your mainwindow sample
    MainWindow w = new MainWindow();
    w.Show();
};

//Runs the worker on its own thread
worker.RunWorkerAsync();

This should start the closing of your splashscreen, then sleep through it, and when it's done it'll open your mainwindow. I actually use something very similar to this to implement a login and fetch info for my WPF app, while displaying a progress bar and updating the text in it to stuff like "Connecting to server", "Logging in" and "Fetching data".

Incomputable answered 4/11, 2012 at 17:16 Comment(2)
Thank You for this direction. At first I had a few errors because the squiggly brackets which end .DoWork and .RunWorkerCompleted needed semicolons after them. But still, I was getting errors relating to the Dispatcher.Invoke. While trying to resolve that I found the following code to work.Handcuff
Oh ofcourse, sorry! your error: the dispatcher misses a bracket at the end. About the backgroundworker: I found that you can just put your code in it and see how it goes. If doing something gives a threadlockedexception or something, use a dispatcher.invoke action. If getting something (such as the int fadeOutTime) and you get an error, see if you can use a static member instead. It's quite simple and I really like the class. No weird stuff with starting and locking threads, this should only be done if you want optimal performance, like with Battlefield 3 or something. Not for showing a SS.Incomputable
H
1

I found that the following code works. I am not quite clear why and I will delve in closer to understand this better.

Please critique as needed, I am here to learn and share. Cheers.

class Tester
    {
    // Create splash screen instance and reference the image location.
    // IMPORTANT Ensure that the image properties are set to Resource and NOT Splash Screen
    private SplashScreen Splash = new SplashScreen("Resources/SplashScreen.png");

    public void Display()
    {
        Splash.Show(false, true);
        // pause the code, thus, displaying the splash for 3 seconds
        Thread.Sleep(3000); 
        // close the splash
        Close();
    }

    private void Close()
    {
        // sets the fadeout time in milliseconds
        int fadeOutTime = 1500; 

        // wait until the splash screen fadeOut has completed before writing to the console
        BackgroundWorker worker = new BackgroundWorker();
        worker.DoWork += (o, ea) =>
        {
            // Run background task (fade out and close the splash)
            Splash.Close(TimeSpan.FromMilliseconds(fadeOutTime));
            // Sleep this worker but NOT the UI (for the same time as the fade out time)
            Thread.Sleep(fadeOutTime);
        };
        worker.RunWorkerCompleted += (o, ea) =>
        {
            // Execute task after splash has closed completely
            Console.WriteLine("This is after the splash screen fadeOut completes.");
        };
        // start the background task, on it's own thread
        worker.RunWorkerAsync(); 
    }

}
Handcuff answered 5/11, 2012 at 5:48 Comment(4)
Ummm... I only display a splash screen when my application initialisation takes so long that the user might otherwise assume that they didn't click the shortcut properly, and therefore click-it again, starting two instances of the application concurrently (which both then take more than twice as long to startup)... so... I'd HIGHLY recommend that you find a way to NOT sleep the main thread while your splash screen is on show, so it can init your app... and if the main form loads and displays BEHIND the splash screen (hint: allways-on-top) then what's the damage? Cheers. Keith.Andraandrade
Very good points Keith. Thank You for those suggestions. I will improve this as I get a better grasp on BackgroundWorker and multithreading. Cheers Mate. ReidHandcuff
My basic idea is shift EVERYTHING to do with the SplashScreen into the DoWork method... so that it's shown, paused, and faded-out all on the background thread... then it's just one method call in your main FormLoaded event handler to ShowSplashScreenInTheBackground... but I'm stuck getting it to show at all that way... sigh!Andraandrade
The issue I have with this code is that if I invoke it from another class, the splash never fades out or closes. Also, I am agreeing that stopping a thread might not be the best practice, therefore my journey continues.Handcuff
A
0

I eventually came to conclusion that I was barking up the wrong tree in my previous comments. Displaying the SplashScreen in the background is both problematic (it refused to close automatically, no matter what I tried) and unnessary. Here's what I ended up with... Really simple!

using System;
using System.Net;
using System.Windows;

namespace WpfApplication1
{
  /// <summary>
  /// Interaction logic for Window1.xaml
  /// </summary>
  public partial class Window1 : Window
  {
    public Window1() {
      InitializeComponent();
    }

    private void Window_Loaded(object sender, RoutedEventArgs e) {
      // show the splash screen
      // nb: Resources/SplashScreenImage.png file Properties ~ Build Action='Resource'
      var splashScreen = new SplashScreen("Resources/SplashScreenImage.png");
      splashScreen.Show(false); // don't close automatically
      // ... initialise my application ...
      Initialise();
      // close the splash screen.
      splashScreen.Close(TimeSpan.FromMilliseconds(250D));
    }

    private void Initialise() {
      // do my long-running application initialisation on the main thread. 
      // In reality you'd do this download asyncronously, but in this case
      // it serves as a simple proxy for some "heavy" inititalisation work.
      textBox1.Text = new WebClient().DownloadString("https://mcmap.net/q/1223125/-splashscreen-close-timespan-frommilliseconds-int-is-there-an-event-dispatched-at-timespan-complete");
    }
  }

}

I hope that helps... though I'm not at all confident that it will ;-)

Cheers. Keith.

PS: I wonder why the splash refused to close? My guess it internally relies on events which are only available (i.e. subscribable) on WPF's equivalent of the event-dispatch-thread (whatever it's called).

Andraandrade answered 5/11, 2012 at 8:6 Comment(1)
At first, I thought there would be an event fired when the TimeSpan completed. In efforts to steer away from stopping Threads, I have started to pursue the use of timers for this instead. I will get a solution code up as soon as I get back on track. What a learning this has lead me on. :)Handcuff
H
0

I never did find an event to listen for upon completion of the TimeSpan. Also, after deciding to Not stop the threads, I chose to use DispatcherTimers instead.

(I have thinned and contained the logic into this one class for reference purposes)

using System;
using System.Windows;
using System.Windows.Threading;


namespace StartupSplash2
{

public partial class MainWindow : Window
{
    private DispatcherTimer visibleTimer;
    private DispatcherTimer fadeoutTimer;
    private SplashScreen splash;
    private int visibleTime = (4000); //milliseconds of splash visible time
    private int fadeoutTime = (1500); //milliseconds of splash fadeout time

    public MainWindow()
    {   
        //hide this MainWindow window until splash completes
        this.Visibility = Visibility.Hidden; 
        InitializeComponent();
        splashIn(); //start the splash
    }

    private void splashIn()
    {
        splash = new SplashScreen("Resources/SplashScreen.png"); //ensure image property is set to Resource and not screen saver
        visibleTimer = new DispatcherTimer(); //timer controlling how long splash is visible
        visibleTimer.Interval = TimeSpan.FromMilliseconds(visibleTime);
        visibleTimer.Tick += showTimer_Tick; //when timer time is reached, call 'showTimer_Tick" to begin fadeout
        splash.Show(false, true); //display splash
        visibleTimer.Start();
    }

    private void showTimer_Tick(object sender, EventArgs e)
    {
        visibleTimer.Stop();
        visibleTimer = null; //clear the unused timer
        fadeoutTimer = new DispatcherTimer();
        fadeoutTimer.Interval = TimeSpan.FromMilliseconds(fadeoutTime); //a timer that runs while splash fades out and controlls when main window is displayed
        fadeoutTimer.Tick += fadeTimer_Tick; //when fadeout timer is reached, call 'fadeTimer_Tick' to show main window
        splash.Close(TimeSpan.FromMilliseconds(fadeoutTime)); //begin splash fadeout to close
        fadeoutTimer.Start();
    }

    private void fadeTimer_Tick(object sender, EventArgs e)
    {
        fadeoutTimer.Stop();
        fadeoutTimer = null; //clear the unused timer
        splash = null; //clear the splash var
        MainWindowReady(); //call method to display main window
    }

    public void MainWindowReady()
    {
        this.Visibility = Visibility.Visible;
        //Here is the start of the Main Window Code
        this.Content = "Ok, the app is ready to roll";
    }

  }
}
Handcuff answered 12/11, 2012 at 4:2 Comment(0)
H
0

I found an event called SplashScreen.Dismissed that allows you to start the app after the SplashScreen expires. However, minimum required OS is Windows 8 and I could not use it. More info can be found here MSDN

Handcuff answered 16/11, 2012 at 13:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.