Creating and running WPF window in another thread - wpf

So I am working on a console project which should display a WPF window at some point where I can see the visual representation of my graph while it continues running through the algorithm.
I've come so far that I can create a Window and open it in another thread, but as soon as I call Dispatcher.Run() the Console gets blocked. My approach so far:
Step 1: Creating a thread which creates the Application and an SynchronizationContext for itself once run
Step 2: Calling the application's method (it's an inherited custom class which provides these methods) to create a window using a transportation class, using the sync-context of the thread. Expectation: It should create an instance of a Window owned by the separate UI thread.
Step 3: Calling the ShowWindow() method on the application using the sync-context of the thread, which shows the Window (does work) and then runs the Dispatcher. Because it's dispatched using the sync-context of the thread, it shouldn't lock the Console thread. It does, though.
Step 4: Using the Window's dispatcher to call the update methods for the graph.
Well, in my thoughts this should perfectly well work, but somehow the Dispatcher locks the Console thread and not the UI thread. Am I overlooking something here?
The custom Application:
internal sealed class CrossThreadApplication : Application
{
private SynchronizationContext _context;
///=================================================================================================
/// <summary> Constructor. </summary>
///
/// <param name="context"> The context. </param>
///=================================================================================================
public CrossThreadApplication(SynchronizationContext context)
{
if (context == null) throw new ArgumentNullException(nameof(context));
_context = context;
}
///=================================================================================================
/// <summary> Shows the window. </summary>
///
/// <exception cref="ArgumentNullException"> Thrown when one or more required arguments are
/// null. </exception>
///
/// <param name="window"> The window. </param>
///=================================================================================================
public void ShowWindow(Window window)
{
if (window == null) throw new ArgumentNullException(nameof(window));
_context.Send(state =>
{
((Window)state).Show();
System.Windows.Threading.Dispatcher.Run();
}, window);
}
///=================================================================================================
/// <summary> Creates a new object. </summary>
///
/// <param name="handle"> The creation handle for a Window. </param>
///
/// <returns> An object. </returns>
///=================================================================================================
public void Create(CreationHandle<Window> handle)
{
_context.Send(state =>
{
CreationHandle<Window> hnd = (CreationHandle<Window>) state;
hnd.Set(Activator.CreateInstance(hnd.CreationType));
}, handle);
}
}
The UIHost class which hosts the UI thread and interacts with the application
static UIHost()
{
HostThread = new Thread(Start);
HostThread.SetApartmentState(ApartmentState.STA);
HostThread.Name = "UI Thread";
CreationEvent = new ManualResetEvent(false);
HostThread.Start(CreationEvent);
}
private static void Start(object o)
{
ManualResetEvent ev = (ManualResetEvent) o;
SynchronizationContext context = new SynchronizationContext();
SynchronizationContext.SetSynchronizationContext(context);
_application = new CrossThreadApplication(context);
ev.Set();
_application.Run();
}
///=================================================================================================
/// <summary> Executes the user interface operation. </summary>
///
/// <param name="windowType"> The window type. </param>
/// <param name="reset"> The reset. </param>
///
/// <returns> An object. </returns>
///=================================================================================================
internal static object RunUI(Type windowType, ManualResetEvent reset)
{
CreationEvent.WaitOne();
CreationHandle<Window> handle = new CreationHandle<Window>();
_application.Create(handle);
_application.ShowWindow(handle.Get());
reset.Set();
return handle.Get();
}
The issue occurs in CrossThreadApplication.ShowWindow() at *.Dispatcher.Run();.
Why is this so? How can I get this working? It has to be possible somehow.

Related

Getting the parent window from the most inner WPF usercontrol

