I have implemented a splashscreen for my project, it works well as desired.. but in my project i have an option of logout for user,this displays start page where a different login is provided(which is the starting screen..i.e, "chooselogin.xaml"). So when the user clicks on "choose a different login" while he already selected one in the application.. again the splashscreen appears, which is not required and looks odd.
the following code is what i think leading to problem... guys
public partial class Chooselogin : Window
{
public Chooselogin()
{
new SplashWindow().ShowDialog();
InitializeComponent();
}
......
This code is my "App.xaml"..
<Application x:Class="WpfApplication1.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="Chooselogin.xaml">
<Application.Resources>
<ResourceDictionary Source="/Themes/ExpressionDark.xaml"/>
</Application.Resources>
The splash screen code is as follows..
public partial class SplashWindow : Window
{
Thread loadingThread;
Storyboard Showboard;
Storyboard Hideboard;
private delegate void ShowDelegate(string txt);
private delegate void HideDelegate();
ShowDelegate showDelegate;
HideDelegate hideDelegate;
public SplashWindow()
{
InitializeComponent();
showDelegate = new ShowDelegate(this.showText);
hideDelegate = new HideDelegate(this.hideText);
Showboard = this.Resources["showStoryBoard"] as Storyboard;
Hideboard = this.Resources["HideStoryBoard"] as Storyboard;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
loadingThread = new Thread(load);
loadingThread.Start();
}
private void load()
{
Thread.Sleep(1000);
this.Dispatcher.Invoke(showDelegate, "Loading assets...please wait");
Thread.Sleep(2000);
//do some loading work
this.Dispatcher.Invoke(hideDelegate);
Thread.Sleep(2000);
this.Dispatcher.Invoke(showDelegate, "Loading profiles..");
Thread.Sleep(2000);
//do some loading work
this.Dispatcher.Invoke(hideDelegate);
Thread.Sleep(2000);
this.Dispatcher.Invoke(showDelegate, "Loading Data... almost done");
Thread.Sleep(2000);
this.Dispatcher.Invoke(hideDelegate);
//close the window
Thread.Sleep(2000);
this.Dispatcher.Invoke(DispatcherPriority.Normal,
(Action)delegate() { Close(); });
}
private void showText(string txt)
{
txtLoading.Text = txt;
BeginStoryboard(Showboard);
}
private void hideText()
{
BeginStoryboard(Hideboard);
}
}
The splash screen is supposed to be opened at start of application.. please help guys..
How about something simple like this?:
public partial class Chooselogin : Window
{
private static bool isFirstTime = true;
public Chooselogin()
{
if (isFirstTime)
{
new SplashWindow().ShowDialog();
isFirstTime = false;
}
InitializeComponent();
}
...
}
Now it will only display the splash screen once.
I recommend reading this post by Kent Boogaart
Example from the post
"WPF provides a SplashScreen class. It is simple by design and addresses the main goal of splash screens: immediate feedback. By virtue of forgoing the WPF stack and instead relying on Windows Imaging Component (WIC) to display images, it provides the quickest path to getting a splash on the screen short of writing your own native bootstrapper."
Related
I have a windows form application which is supposed to show a splash screen with a label field that I want to update as the main form (called welcome.cs) loads in the background. The splash screen shows & hides just fine, but the label doesn't update.
I've done a lot of research but haven't quite found the solution.
Program.cs
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
using (new SingleGlobalInstance(1000))
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
SplashScreen splashscreen = new SplashScreen();
splashscreen.ShowSplashScreen();
Welcome welcome = new Welcome(splashscreen); //Takes some time to load
splashscreen.CloseForm();
Application.Run(welcome);
}
}
Splashscreen.cs
public partial class SplashScreen : Form
{
//Delegate for cross thread call to close
private delegate void CloseDelegate();
private delegate void UpdateStatusDelegate(string status);
private static SplashScreen splashScreen;
private Thread thread = null;
public SplashScreen()
{
InitializeComponent();
}
public void ShowSplashScreen()
{
// Make sure it is only launched once.
if (splashScreen != null)
return;
thread = new Thread(ShowForm);
thread.IsBackground = true;
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
static private void ShowForm()
{
splashScreen = new SplashScreen();
Application.Run(splashScreen);
}
public void CloseForm()
{
splashScreen.Invoke(new CloseDelegate(CloseFormInternal));
}
static private void CloseFormInternal()
{
splashScreen.Close();
}
public void UpdateStatus(string status)
{
splashScreen.Invoke(new UpdateStatusDelegate(UpdateStatusInternal), status);
}
private void UpdateStatusInternal (string status)
{
if (splashScreen != null && splashScreen.IsHandleCreated)
{
lblStatus.Text = status;
}
}
}
Welcome.cs
public Welcome(Splashscreen splashscreen)
{
InitializeComponent();
//Code to log the user into the system
splashScreen.UpdateStatus("Logging in...");
//my expectation is that UpdateStatus call will update the label displayed on the splash screen but it doesn't.
//Do more stuff.....
}
Does it have something to do with multi-threading or is it because im creating a new instance of splashscreen in welcome.cs before calling UpdateStatus? How would I get around this?
You could do the following
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
string[] args = Environment.GetCommandLineArgs();
// Creates the Splash
splash = new FrmSplash();
//Opens the Splash in a new Thread, this way any gifs, progress bars, lablels changes will work because the main thread isnt blocked
var t = Task.Factory.StartNew(() =>
{
splash.ShowDialog();
});
while (!splash.Created) // wait the splash screen form load process
System.Threading.Thread.Sleep(300);
UpdateSplashMessage("Loading the program... Please wait");
// Some slow initialization code.
// ...
//Close splash screen
CloseSplash();
Application.Run(args);
}
static void CloseSplash()
{
splash.Invoke(new MethodInvoker(() =>
{
splash.Close(); // Closes the splash that is running in the other thread
}));
}
static void UpdateSplashMessage(string msg)
{
splash.Invoke(new MethodInvoker(() =>
{
splash.AtualizarMensagem(msg);
}));
}
Note that you will need to create a method called AtualizarMensagem(string str) in your splash screen form, like this
public void AtualizarMensagem(string novaMsg)
{
lblCarregando.Text = novaMsg;
}
I have this code in my "useful snnipets" folder, it always works for me.
Hope this helps.
I'm playing around with a tray application. The application runs only in the System Tray and has no Windows Form associated with it. The application uses a ManagementEventWatcher and displays an alert window in certain scenarios.
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new AppContext());
}
...
public class AppContext : ApplicationContext
{
private System.ComponentModel.IContainer _components;
private NotifyIcon _notifyIcon;
private ContextMenuStrip _contextMenu;
private ManagementEventWatcher _regWatcher;
public AppContext()
{
//Initialize context menu & tray icon
_regWatcher = new ManagementEventWatcher(query);
_regWatcher.EventArrived += new EventArrivedEventHandler(_regWatcher_EventArrived);
_regWatcher.Start();
}
void _regWatcher_EventArrived(object sender, EventArrivedEventArgs e)
{
Alert.Show("Alert!", "My Message", someParam);
}
}
...
public class Alert
{
public static void Show(string title, string message, string extraInfo)
{
new Alert(title, message, extraInfo).ShowDialog();
}
private Alert(string title, string message, string extraInfo)
{
InitializeComponent();
this.Icon = Properties.Resources._default;
this.Text = title;
this.label1.Text = message;
this.linkLabel1.Text = extraInfo;
}
}
Interestingly, it doesn't complain about not accessing the UI in a thread-safe way. I suppose because it only exists on this background thread. But later on when the Form tries to access the clipboard, it doesn't work because it is running on an MTA thread. So far, all the similar questions I have found already have a form to call Invoke on, or have the option of using a BackgroundWorker. What is the best way to create and display the Alert Form on the main thread in this case?
Thanks to Idle_Mind's link to Andy Whitfield's blog post I've arrived at a solution. I added a private global SynchronizationContext to the AppContext class. In the constructor I initialize it to an instance of a WindowsFormsSynchronizationContext. Then when the registry watcher's event occurs, I can Post the task back to the main thread.
public class AppContext : ApplicationContext
{
private SynchronizationContext _uiThreadContext;
...
public AppContext()
{
//Initialize context menu & tray icon
_uiThreadContext = new WindowsFormsSynchronizationContext();
_regWatcher = new ManagementEventWatcher(query);
_regWatcher.EventArrived += new EventArrivedEventHandler(_regWatcher_EventArrived);
_regWatcher.Start();
...
}
private void _regWatcher_EventArrived(object sender, EventArrivedEventArgs e)
{
...
_uiThreadContext.Post(new SendOrPostCallback(MyEventHandler), parameters)
}
I am new to c#. I have created main windows that I am adding usercontrols to switch between screens with command:
Switcher.Switch(new NewPage());
The class Switcher is:
public static class Switcher
{
public static MainWindow pageSwitcher;
public static void Switch(UserControl newPage)
{
pageSwitcher.Navigate(newPage);
}
public static void Switch(UserControl newPage, object state)
{
pageSwitcher.Navigate(newPage, state);
}
}
But how to I exit the user control? I wish to finish it (like back button). I can use:
Switcher.Switch(new PreviousPage());
but it will keep the new page in memory and will not release it.
Example of NewPage class:
namespace MyProject.Screens
{
public partial class NewPage : UserControl
{
public NewPage()
{
InitializeComponent();
}
private void back_button_Click_(object sender, RoutedEventArgs e)
{
//what to put here?
}
}
}
The framework does a lot of the heavy lifting for navigation for you, including the "back" operation that you're interested in.
Take a look at http://msdn.microsoft.com/en-us/library/ms750478.aspx
NavigationService.GoBack is what you'll use.
In the off-chance that you're working on a Windows Store App, let me know, since my answer will be different.
You should really try and use the standard Navigation services available with WPF. This will give you configurable oage caching and journalling.
http://msdn.microsoft.com/en-GB/library/ms750478(v=vs.100).aspx
Try this:
private void back_button_Click_(object sender, RoutedEventArgs e)
{
Window parentWindow = (Window)this.Parent;
parentWindow.Close();
}
I have the problem with data transfer - i have a wpf application with a splash screen which is runs in App class before main frame is loaded. This Splash is a dialog and App is a static class - how is it possible to pass the data from splash dialog to mainframe maybe via App.. or there is other way?
An event could pass the data about.
public App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var splash = new Splash();
var main = new Main();
splash.SplashViewFinished += (s, data) => {
main.Data = data;
//code to show main..
};
//code to show splash...
}
}
public class Splash : Window
{
public event EventHandler<SplashDataArgs> SplashViewFinished;
}
public class SplashDataArgs: EventArgs
{
}
Or you could use the mediator pattern. Like the Messenger class in MVVM light
http://www.galasoft.ch/mvvm/
Handling Dialogs in WPF with MVVM
http://mvvmlight.codeplex.com/discussions/209338?ProjectName=mvvmlight
My Solution:
So I managed to find another tutorial http://www.codeproject.com/KB/dotnet/Yet_Another_Splash_Screen.aspx and the sourcecode seemed to make more sense to me. Here is the code i'm using now. Main() is left untouched.
Splash.cs
`
public partial class Frm_Splash : Form
{
delegate void ProgressDelegate(int percent);
delegate void SplashShowCloseDelegate();
/// <summary>
/// To ensure splash screen is closed using the API and not by keyboard or any other things
/// </summary>
bool CloseSplashScreenFlag = false;
/// <summary>
/// Base constructor
/// </summary>
///
public Frm_Splash()
{
InitializeComponent();
progress_Splash.Show();
this.ClientSize = this.BackgroundImage.Size;
}
public void ShowSplashScreen()
{
if (InvokeRequired)
{
// We're not in the UI thread, so we need to call BeginInvoke
BeginInvoke(new SplashShowCloseDelegate(ShowSplashScreen));
return;
}
this.Show();
Application.Run(this);
}
/// <summary>
/// Closes the SplashScreen
/// </summary>
public void CloseSplashScreen()
{
if (InvokeRequired)
{
// We're not in the UI thread, so we need to call BeginInvoke
BeginInvoke(new SplashShowCloseDelegate(CloseSplashScreen));
return;
}
CloseSplashScreenFlag = true;
this.Close();
}
/// <summary>
/// Update text in default green color of success message
/// </summary>
/// <param name="Text">Message</param>
public void Progress(int percent)
{
if (InvokeRequired)
{
// We're not in the UI thread, so we need to call BeginInvoke
BeginInvoke(new ProgressDelegate(Progress), new object[] { percent });
return;
}
// Must be on the UI thread if we've got this far
progress_Splash.Value = percent;
// Fade in the splash screen - looks pro. :D
if (percent < 10)
this.Opacity = this.Opacity + .15;
}
/// <summary>
/// Prevents the closing of form other than by calling the CloseSplashScreen function
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void SplashForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (CloseSplashScreenFlag == false)
e.Cancel = true;
}
}`
Form1.cs
public partial class Frm_Main : Form
{
Frm_Splash frm_Splash = new Frm_Splash();
public Frm_Main()
{
this.Hide();
Thread splashthread = new Thread(new ThreadStart(frm_Splash.ShowSplashScreen));
splashthread.IsBackground = true;
splashthread.Start();
InitializeComponent();
CenterToScreen();
}
private void Frm_Main_Load(object sender, EventArgs e)
{
if (PassedAll() == true)
FillMovieLB();
if (FillMovieProgress == 100)
{
//Throw in this sleep so the user can see the progress bar reach all the way to the end.
Thread.Sleep(1000);
this.Show();
frm_Splash.CloseSplashScreen();
this.Activate();
}
}
Original Question
G'day all,
I'm very new to programming in C# and i'm having a problem with the http://www.codeproject.com/KB/cs/prettygoodsplashscreen.aspx tutorial and implementing it within my application. I'm finding it a little difficult to understand what the problem is. I know there is alot of stuff about getting this splash screen to work but I can't get my head around it.
When I start the program, the Frm_Main will display, you can see the listbox being populated, because i've placed it in BackgroundWorker.DoWork(), and then afterwards my frm_Splash will show after the work is done. Obviously, the way it should be working is, frm_Splash will show during the work being done on Frm_Main, and the progress bar will show the progress of the loading (this part I haven't implemented yet).
Edit: I may not have been clear, but the question is: How can I get my splashscreen to display while the work is being done and before the main form is displayed?
Thanks everybody. :)
Here is my code:
static Frm_Splash frm_Splash = new Frm_Splash();
public delegate void ShowFormDelegate();
public void ShowForm()
{
frm_Splash.Show();
}
public Frm_Main()
{
InitializeComponent();
CenterToScreen();
if (PassedAll() == true)
{
back_loadprog.RunWorkerAsync();
}
}
private void back_loadprog_DoWork(object sender, DoWorkEventArgs e)
{
Invoke(new ShowFormDelegate(ShowForm));
Invoke(new FillMovieLBDelegate(FillMovieLB));
}
Here, have some code... Works for me.
Splash Form:
namespace Screens.Forms
{
public partial class Splash : DevExpress.XtraEditors.XtraForm
{
public Splash()
{
InitializeComponent();
}
string RandomLoadingMessage()
{
string[] lines ={
"Pripremam warp pogon",
"Moj drugi ekran za učitavanje je brži, probaj njega",
"Verzija programa koju imam u testiranju imala je smiješnije poruke"
};
return lines[new Random().Next(lines.Length)];
}
public void RandomizeText()
{
lblMessage.Text = RandomLoadingMessage();
}
private void Splash_Load(object sender, EventArgs e)
{
RandomizeText();
}
private static Splash _splash;
private static bool _shouldClose;
static void ThreadFunc()
{
_splash = new Splash();
_splash.Show();
while (!_shouldClose)
{
Application.DoEvents();
Thread.Sleep(100);
if (new Random().Next(1000) < 10)
{
_splash.Invoke(new MethodInvoker(_splash.RandomizeText));
}
}
for (int n = 0; n < 18; n++)
{
Application.DoEvents();
Thread.Sleep(60);
}
if (_splash != null)
{
_splash.Close();
_splash = null;
}
}
static public void ShowSplash()
{
_shouldClose = false;
Thread t = new Thread(ThreadFunc);
t.Priority = ThreadPriority.Lowest;
t.Start();
}
internal static void RemoveSplash()
{
_shouldClose = true;
}
internal static void ShowSplash(List<string> fromTwitterMessages)
{
ShowSplash();
}
}
}
Show it with:
Splash.ShowSplash();
Do the work you need, then when done:
Splash.RemoveSplash();
You need to take this a step further back to your Main() function of the application.
In general you could do this:
Create a ManualResetEvent or better ManualResetEventSlim if you are on .NET 4
Start a new thread displaying your SplashScreen, use Application.Run
In your SplashScreen you should create a time which polls the created ManualResetEvent
frequently, a nice animation could be placed here also
If the event is set you should close the form
Back in your Main() do your stuff like creating forms etc.
When finish set the event, so that the SplashScreen can be closed
To be sure that your MainForm is not shown before your SplashScreen is closed you can use another event