I need to have a PNG (with transparency) as a splash screen. The transparent portions of the image should be clear so the user can see any windows behind it (or desktop).
I also need to display the splash screen for 5 seconds (the contract specifically says 5 seconds) and it can't be any shorter. I am aware of the build property in VS 2010 but the splash screen comes and goes too quick (less than 5 seconds).
What can I do to make it stay 5 seconds (approximately)
I had a similar problem, where i couldn't use the built-in splashscreen option, on a WPF project.
That project is now open source, you have have a look here: https://code.google.com/p/theomniscientchimp/
It's an auto-updater (there are a few things you don't need i guess).
This is the minimum you should need:
WPF side:
<Window x:Class="TheOmniscientChimp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CustomXaml"
Icon="SC2_Replay_Monkey.ico"
Title="MainWindow" Height="256" Width="456" Background="#00005555" AllowsTransparency="True" WindowStyle="None" WindowStartupLocation="CenterScreen" >
<Grid Width="Auto">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Image Name="splashScreenImage" Stretch="Fill" Grid.Row="0" />
</Grid>
</Window>
C# side (code behind):
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
BitmapImage splashScreenImageSource = new BitmapImage();
splashScreenImageSource.BeginInit();
splashScreenImageSource.UriSource = new Uri("Your_Image.png", UriKind.Relative);
splashScreenImageSource.EndInit();
splashScreenImage.Source = splashScreenImageSource;
}
public void AsynchronousExit()
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
worker.RunWorkerAsync();
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
//Makes the thread wait for 5s before exiting.
Thread.Sleep(5000);
}
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Environment.Exit(0);
}
}
Tell me if you need help to adjust.
FB.
If i had to do it i would add a window and set its properties AllowsTransparency = true; set it to start before all forms i mean before loading this can be done by modifying App.xml and set Startup="Application_Startup
To disable the top defauld control you have to set WindowStyle = none
and there in its code
private void Application_Startup(object sender, StartupEventArgs e)
{
MainWindow mn = new MainWindow();
mn.ShowDialog();
}
use timer to do what ever you want
private DispatcherTimer timer;
timer = new DispatcherTimer();
timer.Tick += new EventHandler(timer_Tick);
timer.Interval = TimeSpan.FromMilliseconds(5000);
timer.IsEnabled = true;
void timer_Tick(object sender, EventArgs e)
{
///Close your window here
}
hope this helps
I had the same problem, buts it's actually suprisingly easy to solve without anything special:
add your image in to the main project and set the build property to Splash Screen.
add a Thread.Sleep(5000) into the constructor for your app's main window right before the InitializeComponents() call.
This will delay the loading of your main wpf window, and the splash screen will stay up for at least the load time + the time of the sleep before the main window pops up and the splash goes away.
You'll probably have to do something similar to what you'd do in WinForms. Spin up a new thread and start a Wpf Application on that thread. In WinForms you'd do this by using System.Windows.Forms.Application.Run(new SplashForm()); Should be something similar to this in Wpf.
EDIT:
I found this, so it should work. http://msdn.microsoft.com/en-us/library/ms597011.aspx
The key is to do this ON A SEPARATE THREAD. The thread the application starts is is already tied to one WPF application and you want your splash to basically be its own GUI. The form can then have a timer that causes it to close itself, which should automatically terminate this other application.
If you use want a splash screen window and use the background worker, you make your main code more complex as it is not necessary. The solution is to write the main code as a normal synchronous code style in main UI thread while showing the splash window until you close it, and you can send an update to change the splash screen loading text or progress bar.
SplashWindow window = null;
var thread = new Thread(new ThreadStart(() =>
{
Debug.Print("Thread begin");
window = new SplashWindow();
window.ShowDialog();
}));
thread.Name = "RunSplashWindow";
thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = true;
thread.Start();
Debug.Print("App begin");
Thread.Sleep(1000);
if (window != null)
{
window.Dispatcher.BeginInvoke(new Del(() => {
window.SetHeader("Running...");
}), new object[0]);
}
Thread.Sleep(1000);
for (int i = 1; i <= 100; i++)
{
if (window != null)
{
window.Dispatcher.BeginInvoke(new Del(() =>
{
window.SetProgress((double)i);
}), new object[0]);
}
Thread.Sleep(10);
}
Thread.Sleep(1000);
Debug.Print("App end");
if (window != null)
{
window.Dispatcher.BeginInvoke(new Del(() =>
{
window.Close();
}), new object[0]);
}
Related
-Updated--14/10 also asked this question
To give some clear idea of what is going on and taking into account the comments and from this article here
What I really want to do now is invoke a new form with a progress bar on it and have that run and animate whilst my back ground thread runs my long process to the database and then invoke a close form event
The background worker is set up here
public partial class MainWindow : Window
{
//Declare background workers
BackgroundWorker bw = new BackgroundWorker();
BackgroundWorker bwLoadCSV = new BackgroundWorker();
BackgroundWorker bwProgressBar = new BackgroundWorker();
Then delegates added here
public MainWindow()
{
bwLoadCSV.WorkerReportsProgress = true;
bwLoadCSV.WorkerSupportsCancellation = true;
bwLoadCSV.DoWork += new DoWorkEventHandler(bwLoadCSV_DoWork);
bwLoadCSV.ProgressChanged += new ProgressChangedEventHandler(bwLoadCSV_ProgressChanged);
bwLoadCSV.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bwLoadCSV_RunWorkerCompleted);
The call is made here from the main window class
private void CSV_Load_Click(object sender, RoutedEventArgs e)
///Function to read csv into datagrid
///
{
//Turn Cursor to wait
System.Windows.Forms.Cursor.Current = System.Windows.Forms.Cursors.WaitCursor;
//Test connection to sql server
if (CHHoursDataProvider.IsDatabaseOnline() == false)
{
System.Windows.Forms.MessageBox.Show("Can not establish contact with sql server" + "\n" + "Contact IT", "Connection Error");
//Set UI picture
return;
}
//Set a control to update the user here
tbLoadDgStat.Visibility = Visibility.Visible;
tbLoadDgStat.Text = "Getting data templete from Database...";
string FilePath = txFilePath.Text;
if (bwLoadCSV.IsBusy != true)
{
//load the context object with parameters for Background worker
bwCSVLoadContext Context = new bwCSVLoadContext();
Context.Site = cBChSite.Text;
Context.FilePath = txFilePath.Text;
Context.FileName = fileTest;
Context.Wageyear = cbWageYear.Text;
Context.Startdate = ((DateTime)dpStartDate.SelectedDate);
Context.Enddate = ((DateTime)dpEndDate.SelectedDate);
bwLoadCSV.RunWorkerAsync(Context);
}
The background worker do work is
private void bwLoadCSV_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
bwCSVLoadContext Context = e.Argument as bwCSVLoadContext;
worker.ReportProgress((10));
if ((worker.CancellationPending == true))
{
e.Cancel = true;
}
else
{
// Perform a time consuming operation and report progress load csv into datagrid.
To report the background work I do this. This is where I am trying to load a new form call ProgressDialog which has a progress bar on it, which I am try set to Indeterminable so it just "swishes" across my ProgressDialoge form to show the user something is still going on. I have used the reporter part of the background work because I believe it has access to the main window thread and I am hoping that the invoke method is then called from the main window thread, but I am not really sure
Here is the reporter
private void bwLoadCSV_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.Dispatcher.Invoke((MethodInvoker)delegate { tbLoadDgStat.Visibility = Visibility.Visible; });
//tbLoadDgStat.Visibility = Visibility.Visible;
//this.progressBar1.Value = e.ProgressPercentage;//This works but pauses on long steps
if (e.ProgressPercentage == 10)
{
//Try to open a new form with a class ProgressDialog and set the progressbar
// on the frm to IsIndeterminate=true
//THIS IS NOT WORKING
this.Dispatcher.BeginInvoke (new Action(() =>
{ ProgressDialog progressDialog = new ProgressDialog();
progressDialog.SetIndeterminate(true);
}));
//this updates the main form OK
this.Dispatcher.Invoke((MethodInvoker)delegate { tbLoadDgStat.Text = "Getting data templete from Database..."; });
}
else if (e.ProgressPercentage == 20)
{
this.Dispatcher.Invoke((MethodInvoker)delegate { tbLoadDgStat.Text = "Data template retrieved..."; });
}
else
{
if (e.ProgressPercentage % 10 == 0)
{
this.Dispatcher.Invoke((MethodInvoker)delegate { tbLoadDgStat.Text = "Adding Data..." + e.ProgressPercentage.ToString() + "%"; });
}
}
Lastly the xaml for the ProgressDialog Form and it's class
<Window x:Class="Test_Read_CSV.ProgressDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Progress Dialog" Height="115" Width="306" Name="ProgressPopup">
<Grid>
<ProgressBar Height="31" HorizontalAlignment="Left" Margin="12,33,0,0" Name="progressBar1" VerticalAlignment="Top" Width="250" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="7,4,0,0" Name="tbEvent" VerticalAlignment="Top" Width="254" IsReadOnly="True" IsEnabled="False" />
</Grid>
class
/// <summary>
/// Interaction logic for ProgressDialog.xaml
/// </summary>
public partial class ProgressDialog : Window
{
public ProgressDialog()
{
WindowStartupLocation = WindowStartupLocation.CenterScreen;
InitializeComponent();
}
public ProgressDialog(String Purpose)
{
InitializeComponent();
tbEvent.Text = Purpose;
WindowStartupLocation = WindowStartupLocation.CenterScreen;
}
public void UpdateProgress(int progress)
{
progressBar1.Dispatcher.BeginInvoke(
new Action(() =>
{
progressBar1.Value = progress;
}
));
}
public void SetIndeterminate(bool isIndeterminate)
{
progressBar1.Dispatcher.BeginInvoke(
new Action(() =>
{
if (isIndeterminate)
{
progressBar1.IsIndeterminate = true;
}
else
{
progressBar1.IsIndeterminate = false;
}
}
));
}
}
}
I have read and done a number of tutorial on background worker and even some on threads but can not seem to get the result I want
The idea is I have two long processes where I am either getting a datatable clone from my remote bd or I am updating the db from my wpf application (.net 4). While the process is running I want a progress bar control and to update it, for the obivous reason of making it clear that some work is going on. So I did the usual report progress routines in the background worker and it works....However, in my dowork thread I have this command
CHHoursDataProvider CH = new CHHoursDataProvider();
oTable = CH.CloneCHHours();
this where the communication with db is and this command takes a good 60 - 90 secs on a vpn remote connection so even if I do this
CHHoursDataProvider CH = new CHHoursDataProvider();
worker.ReportProgress((10));
oTable = CH.CloneCHHours();
worker.ReportProgress((20));
The main window still looks frozen and like it has crashed!
So all I want to do is at the start of the call to the background work is set a progressbar running and leave it running till the end of the task. This is all I need to do to finish my first ever project and after three days I still can not get my head around it!
So I have tried the follow
In the bw progress changed and in the main window class
this.progressBar2.IsIndeterminate = true;
However the animation does not start till the Dowork thread has finished.
I then created another background worker to do update the progressbar2, which linked to a button on the main window was ok, but as soon as I tried to use it from the other background worker or from the main window class did not run till the dowork thread had completed on the first background worker
I then tried to follow a invoke method but REALLY got lost on that!
So can anyone help I can guess it is something to do with threading and working on the wrong thread etc but what I do about it I have no clue.
I can post more code as needed
Ian
As you haven't shown your full BackgroundWorker code, I can't tell if you have implemented it correctly. As such, all I can do is to show you a simple working example of updating a ProgressBar control:
UserControl XAML:
<UserControl x:Class="WpfApplication1.Views.TestView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" Loaded="UserControl_Loaded">
<ProgressBar x:Name="progressBar" Height="25" Margin="20" Minimum="0"
Maximum="50" />
</UserControl>
MainWindow XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Views="clr-namespace:WpfApplication1.Views"
Title="MainWindow" Height="350" Width="525">
<Views:TestView />
</Window>
UserControl code behind:
using System.ComponentModel;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
namespace WpfApplication1.Views
{
public partial class TestView : UserControl
{
private BackgroundWorker backgroundWorker = new BackgroundWorker();
public TestView()
{
InitializeComponent();
backgroundWorker.WorkerReportsProgress = true;
backgroundWorker.ProgressChanged += ProgressChanged;
backgroundWorker.DoWork += DoWork;
// not required for this question, but is a helpful event to handle
backgroundWorker.RunWorkerCompleted += BackgroundWorker_RunWorkerCompleted;
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
backgroundWorker.RunWorkerAsync();
}
private void DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i <= 100; i++)
{
// Simulate long running work
Thread.Sleep(100);
backgroundWorker.ReportProgress(i);
}
}
private void ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// This is called on the UI thread when ReportProgress method is called
progressBar.Value = e.ProgressPercentage;
}
private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// This is called on the UI thread when the DoWork method completes
// so it's a good place to hide busy indicators, or put clean up code
}
}
}
I have created a splash screen form for my wpf application. I am trying to show the splash screen while the main form is being loaded. I have created a thread in main form's constructor which shows the splash screen and this thread is aborted when the main form is fully loaded. The problem is that the SplashThread.Abort() crashes my application. So, is there some thing wrong in my approach for the splash screen or in my coding. I want to show the splash screen only for the time taken by the main form to be loaded.
Following is my piece of code.
private Thread splashThread;
public App()
{
splashThread = (new Thread(() => (new BDTSplashscreen()).ShowDialog()));
splashThread.SetApartmentState(ApartmentState.STA);
splashThread.Start();
this.Loaded += App_Loaded;
this.Closing += App_Closing;
try
{
//My Stuff
}
catch (Exception ex)
{
System.Windows.MessageBox.Show(ex.Message);
}
}
private void App_Loaded(object sender, RoutedEventArgs e)
{
splashThread.Abort();
this.Activate();
}
This solution may not be appropriate for you, but it might help others.
If your splash screen is simple, i.e., only an image without any dynamic text, then the easiest implementation is to add the image to your project and change the Build Action to SplashScreen in the 'Properties' pane.
This will create a very lightweight splash screen that starts when you launch your application. It uses unmanaged Win32, so it will display sooner than a WPF-based splash screen, which must wait for several assemblies to load and some code to JIT.
When your main window is shown, the splash screen will automatically be hidden with a nice fade-out effect. It's fast and easy.
You might even be able to combine this simple splash screen with a more advanced one. Show the simple one first, and then launch your own with the same size, position, and background image. As the simple one fades out, it should look like the extra content is fading in. See #mm8's answer for how to handle the threading.
This is how i did it:
create a new view with this xaml and code behind:
<Grid>
<Label x:Name="ProgressMessage" Background="LightSteelBlue" Height="28" Margin="19,0,17,15" VerticalAlignment="Bottom"
Foreground="White" ></Label>
</Grid>
public partial class Splash : Window
{
public Splash()
{
InitializeComponent();
}
async public Task ReceiveMessageAsync(string message)
{
await Dispatcher.InvokeAsync(() =>
{
ProgressMessage.Content = message;
});
}
public void ReceiveMessage(string message)
{
ProgressMessage.Content = message;
}
}
then in your main method you call it:
splash = new Splash();
splash.Show();
do your task and use the Receive message function to show the progress of your loading tasks:
splash.ReceiveMessage("I'm Loading !!");
then, when your loading task are completed you just close it using:
splash.Close();
and launch your MainWindow.
Just a couple of notes:
your main method should of course be set as Start method for your application.
Your main method should also be wrapped by this:
Thread app = new Thread((ThreadStart)delegate
{
});
app.SetApartmentState(ApartmentState.STA);
app.Start();
You could override the OnStartup method of your App.xaml.cs, show the splash screen on a background thread, keep a reference to the splash screen window and then simply close it once the main window has been loaded. Something like this:
protected override void OnStartup(StartupEventArgs e)
{
Window tempWindow = null;
Thread thread = new Thread(new ThreadStart(() =>
{
SynchronizationContext.SetSynchronizationContext(
new DispatcherSynchronizationContext(Dispatcher.CurrentDispatcher));
tempWindow = new BDTSplashscreen();
tempWindow.WindowStyle = WindowStyle.None;
tempWindow.Closed += (ss, ee) => Dispatcher.CurrentDispatcher.BeginInvokeShutdown(DispatcherPriority.Background);
tempWindow.Show();
Dispatcher.Run();
}));
thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = true;
thread.Start();
//show the main window
MainWindow mw = new MainWindow();
mw.Loaded += (ss, ee) => tempWindow.Dispatcher.Invoke(() => tempWindow.Close());
mw.Show();
}
I have a problem which i have had for over a week now and I can't figure out why.
i have a waiting screen which I've placed in a different thread so it can load and show it's self even if the code is doing loads of work (when i did evrything in the same thread the screen wouldn't show it's self and i think that's because the code was to busy doing other stuff)
however it doesn't always freeze just some times and only on the tablet, it works fine on my pc (at least i have not noticed it freezing while testing it on my pc).
i've tried to 'hack' some workarounds in it, for example placing a cancel button on the waiting screen so it can be closed however it cannot be clicked (asif it freezes and doesn't respond)
1 time i also thought it was giving the problem because i would close it before the thread was started so i made a boolean which would say it if the thread was still loading or not if it was and i tried to close it i would add an event listner so it would be closed when the thread had finished starting up.
anyway here's the code, i do hope someone here can help me out.
public partial class WaitWindow : Window
{
//private static WaitWindow ww = new WaitWindow();
private static Thread thread;
private static event ThreadStartingEvent started;
private delegate void ThreadStartingEvent(object sender, EventArgs e);
public static bool disposable = false;
private static bool startingThread;
private static bool StartingThread
{
get
{
return startingThread;
}
set
{
startingThread = value;
if (!startingThread && started != null)
{
started(null, new EventArgs());
}
}
}
// To refresh the UI immediately
private delegate void RefreshDelegate();
private static void Refresh(DependencyObject obj)
{
obj.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Render,
(RefreshDelegate)delegate { });
}
public WaitWindow()
{
InitializeComponent();
}
public static void BeginDisplay()
{
if (thread == null)
{
startingThread = true;
thread = new Thread(() =>
{
WaitWindow ww = new WaitWindow();
ww.Show();
ww.Closed += (sender2, e2) =>
ww.Dispatcher.InvokeShutdown();
System.Windows.Threading.Dispatcher.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
startingThread = false;
}
}
public static void EndDisplay()
{
if (startingThread)
{
started += new ThreadStartingEvent(WaitWindow_started);
}
if (thread != null)
{
disposable = false;
thread.Abort();
thread = null;
}
}
static void WaitWindow_started(object sender, EventArgs e)
{
thread.Abort();
thread = null;
started = null;
}
private void button1_Click(object sender, RoutedEventArgs e)
{
if (disposable)
{
disposable = false;
thread.Abort();
thread = null;
}
}
}
and the xaml code:
<Window x:Class="WpfApplication1.WaitWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
Title="WaitWindow" WindowStyle="None" ResizeMode="NoResize" WindowStartupLocation="CenterScreen"
Background="Transparent" AllowsTransparency="True"
Width="1024" Height="640">
<Grid>
<xctk:BusyIndicator Name="BusyBar" IsBusy="True" BusyContent="Even geduld a.u.b.">
</xctk:BusyIndicator>
<Button Name="button1" Width="28" HorizontalAlignment="Left" Margin="550,271,0,0" Height="28" VerticalAlignment="Top" Click="button1_Click">X</Button>
</Grid>
</Window>
maby some extra info: when i am doing tasks which might take some time (getting data from a remote database) i call BeginDisplay() and when the code is done i call EndDisplay()
this seemed rather obvious to me but i suppose there's no harm in mentioning it.
EDIT:
i should probably mention i'm using .net framework 3.5
If you are doing background asynchronous tasks you will need to create a SynchronizationContext. Perhaps this is the cause of the problem?
// Create a thread
Thread newWindowThread = new Thread(new ThreadStart( () =>
{
// Create our context, and install it:
SynchronizationContext.SetSynchronizationContext(
new DispatcherSynchronizationContext(
Dispatcher.CurrentDispatcher));
Window1 tempWindow = new Window1();
// When the window closes, shut down the dispatcher
tempWindow.Closed += (s,e) =>
Dispatcher.CurrentDispatcher.BeginInvokeShutdown(DispatcherPriority.Background);
tempWindow.Show();
// Start the Dispatcher Processing
System.Windows.Threading.Dispatcher.Run();
}));
// Set the apartment state
newWindowThread.SetApartmentState(ApartmentState.STA);
// Make the thread a background thread
newWindowThread.IsBackground = true;
// Start the thread
newWindowThread.Start();
This code is pinched from this blog article, which is worth a good read.
Normally, you would move the processing that is locking the UI off to a background thread and keep the main thread free for all UI updates which you get back to by Dispatcher.Invoke(), so what you have here is a very different implementation.
The first thing that jumps out at me is the use of Thread.Abort() I'm pretty sure the MSDN documentation basically says don't use it unless your thread has no other means of stopping. In particular, if you have aborted the thread, what is going to be left around to close the open window. Could this be your problem?
I have an application that at its heart is a keyboard hook. This hook allows me to turn on and off some features on a Tablet that the company I work for manufactures. The program started off as a WinForms application and the developer who first made it is no longer working with this company. Long story short there were some bugs in the program that needed to get fixed so I've been spending some time cleaning up the code base, and updating the UI to have a more fresh feeling to it. One of the WinForms window was to display at the bottom center of the screen for 500ms with a picture of the option that you just modified. For instance if you just disabled bluetooth a grayed out bluetooth symbol would show up on the bottom of the screen.
Well I've been on a kick lately to stop using Winforms all together and I wanted the challenge of updating the UI to use WPF. I came up with this code to replace the on screen window that was shown before (actual xaml not shown as that isn't the problem)
public partial class OnScreenDisplayDialog : Window
{
public static void ShowUserUpdate(Enums.OnScreenDisplayOptions target)
{
var f = new OnScreenDisplayDialog();
var imageSource = GetPictureFromOptions(target);
f.targetImage.Source = new BitmapImage(new Uri(imageSource, UriKind.Relative));
f.Show();
f.Top = SystemParameters.PrimaryScreenHeight - f.ActualHeight;
f.StartCloseTimer();
}
private OnScreenDisplayDialog()
{
InitializeComponent();
}
private void StartCloseTimer()
{
DispatcherTimer timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromMilleseconds(500);
timer.Tick += TimerTick;
timer.Start();
}
private void TimerTick(object sender, EventArgs e)
{
DispatcherTimer timer = (DispatcherTimer)sender;
timer.Stop();
timer.Tick -= TimerTick;
var sb = new Storyboard();
var fadeout = new DoubleAnimation(0, new Duration(TimeSpan.FromSeconds(1)));
Storyboard.SetTargetProperty(fadeout, new System.Windows.PropertyPath("Opacity"));
sb.Children.Add(fadeout);
sb.Completed += closeStoryBoard_Completed;
this.BeginStoryboard(sb);
}
private void closeStoryBoard_Completed(object sender, EventArgs e)
{
this.Close();
}
}
So the idea is that I only want to make a single call to this Window like this
Forms.OnScreenDisplayDialog.ShowUserUpdate(Enums.OnScreenDisplayOptions.BluetoothOn);
The problem that I appear to be having is that with a simple test like the code below I get some unexpected results
Forms.OnScreenDisplayDialog.ShowUserUpdate(Enums.OnScreenDisplayOptions.BluetoothOn);
System.Threading.Thread.Sleep(1000);
Forms.OnScreenDisplayDialog.ShowUserUpdate(Enums.OnScreenDisplayOptions.BrightnessDown);
System.Threading.Thread.Sleep(1000);
Forms.OnScreenDisplayDialog.ShowUserUpdate(Enums.OnScreenDisplayOptions.BrightnessUp);
So i would expect that first window to show for 500ms then dissappear. 500ms later the next window shows (repeat). but instead all 3 windows remain visible, and even after about 10 seconds the windows are still visible. If I click elsewhere (so that they loose focus) they close. And I can't for the life of me figure out why. I put a break point on the Storyboard completed method and all 3 complete at the same time (which makes me think the timer is static, instead of dynamic).
Can someone please help me figure out what I am doing wrong here?
EDIT
So as my comment mentioned I wanted to show what I put in my code to test. There are 2 things here. One is from the main entry point in my program for quick and easy testing. The second is ran after my keyboard hook is setup and running. Normally I filter out the keys being pressed and act accordingly, but in this case if a key is pressed or released I open this window. So here is the main entry point portion of code
[STAThread]
static void Main()
{
InstantiateProgram();
EnsureApplicationResources();
if (true)
{
new System.Threading.Thread(Test).Start();
System.Threading.Thread.Sleep(1000);
new System.Threading.Thread(Test).Start();
System.Threading.Thread.Sleep(1000);
new System.Threading.Thread(Test).Start();
}
singleton = new System.Threading.Mutex(true, "Keymon");
if (!singleton.WaitOne(System.TimeSpan.Zero, true)) return;
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
System.Windows.Forms.Application.Run(main);
}
private static void Test()
{
Console.Beep(1000, 200);
System.Windows.Application.Current.Dispatcher.Invoke(new Action(delegate
{
Forms.OnScreenDisplayDialog.ShowUserUpdate((Enums.OnScreenDisplayOptions)r.Next(0,7));
}));
//System.Threading.Thread.Sleep(1000);
}
static Random r = new Random();
public static void EnsureApplicationResources()
{
if (System.Windows.Application.Current == null)
{
// create the Application object
new System.Windows.Application();
System.Windows.Application.Current.ShutdownMode = ShutdownMode.OnExplicitShutdown;
// merge in your application resources
System.Windows.Application.Current.Resources.MergedDictionaries.Add(
System.Windows.Application.LoadComponent(
new Uri("/Themes/Generic.xaml",
UriKind.Relative)) as ResourceDictionary);
}
}
private static void InstantiateProgram()
{
System.Windows.Forms.Application.EnableVisualStyles();
System.Windows.Forms.Application.SetCompatibleTextRenderingDefault(false);
main = new frmMain();
}
Here is the code I mentioned that is used after the main form is initialized
public frmMain()
{
InitializeComponent();
theHook = new KeyboardHookLibrary.KeyboardHook();
theHook.KeyDown += theHook_KeyDown;
theHook.KeyUp += theHook_KeyUp;
theHook.Start();
}
~frmMain()
{
theHook.Dispose();
}
void theHook_KeyUp(int key)
{
Forms.OnScreenDisplayDialog.ShowUserUpdate(Enums.OnScreenDisplayOptions.BrightnessDown);
}
void theHook_KeyDown(int key)
{
Forms.OnScreenDisplayDialog.ShowUserUpdate(Enums.OnScreenDisplayOptions.BluetoothOn);
}
and just in case I'll include xaml of the On screen display
<Window x:Class="XPKbdMon.Forms.OnScreenDisplayDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
Height="200"
Width="200"
WindowStyle="None"
AllowsTransparency="True"
Background="Black"
ShowInTaskbar="False">
<!--WindowStartupLocation="CenterScreen"-->
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image x:Name="targetImage" />
<ProgressBar x:Name="brightnessBar" Grid.Row="1" Visibility="Collapsed" Height="30"/>
</Grid>
</Window>
I think the problem lies in the test code rather than in the actual animation code. You're calling Thread.Sleep(1000) on the main (GUI) thread, right? That will effectively block both animations and the DispatcherTimer.
Try running your test code on a different thread. You'll need to make sure that the calls to ShowUserUpdate are run on the GUI thread, though, using Dispatcher.Invoke or BeginInvoke.
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.");
}
}
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".
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();
}
}
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("http://stackoverflow.com/questions/13213625/splashscreen-closetimespan-frommilliseconds-listen-for-closed-event");
}
}
}
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).
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";
}
}
}
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