Creating and changing splash screen in WPF - c#

I have following Code:
Thread thread = new Thread(new ThreadStart(CreateSplashScrn));
thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = true;
thread.Start();
OpenSplashScrn();
ChangeSplashScrnMessageText("String");
public void CreateSplashScrn()
{
splash = new SplashScreen(this);
System.Windows.Threading.Dispatcher.Run();
}
public void OpenSplashScrn()
{
splash.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal,
new Action(() => { splash.Show(); }));
}
public void ChangeSplashScrnMessageText(string messageText)
{
splash.messageLabel.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal,
new Action(() => { splash.messageLabel.Content = messageText; }));
}
However, this returns a Null Reference Exception at OpenSplashScrn().
How can I open this in another thread and change the Label content?
Is this possible over a Task?

You should NOT open splach screen in background thread and perform the long running initialization in UI thread.
You should open splash screen in UI thread and perform the long running initialization in non UI thread.
var splash = new SplashScreen(this);
splash.Show();
Thread thread = new Thread(new ThreadStart(Initialize));
thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = true;
thread.Start();
public void Initialize()
{
//move your long running logic from your app here..
ChangeSplashScrnMessageText("Initialization Started");
Thread.Sleep(1000);
ChangeSplashScrnMessageText("Initialize finished");
}
public void ChangeSplashScrnMessageText(string messageText)
{
splash.messageLabel.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal,
new Action(() => { splash.messageLabel.Content = messageText; }));
}
EDIT: Why you should not open Splash Screen in another thread?
Because it complicates things a log and in 99% there is no reason to do so. You can have multiple windows running in single thread and still perform some long running task in the background.
I guess, that in your main window you are trying to perform the long running task in UI thread. Just move it to background thread...

Related

Running splash dialog while running heavy task in another dialog in WPF

I'm trying to implement a task as described in the caption.
The HeaviWindow performs some heavy task and so not displayed until the task finished. This time I want to display a splash screen with animated loader.
Till now I used the following construction:
private RunHeavyTask()
{
ShowSplashScreen("Please wait...");
var dialog = new HeaviWindow();
dialog.ShowDialog();
}
private void ShowSplashScreen(string str)
{
Thread newWindowThread = new Thread(new ParameterizedThreadStart(ThreadStartingPoint));
newWindowThread.SetApartmentState(ApartmentState.STA);
newWindowThread.IsBackground = true;
newWindowThread.Start(str);
}
private void ThreadStartingPoint(object str)
{
splash = new WaitWindow((string)str);
splash.Show();
System.Windows.Threading.Dispatcher.Run();
}
But suddenly it turned out that while running the thread all other background threads stop to work.
I've tried to use Task for this purpose:
private void ShowSplashScreen(string str)
Task.Run(() =>
{
Application.Current.Dispatcher.Invoke(delegate
{
splash = new WaitWindow((string)str);
splash.ShowDialog();
});
});
}
But splash screen not displayed until the heavy dialog finish the task. The same result if I use BackgroundWorker.
So my question - how can I display a splash dialog while running a heavy task in another one. The splash dialog use some animation so it need to be updated.
Try this:
Window splash;
private void RunHeavyTask()
{
ShowSplashScreen("Please wait...");
//simulate "heavy task" by sleeping for 5 seconds
Thread.Sleep(5000);
//close the splash window
splash.Dispatcher.BeginInvoke(() => splash.Close());
}
private void ShowSplashScreen(string str)
{
Thread newWindowThread = new Thread(new ThreadStart(() =>
{
SynchronizationContext.SetSynchronizationContext(
new DispatcherSynchronizationContext(
Dispatcher.CurrentDispatcher));
//pass str to your custom Window here...
splash = new Window() { Content = new ProgressBar() { IsIndeterminate = true } };
splash.Closed += (s, e) =>
Dispatcher.CurrentDispatcher.BeginInvokeShutdown(DispatcherPriority.Background);
splash.Show();
Dispatcher.Run();
}));
newWindowThread.SetApartmentState(ApartmentState.STA);
newWindowThread.IsBackground = true;
newWindowThread.Start();
}
It will display a window with an indeterminate ProgressBar on a background thread while the thread that calls RunHeavyTask() is blocked.
It's not very clear what exactly you are trying to do. More details would be nice as it seems that your general approach needs to be optimized.
Generally you don't start the Window in a separate thread to execute heavy work (CPU bound) on the UI thread. Rather would you execute the CPU intensive task on a background thread. You can wait for this task asynchronously and then close the splash screen afterwards when the task has completed.
The following simple example shows the pattern to be used to execute the CPU bound work asynchronously on a background thread to keep the UI thread responsive, while using IProgress<T> to access the UI thread from the background thread using a delegate registered with an instance of Progress<T>.
The example assumes that the splash screen is showing some progress and for this purpose exposes a Progress property:
// Show a splash screen,
// while executing CPU bound work asynchronously on a background thread
private async Task RunHeavyTaskAsync()
{
splashScreen = new Window();
splashScreen.Show();
// Use IProgress<T> to access the UI thread via a delegate,
// which is registered using the constructor of Progress<T>
var progressReporter = new Progress<double>(value => splashScreen.Progress = value);
// Pass the IProgress<T> instance to the background thread
// and wait non-blocking (asynchronously) for the thread to complete
await Task.Run(() => DoWork(progressReporter));
// Close splash screen after 5s heavy work
splashScreen.Close();
}
// Will be executed on a background thread
private void DoWork(IProgress<double> progressReporter)
{
// Post progress from background thread (current context)
// to the UI thread (splash screen).
// IProgress<T>.Report() will execute the previously registered delegate
progressReporter.Report(50);
// Do 5s heavy work
Thread.Sleep(5000);
progressReporter.Report(100);
}