I have an WPF User control, let's say UCInner, which contains a WPF Popup. UCInner is used in another WPF user control, let's say UCOuter.
UCOuter is embedded in an ElementHost (ElementHost.Child = UCOuter).
Finally UCOuter is embedded within an Outlook VSTO custom task pane ahd this latter used in a winforms application (Outlook VSTO Add-in).
So from the most inner WPF Control, UCInner, I would like to obtain the parent Window. I have tried some alternatives with no success, I am always getting null or exceptions:
Window w = Window.GetWindow(myPopup);
Window w = Window.GetWindow(UCInner);
I also have tried what explained here and also this one.
UPDATED -
ANOTHER ATTEMPT:
I have tried below piece of code and i can get successfully the window handle, but now from the handle I need to get the Window Object.
dynamic activeWindow = Globals.ThisAddIn.Application.ActiveWindow();
Microsoft.VisualStudio.OLE.Interop.IOleWindow win = activeWindow as Microsoft.VisualStudio.OLE.Interop.IOleWindow;
IntPtr handle;
win.GetWindow(out handle);
So in order to get the Window object I have tried this based on the Window handle:
System.Windows.Interop.HwndSource hwndSource = System.Windows.Interop.HwndSource.FromHwnd(handle);
Window w = hwndSource.RootVisual as Window;
but this does not work, hwndSource is null.
If you need to figure out the right parent window to display your own WPF window, cast Application.ActiveWindow to IOleWindow (Application.ActiveWindow can return either Explorer or Inspector, they both support IOleWindow) and call IOleWindow.GetWindow. Once you have the HWND, create an instance of the WindowInteropHelper class and specify the Outlook window handle as the parent:
if (outlookHwnd != IntPtr.Zero)
{
WindowInteropHelper helper = new WindowInteropHelper(YourDialogWindow);
helper.Owner = outlookHwnd;
YourDialogWindow.ShowInTaskbar = false;
}
First, you need to retrieve the parent window handle, in case of Explorer window in Outlook you can use:
Outlook.Explorer explorer = OutlookApplication.ActiveExplorer();
IOleWindow oleWindow = explorer as IOleWindow;
IntPtr handle = IntPtr.Zero;
oleWindow.GetWindow(out handle);
if (handle != IntPtr.Zero)
{
WindowInteropHelper helper = new WindowInteropHelper(DialogWindow);
helper.Owner = handle;
DialogWindow.ShowInTaskbar = false;
DialogWindow.ShowDialog();
}
where IOleWindow can be defined in the following way:
/// <summary>
/// Implemented and used by containers and objects to obtain window handles
/// and manage context-sensitive help.
/// </summary>
/// <remarks>
/// The IOleWindow interface provides methods that allow an application to obtain
/// the handle to the various windows that participate in in-place activation,
/// and also to enter and exit context-sensitive help mode.
/// </remarks>
[ComImport]
[Guid("00000114-0000-0000-C000-000000000046")]
[InterfaceType (ComInterfaceType.InterfaceIsIUnknown)]
public interface IOleWindow
{
/// <summary>
/// Returns the window handle to one of the windows participating in in-place activation
/// (frame, document, parent, or in-place object window).
/// </summary>
/// <param name="phwnd">Pointer to where to return the window handle.</param>
void GetWindow (out IntPtr phwnd) ;
/// <summary>
/// Determines whether context-sensitive help mode should be entered during an
/// in-place activation session.
/// </summary>
/// <param name="fEnterMode"><c>true</c> if help mode should be entered;
/// <c>false</c> if it should be exited.</param>
void ContextSensitiveHelp ([In, MarshalAs(UnmanagedType.Bool)] bool fEnterMode) ;
}

WPF Best way of displaying a busy indicator when dynamically creating a page

