Show WPF window from test unit - c#

I am running a test unit (and learning about them). Quite simply, my unit creates a List and passes it to my MainWindow.
The issue I have is after I show() the main window the unit method ends. I want the unit to not finish until I close the MainWindow. This is what I've done (see below) - it obviously doesn't work and feels like I'm on the wrong path here. How can I do this properly?
[TestClass]
public class Logging
{
bool continueOn = true;
[TestMethod]
public void ShowLogs()
{
ShowResults(createLogList());
}
private void ShowResults(List<Log> logList)
{
MainWindow mw = new MainWindow(logList);
mw.Closed += mw_Closed;
mw.Show();
while (continueOn)
{ }
}
void mw_Closed(object sender, EventArgs e)
{
this.continueOn = false;
}
private List<Log> createLogList()
{
List<Log> listLog = new List<Log>();
//logic
return listLog;
}
Maybe I have to put this onto a background worker thread and monitor that - to be honest I've no idea and before I waste hours, I'd appreciate some guidance.

The WPF Window must be created and shown on a thread which supports the WPF window infrastructure (message pumping).
[TestMethod]
public void TestMethod1()
{
MainWindow window = null;
// The dispatcher thread
var t = new Thread(() =>
{
window = new MainWindow();
// Initiates the dispatcher thread shutdown when the window closes
window.Closed += (s, e) => window.Dispatcher.InvokeShutdown();
window.Show();
// Makes the thread support message pumping
System.Windows.Threading.Dispatcher.Run();
});
// Configure the thread
t.SetApartmentState(ApartmentState.STA);
t.Start();
t.Join();
}
Note that:
The window must be created and shown inside the new thread.
You must initiate a dispatcher (System.Windows.Threading.Dispatcher.Run()) before the ThreadStart returns, otherwise the window will show and die soon after.
The thread must be configured to run in STA apartment.
For more information, visit this link.

Of course, since it is only for testing, using
ShowDialog()
may be an option instead of 'Show()'

Related

WPF Access window created on different UI thread

I created new Window on a different thread because it has heavy UI operations, that was done to keep my main window run smoothly. Everything works perfectly. But here is the question:
How I can access newly created window?
After calling Dispatcher.Run() I can not manipulate visualisationWindow anymore. I want to keep access to that newly created window object.
Here is how my window created:
private void CreateVisualisationWindow()
{
Thread VisualisationWIndowThread = new Thread(new ThreadStart(ThreadStartingPoint));
VisualisationWIndowThread.SetApartmentState(ApartmentState.STA);
VisualisationWIndowThread.IsBackground = true;
VisualisationWIndowThread.Start();
}
private void ThreadStartingPoint()
{
Visualisation visualisationWindow = new Visualisation();
visualisationWindow.Show();
System.Windows.Threading.Dispatcher.Run();
}
Also I tried accessing it through System.Windows.Threading.Dispatcher.FromThread(VisualisationWIndowThread) but seems I misunderstand some core things.
I simulated your issue using two WPF Window objects and a timer to ensure that the Second Window was created before calling operations on it. Below is my code sample and it updates the second Windows TextBox every five seconds:
private Timer _timer;
private SecondWindow _secondWindow;
public MainWindow()
{
InitializeComponent();
CreateVisualisationWindow();
_timer = new Timer(Callback);
_timer.Change(5000, 5000);
}
private void Callback(object state)
{
UpdateSecondWindowText();
}
private void CreateVisualisationWindow()
{
Thread VisualisationWIndowThread = new Thread(ThreadStartingPoint);
VisualisationWIndowThread.SetApartmentState(ApartmentState.STA);
VisualisationWIndowThread.IsBackground = true;
VisualisationWIndowThread.Start();
}
private void ThreadStartingPoint()
{
_secondWindow = new SecondWindow();
_secondWindow.SecondWindowTextBlock.Text = "Hello";
_secondWindow.Show();
Dispatcher.Run();
}
private void UpdateSecondWindowText()
{
_secondWindow.Dispatcher.BeginInvoke(new Action(() =>
{
_secondWindow.SecondWindowTextBlock.Text = _secondWindow.SecondWindowTextBlock.Text + " World";
}));
}
So the trick is, you need to call the Dispatcher on the second Window in order to gain access to it.

C# Thread Management for Background-Threads

Today I got a Problem with a Deadlock in a App I developed.
In particular I am stopping a Window which started a Thread in the Background.
Problem? On Closing of the Window the Window closed, but the Process was still alive in the Background.
I thought about if the Thread itself blocks, but as a Background-declared Thread it should normaly shut down anyway.
But then I noticed that "Logic.DoSomething()" started it's own endless-Threads without declaring it as Background-Threads.
So why is a (non-Background) Thread in a (Background) Thread blocking the Process? Shouldn't it shutdown because it's "Parent" runs as Background-Thread?
And if not, why doesn't block the Parent-Thread itself?
public partial class MainWindow : Window
{
private Thread TheThread { get; set; }
public MainWindow()
{
InitializeComponent();
Loaded += MainWindow_Loaded;
Closed += MainWindow_Closed;
}
private void MainWindow_Loaded(object sender, EventArgs e)
{
// Create the (background-)thread and start it
TheThread = new Thread(() => Logic.DoSomething());
TheThread.IsBackground = true;
TheThread.Start();
}
private void MainWindow_Closed(object sender, EventArgs e)
{
// Close Thread if existent.
TheThread.Abort("Window was closed");
TheThread.Join(500);
}
}
PS: Please note, that I don't use a Worker-Object because I handle the Abort-Exception in the Thread normaly.
Edit: It is a theoretical Question for a Windows Presentation Foundation-Project (WPF) and not for an Windows Forms Project.
Besides of that the Basic-Question was NOT how to Start and Stop a Thread using e.g. CancellationToken. It was a Question why a Thread made by a Process doesn't kill the Process it called or is gettin stuck in the Join.
Hope this is clear enough.
Instead of calling Abort on the thread, which is never a good idea, communicate with the thread and indicate it should stop. Then wait for the thread to stop. A good means of communicating things like this are wait handles. In the following example I'll use a ManualResetEvent:
public partial class MainWindow : Window
{
private ManualResetEvent theThreadShouldStop;
private Thread TheThread { get; set; }
public MainWindow()
{
InitializeComponent();
// ??? This should be set through designer!!
Loaded += MainWindow_Loaded;
Closed += MainWindow_Closed;
theThreadShouldStop = new ManualResetEvent(false);
}
private void MainWindow_Loaded(object sender, EventArgs e)
{
// Create the (background-)thread and start it
TheThread = new Thread(() => Logic.DoSomething(theThreadShouldStop));
TheThread.IsBackground = true;
TheThread.Start();
}
private void MainWindow_Closed(object sender, EventArgs e)
{
// Close Thread if existent.
theThreadShouldStop.Set();
TheThread.Join();
theThreadShouldStop.Close();
}
}
Now the thread method needs to be cooperative, but as I don't know what the thread method does, I'll give an example of a common scenario where the thread runs in a loop that should be cancellable:
public class Logic
{
public void DoSomething(ManualResetEvent cancel)
{
while (!WantHandle.WaitOne(cancel, 1))
{
... Do stuff
}
}
}
Threads don't have "parents". There is no hierarchy. They don't know or care what thread started them. They're just threads, and the application ends when no non-background threads are running; that's all there is to it. Threads block until another thread ends when you ask them to by calling Join. They don't just do things on their own without you telling them to. They do exactly what you tell them to.

How to ask the GUI thread to create objects?

I've got the following program flow in my Windows Forms application (WPF is not a viable option unfortunately):
The GUI Thread creates a splash screen and a pretty empty main window, both inheriting Form.
The splash screen is shown and given to Application.Run().
The splash screen will send an event which triggers an async Event Handler which performs initialization, using the IProgress interface to report progress back to the GUI. (This works flawlessly.)
At some point during the initialization, I need to dynamically create GUI components based on information provided by certain plugins and add them to the Main Window.
At this point I'm stuck: I know I need to ask the GUI thread to create those components for me, but there is no Control I could call InvokeRequired on. Doing MainWindow.InvokeRequired works neither.
The only idea I could come up with was to fire an event which is connected to a factory in the GUI Thread, and then wait for that factory to fire another event which provides the created controls. However I am pretty sure there is a more robust solution. Does anyone know how to achieve this?
Using the comments on my question, especially the note about the continuation method which made me find this very useful question, I achieved the following:
The first part of initialization is performed asynchronously (no change).
The second part of the initialization (which creates the UI elements) is performed afterwards as a Continuation Task, in the context of the UI thread.
Apart from the rather short GUI initialization part, the Splash Screen is responsive (i.e. the mouse cursor does not change to "Waiting" once it hovers the Splash Screen).
Neither of the initialization routines knows the splash screen at all (i.e. I could easily exchange it).
The core controller only knows the SplashScreen interface and does not even know it is a Control.
There currently is no exception handling. This is my next task but doesn't affect this question.
TL;DR: The code looks somewhat like this:
public void Start(ISplashScreen splashScreen, ...)
{
InitializationResult initializationResult = null;
var progress = new Progress<int>((steps) => splashScreen.IncrementProgress(steps));
splashScreen.Started += async (sender, args) => await Task.Factory.StartNew(
// Perform non-GUI initialization - The GUI thread will be responsive in the meantime.
() => Initialize(..., progress, out initializationResult)
).ContinueWith(
// Perform GUI initialization afterwards in the UI context
(task) =>
{
InitializeGUI(initializationResult, progress);
splashScreen.CloseSplash();
},
TaskScheduler.FromCurrentSynchronizationContext()
);
splashScreen.Finished += (sender, args) => RunApplication(initializationResult);
splashScreen.SetProgressRange(0, initializationSteps);
splashScreen.ShowSplash();
Application.Run();
}
It is much easier to manage multiple forms and display one while the other is working or being constructed.
I suggest you try the following:
When application is started you create splash screen form so your Program.cs is like this
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new SplashForm());
}
Inside the splash form constructor, create a new thread (I will use BackgroundWorker but there are other options like tasks) to build your main form.
public SplashForm()
{
InitializeComponent();
backgroundWorker1.WorkerSupportsCancellation = true;
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
backgroundWorker1.RunWorkerAsync();
}
Now we need to write the SplashForm member functions to tell background worker what to do
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
// Perform non-GUI initialization - The GUI thread will be responsive in the meantime
// My time consuming operation is just this loop.
//make sure you use worker.ReportProgress() here
for (int i = 1; (i <= 10); i++)
{
if ((worker.CancellationPending == true))
{
e.Cancel = true;
break;
}
else
{
System.Threading.Thread.Sleep(500);
worker.ReportProgress((i * 10));
}
}
SetVisible(false);
MainForm mainForm = new MainForm();
mainForm.ShowDialog();
//instead of
//this.Visible = false;
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.progressBar1.Value = e.ProgressPercentage;
}
You might have noticed by now, I am using another member function to hide the splash screen. It is because you are now in another thread and you can't just use this.visible = false;. Here is a link on the matter.
delegate void SetTextCallback(bool visible);
private void SetVisible(bool visible)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetVisible);
this.Invoke(d, new object[] { visible });
}
else
{
this.Visible = visible;
}
}
When I run this sample project it shows the progress bar and then loads the MainForm windows form after hiding the SplashForm.
This way you can put any controls that you might need inside the MainForm constructor. The part you shortened as // Perform GUI initialization afterwards in the UI context should go into MainForm constructor.
Hope this helps.