how to avoid Server busy window if used thread.join() in wpf c#

I have a c++ application and use clr to call the below c# method. Everything is going good except for one thing. As wpf window needs STA thread, I'm trying to create a new thread with STA state and start it. This makes the wpf window modeless, even if the window is started with show dialog. So I tried using thread.join() to make the caller thread to wait until the thread completes or window closes. using thread.join() shows server busy window after a few seconds. (I'm not doing any operation related to the internet in my application).
How to make the caller thread to wait until the window closes? or How to get rid of Server busy window?
void ShowWindow()
{
_ownerHandle = Process.GetCurrentProcess().MainWindowHandle;
Thread newWindowThread = new Thread(new ThreadStart(() =>
{
MyWpfWindow window = new MyWpfWindow();
MyWpfWindowViewModel vm = new MyWpfWindowViewModel();
WindowInteropHelper helper = new WindowInteropHelper(window);
helper.Owner = _ownerHandle;
window.DataContext = vm;
window.ShowDialog();
}));
// set the apartment state this will only invoke the WPF window
newWindowThread.SetApartmentState(ApartmentState.STA);
newWindowThread.IsBackground = true;
// start the thread
newWindowThread.Start();
//waits this thread here untill the newWindowThread execution completes.
newWindowThread.Join();
}
Also, I tried with a while loop as below to block the current main thread.
while(!windowShown)
{
}
in the place of newWindowThread.join(). After using this, my window is not visible.
I'm getting a server busy window as below
Calling Join() blocks the current thread. If you want to wait asynchronously, you could use a SemaphoreSlim:
static async Task ShowWindowAsync()
{
SemaphoreSlim semaphore = new SemaphoreSlim(0, 1);
_ownerHandle = Process.GetCurrentProcess().MainWindowHandle;
Thread newWindowThread = new Thread(new ThreadStart(() =>
{
MyWpfWindow window = new MyWpfWindow();
MyWpfWindowViewModel vm = new MyWpfWindowViewModel();
WindowInteropHelper helper = new WindowInteropHelper(window);
helper.Owner = _ownerHandle;
window.DataContext = vm;
window.Closed += (s, e) =>
{
semaphore.Release();
semaphore.Dispose();
};
window.ShowDialog();
}));
// set the apartment state this will only invoke the WPF window
newWindowThread.SetApartmentState(ApartmentState.STA);
newWindowThread.IsBackground = true;
// start the thread
newWindowThread.Start();
//waits this thread here untill the newWindowThread execution completes.
await semaphore.WaitAsync();
}

C# Dispatcher and progressbar blocked

I'm new with C# and I want to show a loading window with an indeterminate progressbar during the execution of a Dispatcher.BeginInvoke, I try something like this:
progressbarwin = new Windows1();
Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.ContextIdle, (Action)delegate
{
//Do something
progressbarwin.Close();
});
progressbarwin.ShowDialog();
The window is showed (before the delegate) but the progressbar is blocked. What can I do?
#Sinatr
I tried to open the Windows from a new thread UI:
Thread loadT = new Thread(new ThreadStart(() =>
{
Loading progressbarwin = new Loading();
progressbarwin.ShowDialog();
}));
// Set the apartment state
loadT.SetApartmentState(ApartmentState.STA);
// Make the thread a background thread
loadT.IsBackground = true;
// Start the thread
loadT.Start();
//Do something
loadT.Abort();
but it gives me a cross thread exception when i create the window (new Loading())
#Mitra M
It's the same :(

