WPF component in unit testing - wpf

I'm coding a Unit testing and for debug purposes I do need to see some data (please do not discus if one should be using UI for unit testing)
I created a UI thread and started my component like this:
System.Threading.Thread newWindowThread = new Thread(new ThreadStart(() =>
{
SynchronizationContext.SetSynchronizationContext(
new DispatcherSynchronizationContext(
Dispatcher.CurrentDispatcher));
plotter = new PlotterWPF();
plotter.Closed += (s, e) =>
Dispatcher.CurrentDispatcher.BeginInvokeShutdown(DispatcherPriority.Background);
plotter.Show();
plotter.init();
System.Windows.Threading.Dispatcher.Run();
}));
newWindowThread.SetApartmentState(ApartmentState.STA);
newWindowThread.Start();
This works and the plotter appears in screen, my proble is afterwards when I want to send stuff to the plotter I do this:
public void debugFrame(Gtec2.Frame frame)
{
plotter.Dispatcher.Invoke(() =>
{
plotter.Plot(frame);
});
}
and it fails because the plotter is null (in the plotter.Dispatcher... part)
I tried with Applicaiton.Current.Dispatcher but .. application.Current is null.
I also tried to create the plotter outside the thread but .. I cannot because is not STA. I think there should be a way to let the thing now my Dispatcher is the newWindowThread and the dispatcher.Invoke should happen there but .. I have no clue how to do it ..
Any suggestions?

dispatcher = System.Windows.Threading.Dispatcher.FromThread(newWindowThread);
This grabs the dispatcher for this particular thread, from there one one can do
dispatcher.Invoke(() =>
{
/*here you can access the plotter */
});

Related

Creating a new UI thread while calling methods on the original thread