How to implement a popup window with a counter

I would like to implement a simple popup window in Windows Forms, which will show a simple timer to the user while some slow-running process is executing. The premise is simple; show to the user that something is indeed going on and the application is not frozen. Note that this slow-running process is not a loop, nor is it something that I can tap into.
What I want is a simple popup window, showing some message along the lines "Elapsed time: x seconds", where x is incremented every second.
The basic concept is the following:
public void test()
{
//Some code which does stuff
//Popup window with counter
//Perform long running process
//Close popup window with counter
//Some other code which does other stuff
}
I tried to do it using various ways, including background workers, threads, and of course timers. But I did not manage to make it work as I wanted. And I would prefer not to post any of my code so as not to "lead" the responses to a specific way of doing this.
So what would be the best way to do this work?
Thanks.
UPDATE:
In reply to some comments, since I cannot paste any code in the replies section, I'm editing my original question to accomodate this. One of the implementations that I tried is to spawn the popup window in a separate thread. Although I got no runtime errors, the popup window did not refresh correctly. It indeed poped-up, but no text would show within it, and the counter would not refresh. Here's the code:
private void test()
{
frmProgressTimer ofrmProgressTimer = new frmProgressTimer(); //Instance of popup Form
System.Threading.Tasks.Task loadTask = new System.Threading.Tasks.Task(() => ProgressTimer(ofrmProgressTimer));
loadTask.Start();
//Perform long running process
System.Threading.Tasks.Task cwt = loadTask.ContinueWith(task => EndProgressTimer(ofrmProgressTimer));
}
private void ProgressTimer(frmProgressTimer ofrmProgressTimer)
{
ofrmProgressTimer.Show();
ofrmProgressTimer.Invoke((Action)(() =>
{
ofrmProgressTimer.startTimer();
}));
}
private void EndProgressTimer(frmProgressTimer ofrmProgressTimer)
{
ofrmProgressTimer.Invoke((Action)(() =>
{
ofrmProgressTimer.stopTimer();
ofrmProgressTimer.Close();
}));
}
And here's my popup form code:
public partial class frmProgressTimer : Form
{
private int counter = 0;
private Timer timer1;
public frmProgressTimer()
{
InitializeComponent();
timer1 = new Timer();
timer1.Interval = 1000;
timer1.Tick += new EventHandler(timer1_Tick);
}
public void startTimer()
{
timer1.Start();
}
public void stopTimer()
{
timer1.Stop();
}
private void timer1_Tick(object sender, EventArgs e)
{
counter += 1;
labelText.Text = counter.ToString();
}
}
This is actually quite easy to do. Create your dialog, define your long running operation to take place in a non-UI thread when it is shown, add a continuation to that operation which closes the dialog when the task finishes, and then show the dialog.
MyDialog dialog = new MyDialog();
dialog.Shown += async (sender, args) =>
{
await Task.Run(() => DoLongRunningWork());
dialog.Close();
};
dialog.ShowDialog();
The code to have the ticking over time should be entirely contained within the dialog, and based on the question it seems you already have that well under control with a simple Timer.
Make a new form, which will pop up, and show a timer. That way it won't be interrupted with all the work on your main form, and the timer will work continuously.
Remember when showing a new from to use newForm.ShowDialog() not newForm.Show(). Your can google the differences
I would simply start your work on a separate thread. Launch a modal form with your timer output. To display the timer use an actual timer instance set to update every second. When the timer event fire update your dialog.
Finally once you're thread completes close the dialog so your main form is active again.
First of all you need to make it not closeable by the user (as if modal dialogs weren't annoying enough) but closeable by your code. You could accomplish this by subscribing to the FormClosing event of the form. Let's say your popup form's name is Form2:
private bool mayClose = false;
public void PerformClose()
{
this.mayClose = true;
this.Close();
}
private void Form2_FormClosing(object sender, FormClosingEventArgs e)
{
if (!this.mayClose)
e.Cancel = true;
}
Create a Timer, provide a Tick event handler, enable it and set its Interval to 500 milliseconds:
Create a label to host your desired text. Let's call it label1.
Within and surrounding your Tick event handler do something like this:
private DateTime appearedAt = DateTime.UtcNow;
private void timer1_Tick(object sender, EventArgs e)
{
int seconds = (int)(DateTime.UtcNow - this.appearedAt).TotalSeconds;
this.label1.Text = string.Format(#"Ellapsed seconds: {0}", seconds);
}
Make sure your long running process is happening on a background thread, not on the GUI thread.
Say your long running process can be thought of as the execution of a method called MyProcess.
If that is the case, then you need to call that method from a secondary thread.
// PLACE 1: GUI thread right here
Thread thread = new Thread(() =>
{
// PLACE 2: this place will be reached by the secondary thread almost instantly
MyProcess();
// PLACE 3: this place will be reached by the secondary thread
// after the long running process has finished
});
thread.Start();
// PLACE 4: this place will be reached by the GUI thread almost instantly
Show the form right before the long running process starts. This can be done in any of the 2 places (marked in the previous section of code) called PLACE1 or PLACE2. If you do it in PLACE2 you will have to marshal a call back to the GUI thread in order to be able to interact with the WinForms framework safely. Why am I bringing this up ? It's because maybe the long running process is not started from within the GUI thread at all and you absolutely need to do this.
Close the form right after the long running process finishes. This can be done only in PLACE3 and you absolutely need to marshal a call.
To wrap the earlier 2 bullets and the answer, you could do this:
private void DoIt()
{
Form2 form2 = new Form2();
Action showIt = () => form2.Show();
Action closeIt = () => form2.PerformClose();
// PLACE 1: GUI thread right here
Thread thread = new Thread(() =>
{
form2.BeginInvoke(showIt);
// PLACE 2: this place will be reached by the secondary thread almost instantly
MyProcess();
form2.BeginInvoke(closeIt);
// PLACE 3: this place will be reached by the secondary thread
// after the long running process has finished
});
thread.Start();
// PLACE 4: this place will be reached by the GUI thread almost instantly
}
Finally I've managed to resolve this in the most simplistic manner. And it works like a charm. Here's how to do it:
//Create an instance of the popup window
frmProgressTimer ofrmProgressTimer = new frmProgressTimer();
Thread thread = new Thread(() =>
{
ofrmProgressTimer.startTimer();
ofrmProgressTimer.ShowDialog();
});
thread.Start();
//Perform long running process
ofrmProgressTimer.Invoke((Action)(() =>
{
ofrmProgressTimer.stopTimer();
ofrmProgressTimer.Close();
}));
You can see the code for the popup window in the original post/question, with the only difference that the tick function changes the label text as:
labelText.Text = string.Format("Elapsed Time: {0} seconds.", counter.ToString());
Thank you to everybody for trying to help me out.

c# forms blocking other control when show dialog called

When my application is loading. I display a progress bar using the code below. The problem is if someone clicks on the toolbar context menu (the way to exit) it will be blocked until this the progress bar is closed. Does anyone know a better way of achieving this?
The reason I'm using ShowDialog is that when I used Show the progress bar wouldn't animate - I'm using the MarqueeStyle.
Thanks
public partial class PopUpProgessBar : Form
{
public PopUpProgessBar()
{
InitializeComponent();
}
Thread t;
private void StartAnmiation()
{
this.Update();
this.ShowDialog();
}
public void Stop()
{
if (t != null)
{
t.Abort();
t.Join();
}
}
public void Start()
{
if (t == null)
{
t = new Thread(new ThreadStart(this.StartAnmiation));
t.Start();
}
}
This code doesn't look quite right. Are you sure it doesn't throw cross-thread violations? In general, your whole metaphor here is wrong. You need to keep the GUI on the GUI thread. Load your application on the background thread and have it send progress updates to the GUI thread.
Your PopupProgressBar form shouldn't be responsible for loading itself in a new thread, that should be done in presumably your main window.
I would get rid of all the thread stuff in PopupProgressBar and make it simply start updating it's marquee. Then, in your main window (OnLoad) you tell it to do it's thing:
bool done = false;
PopupProgressBar splashForm = null;
ThreadPool.QueueUserWorkItem((x) =>
{
using (splashForm = new PopupProgressBar ())
{
splashForm.Show();
while (!done)
Application.DoEvents();
splashForm.Close();
}
});
// do all your initialization work here
// also, during each step of your initialization you could send call a function
// in splashForm to update
done = true;

Categories

Resources