I have a WPF application that runs as an XBAP in a browser. On a few pages all the controls are dynamically created depending on what the user selects. Because of this it can look like the application is not doing anything until all the controls are loaded. I'd like to have some sort of busy indicator displayed before hand to show the user that the controls are loading, it doesn't have to be animated although would be nice if it did. I've looked into the telerik busy indicator but this doesn't work as it's really for getting data for a single control and doesn't show until the controls are loaded which defeats the purpose.
I was thinking of displaying an overlay, or something similar, first, containing a loading logo, then load the page behind this and hide the overlay when the controls have loaded. I was wondering if this was the best way of going about this or if there's a better way?
Note: I haven't tried this in a XBAP browser app, but it works in WPF Apps without any problems!
I use a DispatcherTimer to show an hourglass when necessary, and abstract this code to a static class.
public static class UiServices
{
/// <summary>
/// A value indicating whether the UI is currently busy
/// </summary>
private static bool IsBusy;
/// <summary>
/// Sets the busystate as busy.
/// </summary>
public static void SetBusyState()
{
SetBusyState(true);
}
/// <summary>
/// Sets the busystate to busy or not busy.
/// </summary>
/// <param name="busy">if set to <c>true</c> the application is now busy.</param>
private static void SetBusyState(bool busy)
{
if (busy != IsBusy)
{
IsBusy = busy;
Mouse.OverrideCursor = busy ? Cursors.Wait : null;
if (IsBusy)
{
new DispatcherTimer(TimeSpan.FromSeconds(0), DispatcherPriority.ApplicationIdle, dispatcherTimer_Tick, Application.Current.Dispatcher);
}
}
}
/// <summary>
/// Handles the Tick event of the dispatcherTimer control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
private static void dispatcherTimer_Tick(object sender, EventArgs e)
{
var dispatcherTimer = sender as DispatcherTimer;
if (dispatcherTimer != null)
{
SetBusyState(false);
dispatcherTimer.Stop();
}
}
}
You would use it like this:
void DoSomething()
{
UiServices.SetBusyState();
// Do your thing
}
Hope this helps!

WPF ObservableCollection Thread Safety

I've got a MVVM setup.
My model periodically calls some service and then invokes an action on the ViewModel which then updates some variables exposed to the View.
The variable is an ReadOnlyObservableCollection<T>, which has an ObservableCollection<T> it listens on.
The problem is that the Model calls the callback from a different thread, and thus it doesn't allow me to clear the ObservableCollection<T> on a different thread.
So I thought: use the dispatcher, if we aren't on the correct thread, invoke it:
private void OnNewItems(IEnumerable<Slot> newItems)
{
if(!Dispatcher.CurrentDispatcher.CheckAccess())
{
Dispatcher.CurrentDispatcher.Invoke(new Action(() => this.OnNewItems(newItems)));
return;
}
this._internalQueue.Clear();
foreach (Slot newItem in newItems)
{
this._internalQueue.Add(newItem);
}
}
Code is pretty straightforward I think.
The problem is that, even though I execute it on the correct thread (I think) it still throws me an exception on the .Clear();
Why is this occuring? How can I work around it without creating my custom ObservableCollection<T>?
I typically initialize the dispatcher used by my view models in a common view model base to help ensure it is the UI thread dispatcher, as Will mentions.
#region ViewModelBase()
/// <summary>
/// Initializes a new instance of the <see cref="ViewModelBase"/> class.
/// </summary>
protected ViewModelBase()
{
_dispatcher = Dispatcher.CurrentDispatcher;
}
#endregion
#region Dispatcher
/// <summary>
/// Gets the dispatcher used by this view model to execute actions on the thread it is associated with.
/// </summary>
/// <value>
/// The <see cref="System.Windows.Threading.Dispatcher"/> used by this view model to
/// execute actions on the thread it is associated with.
/// The default value is the <see cref="System.Windows.Threading.Dispatcher.CurrentDispatcher"/>.
/// </value>
protected Dispatcher Dispatcher
{
get
{
return _dispatcher;
}
}
private readonly Dispatcher _dispatcher;
#endregion
#region Execute(Action action)
/// <summary>
/// Executes the specified <paramref name="action"/> synchronously on the thread
/// the <see cref="ViewModelBase"/> is associated with.
/// </summary>
/// <param name="action">The <see cref="Action"/> to execute.</param>
protected void Execute(Action action)
{
if (this.Dispatcher.CheckAccess())
{
action.Invoke();
}
else
{
this.Dispatcher.Invoke(DispatcherPriority.DataBind, action);
}
}
#endregion
You could then invoke an action on the view model dispatcher like this:
this.Execute(
() =>
{
this.OnNewItems(newItems);
}
);
A neat fix of this problem found on Codeproject- Multi-Threaded ObservableCollection and NotifyCollectionChanged Wrapper

User-friendly way to display a Visual in a WPF application?