How to wait a thread out of that thread without freeze my APP?

I'm running some scripts in runtime, but it's freezing my UI, I'm calling the CodeProvider inside a Thread, but it still freezing.
In my form I call:
var mre = new ManualResetEvent(false);
Thread tr = new Thread(() =>
{
Script sp = new Script();
code = textBox.Text;
sp.Comp(code);
mre.Set();
});
tr.Start();
mre.WaitOne();
I'm using the mre.WaitOne() because I want to wait the thread finish to keep running my code.
Tried to use the same way inside the Compile method too:
public bool Comps(string code)
{
var mre = new ManualResetEvent(false);
Thread tr = new Thread(() =>
{
//Code to generate a CompilerResult and generate the assembly
Run();
mre.Set();
});
tr.Start();
mre.WaitOne();
return true;
}
But while it's waiting it still freezing the UI.
Any ideas?
Thanks
I'm using the mre.WaitOne() because I want to wait the thread finish
to keep running my code.
What did you expect to happen if you force the calling thread to freeze until your processing thread has completed processing? Doing it this way, there is no point in having that extra thread and if the calling thread is the UI thread, of course it will freeze.
If you do background processing you cannot wait for the result synchronously, instead you have to notify the UI in some sort of fashion that the processing is done, i.e. using a callback or dispatching the result back to the UI in some other form.
The entire point of multi-threading is to allow the Thread to execute on it's own, independent of any other threads. What you want to do is use a callback to signal the completion of your thread and then have your UI respond to the completion.
The BackgroundWorker class has an event already built in for this purpose.
There are three events you want to subscribe to:
bw.DoWork +=
new DoWorkEventHandler(bw_DoWork);
bw.ProgressChanged +=
new ProgressChangedEventHandler(bw_ProgressChanged);
bw.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
DoWork is where your work will happen. ProgressChanged allows you to update the UI of progress. RunWorkerCompleted will pop the event with your DoWork function has completed.
This object handles the threading and can be set to run asynchronously by running the bw.RunWorkerAsync() call.
See the following page for detail for this:
http://msdn.microsoft.com/en-us/library/cc221403%28v=vs.95%29.aspx
As an example:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show(String.Format("UI thread: {0}", Thread.CurrentThread.ManagedThreadId));
this.Invoke(new MethodInvoker(delegate() { MessageBox.Show(String.Format("Invoke thread: {0}", Thread.CurrentThread.ManagedThreadId)); }));
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
MessageBox.Show(String.Format("Worker thread: {0}", Thread.CurrentThread.ManagedThreadId));
}
}
This example can be built by adding one button and one background worker to a form. Wire up the events through the events designer for the button1_Click and the backgroundWorker1_DoWork function. You should have three MessagesBoxes that pop up after clicking button1. You'll notice the Id for the UI thread and the Invoke thread are the same, which means that any processing you do from the invoke will cause your UI thread to wait. The third popup is from the worker thread, which has a different ID.
Use BeginInvoke when done. For example:
delegate void MyAction();
void Form1_Load( object sender, EventArgs e )
{
Thread tr = new Thread( () =>
{
Script sp = new Script();
code = textBox.Text;
sp.Comp(code);
BeginInvoke( new MyAction( ThreadOperationEnded ) );
} );
tr.Start();
}
void ThreadOperationEnded()
{
MessageBox.Show( "Finished!" );
}

How to update textbox in new window opened by new thread?