I am in a similar situation as this poster (What's the best way to create a new UI thread and call back methods on the original thread?)
I have an API object which I would like to perform lengthy calculations on, however any properties or methods of this object must be accessed on the current thread (which is the UI thread) or else I get "Accessing disposed TPS.NET DataObject" exceptions
Is there an elegant way of accomplishing this using F# async workflows or will I be stuck managing thread dispatchers as in his solution.
For reference, here is his solution to the issue:
public class Plugin
{
public void Run(Context context)
{
// Get the application's UI thread dispatcher
var dispatcher = Dispatcher.CurrentDispatcher;
// Create a dispatcher frame to push later
var frame = new DispatcherFrame();
// Create a new UI thread (using an StaTaskScheduler)
Task.Factory.StartNew(async () =>
{
var window = new MyWindow();
// The Click event handler now uses the original
// thread's dispatcher to run the slow method
window.MyButton.Click += async (o, e) =>
await dispatcher.InvokeAsync(() => context.SlowMethod());
window.ShowDialog();
// When the window is closed, end the dispatcher frame
frame.Continue = false;
}, CancellationToken.None, TaskCreationOptions.None, new StaTaskScheduler(1));
// Prevent exiting this Run method until the frame is done
Dispatcher.PushFrame(frame);
}
}
Without know the exact details I would suggest having the Click handler on the main thread and do the following:
Copy any data needed off the UI into an F# record and passes this into an async workflow
Return immediately after putting the UI into a 'loading' state
The following code is untested but should put you on the right track:
//Get the context of the UI
let context = System.Threading.SynchronizationContext.Current
//Gather any needed data from the UI into immutable F# records
//Put the UI into a 'loading' state
async {
// Do work on a different thread
do! Async.Sleep 1000
let x = 1
// Switching back to the UI
do! Async.SwitchToContext context
//Update UI
return ()
}
|> Async.Start
This link should also provide some useful information http://tomasp.net/blog/async-non-blocking-gui.aspx/
EDIT:
If you need to go back and forth between the UI thread and a background thread to gather additional information in the async workflow you can make alternating calls between do! Async.SwitchToThreadPool() and do! Async.SwitchToContext context

Multiwindow behaviour and dispatcher in WPF

I have app with 2 windows.
1st LoginWindow used to authentificate user and launch main app. I use thread and run dispatcher for that:
private bool EndTrigger = false;
/.../
Thread thread = new Thread(() =>
{
MainWindow T_window = new MainWindow(t_data);
T_window.WindowState = WindowState.Maximized;
T_window.Show();
EndTrigger = true;
System.Windows.Threading.Dispatcher.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
After that LoginWindow is closed. I used function that checks if MainWindow is ready and Timer like this:
Timer LoginWinClose = new Timer(new TimerCallback(IfLoginWinCanBeClosed), null, 2000, 1000);
and
public void IfLoginWinCanBeClosed(Object stateInfo)
{
if (EndTrigger)
{
this.Dispatcher.Invoke(new Action(delegate
{
this.Close();
}));
}
}
It works as it should: LoginWindow disapper, MainWidow appear and everything works.
But when I tryed to create one more window in MainWindow I get Exception that tells me: Application is shutting down.
It looks like closing LoginWindow leads to attemp of closing application, but if I close any other window (for example MainWindow), I still can create one more from LoginWondow without any error.
Currently I solve this by by changing
this.Close();
to
this.Visibility = Visibility.Collapsed;
It means that LoginWindow will continue to run all the time. If there any another solution?
Thanks to #Sham I understand where is the mistake!
Code, where new window is created located in separate thread (this is because login check operations run in the separated thread to avoid hanging LoginWindow), so I Create and run new window with separate dispatcher in that Thread, instead of main UI thread.
So, the solution is quite easy. Just need to make a little modification:
Thread thread = new Thread(() =>
{
this.Dispatcher.Invoke(new Action(delegate
{
AdminWindow T_window = new AdminWindow(t_data);
T_window.WindowState = WindowState.Maximized;
T_window.Show();
t_data.Link_auth_win.EndTrigger = true;
System.Windows.Threading.Dispatcher.Run();
}));
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();

How to raise a UI event from a background Thread

I've a WebCamCapture class that inherits from UserControl class and I start it in a separate thread like this
ThreadStart webCamThreadStart = () =>
{
webcam = new WebCamCapture();
webcam.FrameNumber = ((ulong)(0ul));
webcam.TimeToCapture_milliseconds = FrameNumber;
webcam.ImageCaptured += new WebCamCapture.WebCamEventHandler(webcam_ImageCaptured);
Start();
};
Thread threadnameThread = new Thread(webCamThreadStart) { IsBackground = true };
threadnameThread.Start();
The web cam starts but it never raises ImageCaptured event.
When I run the code under a Dispatcher BeginInvoke, the event gets fired,
System.Windows.Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
{
//above code
}));
But thats not my purpose, because this way the UI slows down. I want to run the webcam in a separate thread, this was my original question
[Text Input very slow in WPF Application
My question is how we can raise a UI event from a background thread.
Thanks.

System.Windows.Controls.WebBrowser, System.Windows.Threading.Dispatcher, and a windows service

I'm trying to render some html content to a bitmap in a Windows Service.
I'm using System.Windows.Controls.WebBrowser to perform the render. The basic rendering setup works as a standalone process with a WPF window hosting the control, but as a service, at least I'm not getting the LoadCompleted events to fire.
I know that I at least need a Dispatcher or other message pump looping for this WPF control. Perhaps I'm doing it right and there are just additional tricks/incompatibilities necessary for the WebBrowser control. Here's what I've got:
I believe only one Dispatcher needs to be running and that it can run for the life of the service. I believe the Dispatcher.Run() is the actual loop itself and thus needs it's own thread which it can otherwise block. And that thread needs to be [STAThread] in this scenario. Therefore, in a relevant static constructor, I have the following:
var thread = new Thread(() =>
{
dispatcher = Dispatcher.CurrentDispatcher;
Dispatcher.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
where dispatcher is a static field. Again, I think there can only be one but I'm not sure if I'm supposed to be able use Dispatcher.CurrentDispatcher() from anywhere instead and get the right reference.
The rendering operation is as follows. I create, navigate, and dispose of the WebBrowser on dispatcher's thread, but event handler assignments and mres.Wait I think may all happen on the render request-handling operation. I had gotten The calling thread cannot access this object because a different thread owns it but now with this setup I don't.
WebBrowser wb = null;
var mres = new ManualResetEventSlim();
try
{
dispatcher.Invoke(() => { wb = new WebBrowser(); });
wb.LoadCompleted += (s, e) =>
{
// Not firing
};
try
{
using (var ms = new MemoryStream())
using (var sw = new StreamWriter(ms, Encoding.Unicode))
{
sw.Write(html);
sw.Flush();
ms.Seek(0, SeekOrigin.Begin);
// GO!
dispatcher.Invoke(() =>
{
try
{
wb.NavigateToStream(ms);
Debug.Assert(Dispatcher.FromThread(Thread.CurrentThread) != null);
}
catch (Exception ex)
{
// log
}
});
if (!mres.Wait(15 * 1000)) throw new TimeoutException();
}
}
catch (Exception ex)
{
// log
}
}
finally
{
dispatcher.Invoke(() => { if (wb != null) wb.Dispose(); });
}
When I run this, I get my timeout exception every time since the LoadCompleted never fires. I've tried to verify that the dispatcher is running and pumping properly. Not sure how to do that, but I hooked a few of the dispatcher's events from the static constructor and I get some printouts from that, so I think it's working.
The code does get to a wb.NavigateToStream(ms); breakpoint.
Is this bad application of Dispatcher? Is the non-firing of wb.LoadCompleted due to something else?
Thanks!
Here's a modified version of your code which works as a console app. A few points:
You need a parent window for WPF WebBrowser. It may be a hidden window like below, but it has to be physically created (i.e. have a live HWND handle). Otherwise, WB never finishes loading the document (wb.Document.readyState == "interactive"), and LoadCompleted never gets fired. I was not aware of such behavior and it is different from the WinForms version of WebBrowser control. May I ask why you picked WPF for this kind of project?
You do need to add the wb.LoadCompleted event handler on the same thread the WB control was created (the dispatcher's thread here). Internally, WPF WebBrowser is just a wrapper around apartment-threaded WebBrowser ActiveX control, which exposes its events via IConnectionPointContainer interface. The rule is, all calls to an apartment-threaded COM object must be made on (or proxied to) the thread the object was originally created on, because that's what such kind of objects expect. In that sense, IConnectionPointContainer methods are no different to other methods of WB.
A minor one, StreamWriter automatically closes the stream it's initialized with (unless explicitly told to not do so in the constructor), so there is no need to for wrapping the stream with using.
The code is ready to compile and run (it requires some extra assembly references: PresentationFramework, WindowsBase, System.Windows, System.Windows.Forms, Microsoft.mshtml).
using System;
using System.Text;
using System.Threading;
using System.Diagnostics;
using System.Windows;
using System.Windows.Threading;
using System.Windows.Controls;
using System.IO;
using System.Runtime.InteropServices;
using mshtml;
namespace ConsoleWpfApp
{
class Program
{
static Dispatcher dispatcher = null;
static ManualResetEventSlim dispatcherReady = new ManualResetEventSlim();
static void StartUIThread()
{
var thread = new Thread(() =>
{
Debug.Print("UI Thread: {0}", Thread.CurrentThread.ManagedThreadId);
try
{
dispatcher = Dispatcher.CurrentDispatcher;
dispatcherReady.Set();
Dispatcher.Run();
}
catch (Exception ex)
{
Debug.Print("UI Thread exception: {0}", ex.ToString());
}
Debug.Print("UI Thread exits");
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
static void DoWork()
{
Debug.Print("Worker Thread: {0}", Thread.CurrentThread.ManagedThreadId);
dispatcherReady.Wait(); // wait for the UI tread to initialize
var mres = new ManualResetEventSlim();
WebBrowser wb = null;
Window window = null;
try
{
var ms = new MemoryStream();
using (var sw = new StreamWriter(ms, Encoding.Unicode)) // StreamWriter automatically closes the steam
{
sw.Write("<b>Hello, World!</b>");
sw.Flush();
ms.Seek(0, SeekOrigin.Begin);
// GO!
dispatcher.Invoke(() => // could do InvokeAsync here as then we wait anyway
{
Debug.Print("Invoke Thread: {0}", Thread.CurrentThread.ManagedThreadId);
// create a hidden window with WB
window = new Window()
{
Width = 0,
Height = 0,
Visibility = System.Windows.Visibility.Hidden,
WindowStyle = WindowStyle.None,
ShowInTaskbar = false,
ShowActivated = false
};
window.Content = wb = new WebBrowser();
window.Show();
// navigate
wb.LoadCompleted += (s, e) =>
{
Debug.Print("wb.LoadCompleted fired;");
mres.Set(); // singal to the Worker thread
};
wb.NavigateToStream(ms);
});
// wait for LoadCompleted
if (!mres.Wait(5 * 1000))
throw new TimeoutException();
dispatcher.Invoke(() =>
{
// Show the HTML
Console.WriteLine(((HTMLDocument)wb.Document).documentElement.outerHTML);
});
}
}
catch (Exception ex)
{
Debug.Print(ex.ToString());
}
finally
{
dispatcher.Invoke(() =>
{
if (window != null)
window.Close();
if (wb != null)
wb.Dispose();
});
}
}
static void Main(string[] args)
{
StartUIThread();
DoWork();
dispatcher.InvokeShutdown(); // shutdown UI thread
Console.WriteLine("Work done, hit enter to exit");
Console.ReadLine();
}
}
}
Maybe the Webbrowser Control needs Desktop Interaction for rendering the content:
My feeling say that using WPF controls and in particular particulary the Webbrowser-Control (=Wrapper around the IE ActiveX control) isn't the best idea.. There are other rendering engines that might be better suited for this task: Use chrome as browser in C#?

Disable screen while loading data

In my wpf application, I have a menu. When I click on one of the elements of the menu, I change my screen data, which is quite a long process.
I tried to disable the main window when I do such a loading, using this method :
private void SetNavigation(MainContentTypeEnum enumVal, int id, ICheckState vm)
{
var parent = Window.GetWindow(this);
var tmpCursor = parent.Cursor;
parent.Cursor = Cursors.Wait;
parent.IsEnabled = false;
BackgroundWorker bw = new BackgroundWorker();
bw.WorkerReportsProgress = true;
bw.DoWork += (o, args) =>
{
try
{
Dispatcher d = args.Argument as Dispatcher;
d.Invoke(new Action(() =>
{
Navigation.Navigator.SetContol(enumVal, id, vm);
}));
}
catch (Exception ex)
{
Debug.WriteLine(ex.ToString());
}
};
bw.RunWorkerCompleted += (o, args) =>
{
parent.IsEnabled = true;
parent.Cursor = tmpCursor;
};
bw.RunWorkerAsync(Dispatcher.CurrentDispatcher);
}
This method works on the very first call, the form is disabled, and then enabled when data is loaded. But on next calls, it doesn't work anymore, everything freezes until the operation completes. I tried setting a breakpoint, and the method is correctly hit and executed. I don't understant why it only works one time...
Have you an idea ?
Thanks in advance!
Edit: A bit of precision: this code is part of a usercontrol, which is why I call the parent using Window.GetWindow(this);
Edit2: Setting a Thread.Sleep(1000); just before invoking the dispatcher does the job. My guess is that the parent.IsEnabled instruction is not executed quickly enough... but why ?
Edit3: Having made some timings, my data retrieval is quite quick. It seems that the problem exists on the binding phase. I set the value to the bound property, and the method returns. However, the UI still frozen for a moment after that.

Resources