Looking for a simple, elegant way to display any given Visual to the user. The only way I can think of off my head is to slap it in a brush and paint it on a Rectangle that's in a ScrollViewer. Not exactly the best option.
You could create a wrapper that inherits from FrameworkElement that would either host your Visual or a generic wrapper that will host any object deriving from Visual.
Take a look at the example in Visual.AddVisual or, if you want to host more than one visual, take a look at the (partial) example in Using DrawingVisual Objects
I don't see a way how you could do that since a Visual has neither a position nor a size. Perhaps stick to FrameworkElement and create a style for it?
My (current) answer is to slap it in an XpsDocument and display it in a DocumentViewer. I could, I suppose, do it a little less complex, but I already have the infrastructure to do it this way. Its not 100%, but it works.
First, a behavior so that I can bind to DocumentViewer.Document (its a friggen POCO, urgh):
public sealed class XpsDocumentBinding
{
#region Document
/// <summary>
/// The <see cref="DependencyProperty"/> for <see cref="Document"/>.
/// </summary>
public static readonly DependencyProperty DocumentProperty =
DependencyProperty.RegisterAttached(
"Document",
typeof(XpsDocument), //
typeof(XpsDocumentBinding),
new UIPropertyMetadata(null, OnDocumentChanged));
/// <summary>
/// Gets the value of the <see cref="DocumentProperty">Document attached property</see> on the given <paramref name="target"/>.
/// </summary>
/// <param name="target">The <see cref="DependencyObject">target</see> on which the property is set.</param>
public static XpsDocument GetDocument(DependencyObject target)
{
return (XpsDocument)target.GetValue(DocumentProperty);
}
/// <summary>
/// Sets the <paramref name="value"/> of the <see cref="DocumentProperty">Document attached property</see> on the given <paramref name="target"/>.
/// </summary>
/// <param name="dependencyObject">The <see cref="DependencyObject">target</see> on which the property is to be set.</param>
/// <param name="value">The value to set.</param>
public static void SetDocument(DependencyObject target, XpsDocument value)
{
target.SetValue(DocumentProperty, value);
}
/// <summary>
/// Called when Document changes.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
private static void OnDocumentChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var viewer = sender as DocumentViewer;
if (viewer == null)
throw new InvalidOperationException(
"This behavior is only valid on DocumetViewers.");
var doc = e.NewValue as XpsDocument;
if (doc == null)
return;
viewer.Document = doc.GetFixedDocumentSequence();
}
#endregion
}
Then in my model I expose my visual as an XpsDocument
var pack = PackageStore.GetPackage(_uri);
if (pack != null)
return new XpsDocument(pack, CompressionOption.SuperFast, _uri.AbsoluteUri);
MemoryStream ms = new MemoryStream(2048);
Package p = Package.Open(ms, FileMode.Create, FileAccess.ReadWrite);
PackageStore.AddPackage(_uri, p);
XpsDocument doc = new XpsDocument(p, CompressionOption.SuperFast, _uri.AbsoluteUri);
var writer = XpsDocument.CreateXpsDocumentWriter(doc);
var collator = writer.CreateVisualsCollator();
// write the visuals using our collator
collator.BeginBatchWrite();
collator.Write(Visual);
collator.EndBatchWrite();
p.Flush();
return doc;
Just add a DocumentViewer, bind the result of the conversion method to it via the behavior, and there it is. I'm sure there's a shortcut in here somewhere...

'System.Windows.Application' cannot run multiple times

This error ocurr when I call System.Application.Run(); in a WPF application. There is no other call to method Run(). Someone did see this?
Why would you need to call Run() manually? The framework does it for you if you have an Application derived object usually defined in a file called App.xaml.
If you go in your obj directory you'll find the auto generated file for the application object (file is called App.g.cs) and it has something similar to:
/// <summary>
/// App
/// </summary>
public partial class App : System.Windows.Application {
/// <summary>
/// InitializeComponent
/// </summary>
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
public void InitializeComponent() {
#line 4 "..\..\App.xaml"
this.StartupUri = new System.Uri("Window1.xaml", System.UriKind.Relative);
#line default
#line hidden
}
/// <summary>
/// Application Entry Point.
/// </summary>
[System.STAThreadAttribute()]
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
public static void Main() {
gridsh.App app = new gridsh.App();
app.InitializeComponent();
app.Run();
}
}
Notice it defines a static main which has the call to Run.
No, if you didn't include another call to Run, maybe try deleting the file and recreating it...

Resources