I am able to open a new window in a new thread by the following code.
The following code is from MainWindow.xaml.cs
private void buttonStartStop_Click(object sender, RoutedEventArgs e)
{
Test test = new Test();
Thread newWindowThread = new Thread(new ThreadStart(test.start));
newWindowThread.SetApartmentState(ApartmentState.STA);
newWindowThread.IsBackground = true;
newWindowThread.Start();
}
and the following from test.start()
public void start()
{
OutputWindow outputwindow = new OutputWindow();
outputwindow.Show();
Output.print("Begin");
System.Windows.Threading.Dispatcher.Run();
Output.print("FINAL");
System.Windows.Threading.Dispatcher.Run();
}
And the following is from the Output class
public static void print(String str)
{
Dispatcher uiDispatcher = OutputWindow.myOutputWindow.Dispatcher;
uiDispatcher.BeginInvoke(new Action(delegate() { OutputWindow.myOutputWindow.textBoxOutput.AppendText(str + "\n"); }));
uiDispatcher.BeginInvoke(new Action(delegate() { OutputWindow.myOutputWindow.textBoxOutput.ScrollToLine(OutputWindow.myOutputWindow.textBoxOutput.LineCount - 1); }));
}
public static void printOnSameLine(String str)
{
Dispatcher uiDispatcher = OutputWindow.myOutputWindow.Dispatcher;
uiDispatcher.BeginInvoke(new Action(delegate() { OutputWindow.myOutputWindow.textBoxOutput.AppendText(str); }));
uiDispatcher.BeginInvoke(new Action(delegate() { OutputWindow.myOutputWindow.textBoxOutput.ScrollToLine(OutputWindow.myOutputWindow.textBoxOutput.LineCount - 1); }));
}
"Begin" Does get printed in the textbox but "FINAL" does not, I want the start method in Test class to update the textbox in outputwindow through out the program. What is the best way to do this?
Thank you in advance
I'm not sure what are you trying to do. It is normal that FINAL does not print because you called System.Windows.Threading.Dispatcher.Run(). This method keeps thread alive and listens for events. You can look at it like if you have while(true){} inside the Run method. Method will continue to run until Dispatcher is shutdown. You should keep background thread alive and call your static methods from another thread when you need to set a message. Here's an example:
// reference to window in another thread
Window outputWindow = null;
Thread thread = new Thread(() =>
{
// another thread
outputWindow = new Window();
outputWindow.Show();
// run event loop
System.Windows.Threading.Dispatcher.Run();
}) { ApartmentState = ApartmentState.STA, IsBackground = true };
thread.Start();
while (outputWindow == null)
{
// wait until the window in another thread has been created
Thread.Sleep(100);
}
// simulate process
for (int i = 0; i < 10; i++)
{
outputWindow.Dispatcher.BeginInvoke((Action)(() => { outputWindow.Title = i.ToString(); }), System.Windows.Threading.DispatcherPriority.Normal);
Thread.Sleep(500); // simulate some hard work so we can see the change on another window's title
}
// close the window or shutdown dispatcher or abort the thread...
thread.Abort();
EDIT:
This could be quick & dirty generic solution. DoSomeHardWork creates another GUI thread for wait window which displays progress information. This window creates work thread which actually does the work. Work is implemented in method Action. 1st argument is wait window so you can change it from work thread. Of course, in the real world you should go through interface and not directly to window implementation but this is just an example. 2nd argument is object so you can pass whatever you need to the work thread. If you need more arguments pass object[] or modify method signature. In this example I simulate hard work with counter and sleep. You can execute this code on button click multiple times and you will see all wait windows counting their own counter without freezing. Here is the code:
public static void DoSomeHardWork(Action<Window, object> toDo, object actionParams)
{
Thread windowThread = new Thread(() =>
{
Window waitWindow = new Window();
waitWindow.Loaded += (s, e) =>
{
Thread workThread = new Thread(() =>
{
// Run work method in work thread passing the
// wait window as parameter
toDo(waitWindow, actionParams);
}) { IsBackground = true };
// Start the work thread.
workThread.Start();
};
waitWindow.Show();
Dispatcher.Run();
}) { ApartmentState = ApartmentState.STA, IsBackground = true };
// Start the wait window thread.
// When window loads, it will create work thread and start it.
windowThread.Start();
}
private void MyWork(Window waitWindow, object parameters)
{
for (int i = 0; i < 10; i++)
{
// Report progress to user through wait window.
waitWindow.Dispatcher.BeginInvoke((Action)(() => waitWindow.Title = string.Format("{0}: {1}", parameters, i)), DispatcherPriority.Normal);
// Simulate long work.
Thread.Sleep(500);
}
// The work is done. Shutdown the wait window dispather.
// This will stop listening for events and will eventualy terminate
// the wait window thread.
waitWindow.Dispatcher.InvokeShutdown();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
DoSomeHardWork(MyWork, DateTime.Now);
}
Ideally the thread(UI thread) that creates the UI elements own's the elements too. With dispatcher all you are doing is that you are pushing the non-UI related processing into a background thread. Once the background process is completed, the result again will be pushed back to the main UI thread. For sample example check out : http://weblogs.asp.net/pawanmishra/archive/2010/06/06/understanding-dispatcher-in-wpf.aspx

Categories