I have a console app, where I want to show wpf window:
ths = new ThreadStart(() =>
{
window = new wpf_lib.Container();
System.Windows.Application app = new Application();
app.Run(window);
});
th = new Thread(ths);
th.TrySetApartmentState(ApartmentState.STA);
th.Start();
But, how I can get access for wpf window methods and properties from any method in the console app?
You've two options, first and in your case easiest:
Use the System.Windows.Application.MainWindow method to get access to the Window's properties and methods.
Second: Add a property/method to the Container class that returns the Window object you want. This may or may not be possible, mostly depending on wether you've access to the source code of that class.
If you are encountering exceptions regarding thread access, use the Dispatcher of the appropriate WPF object. See this question for details.
Related
I have an unmanaged application which uses a WPF assembly for some of its user interface. Because of this arrangement Application.Current is not created automatically. So when the first WPF window is loaded, my code does this:
if (System.Windows.Application.Current == null)
{
new System.Windows.Application();
}
This works the first time and is the approach I've seen recommended.
But if the user closes the (only) WPF window, and later loads it again, even though Current == null again an exception is thrown when the Application ctor is called.
It is clear in the documentation that you can only have one Application per AppDomain - but why then is Current null and yet I can't create it?
The exception that is thrown is of type InvalidOperationException and has the message:
Cannot create more than one System.Windows.Application instance in the
same AppDomain.
Its InnerException is null.
To work around this I have tried to:
Construct Application using ShutdownMode = ShutdownMode.OnLastWindowClose
Explicitly call Current.Shutdown() when the WPF window is closed
but neither has made any difference.
Is there some other way to manually manage the lifetime of the Current object? Or should I instead attempt to create it when the unmanaged application starts, and then rely on it always being set for the lifetime of the process?
The documentation you linked states the following in its remarks section:
Only one instance of the Application class can be created per
AppDomain, to ensure shared access to a single set of
application-scope window, property, and resource data. Consequently,
the parameterless constructor of the Application class detects whether the instance being initialized is the first instance in an
AppDomain; if it is not, an InvalidOperationException is thrown.
The part I highlighted implies that it is not checking if it is the only / single application currently running, but rather that it checks if any another Application instance has been initialized before (regardless of whether or not it has been closed yet).
Taking a look at the source code of the Application class confirms this: The Application class internally uses a static flag (_appCreatedInThisAppDomain) that is set only once when initializing the first Application instance. But apparently this flag is never reset, which prevents you from creating any more Application instances within the same AppDomain.
This is easy in WinForms, not so much in WPF.
Obviously, we don't have an Application problem, we have an AppDomain problem.
I put a reasonable amount of effort into this, but couldn't get it to behave as I wanted it to, that is to destroy the old then recreate an AppDomain on a new Thread when the spacebar is pressed, I suppose that makes sense though given the scope.
It's a work around at best, and may not even be an option in your situation.
Is there some other way to manually manage the lifetime of the Current
object?
As best I can tell, the simple answer is to just maintain a WPF message loop Thread for the life of the program (via ShutdownMode.OnExplicitShutdown), and use the Application.Current.Dispatcher to display WPF objects as needed.
Here's an example of what I mean, as implemented in a managed console application:
class Program
{
static void Main(string[] args)
{
Thread t = CreateThread();
t.Start();
bool quit = false;
while (!quit)
{
switch(Console.ReadKey().Key)
{
case ConsoleKey.Escape:
Application.Current.Dispatcher.Invoke(() => Application.Current.Shutdown());
quit = true;
break;
case ConsoleKey.W:
Application.Current.Dispatcher.Invoke(() =>
{
var w = new Window() { Width = 500, Height = 500, Title = "WPF Window" };
w.Show();
});
break;
case ConsoleKey.D:
Application.Current.Dispatcher.Invoke(() =>
{
var d = new Window() { Width = 500, Height = 500, Title = "WPF Dialog" };
d.ShowDialog();
});
break;
case ConsoleKey.Spacebar:
//// Nope!
//Application.Current.Dispatcher.Invoke(() => Application.Current.Shutdown());
//t = CreateThread();
//t.Start();
break;
}
};
}
static Thread CreateThread()
{
var t = new Thread(() =>
{
if (System.Windows.Application.Current == null)
{
new System.Windows.Application();
Application.Current.ShutdownMode = ShutdownMode.OnExplicitShutdown;
}
Application.Current.Run();
});
t.SetApartmentState(ApartmentState.STA);
return t;
}
}
You'll need references to PresentationCore, PresentationFramework and WindowsBase to build this example.
I hope it at least inspires someone.
EDIT: FYI, this may not work anymore... It worked when I posted it, now two days later it does not. There was a cumulative update for .NET Framework (kb4538122) installed yesterday, but I'm not sure if this was the breaking change or not.
EDIT: I updated the code, it now works again.
I have an Console Application started as [STAThread].
That application should open a seperate Wpf UI for entering some settings.
The functions for that:
private static void openUI()
{
var application = new System.Windows.Application();
//referenced project in the same solution
var ui = new ManagerUI.MainWindow();
//blocks execution
application.Run(ui);
application.Shutdown();
}
Opening the UI for the first time works as expected.
The problem occurs when opening the UI for the second time.
I get an System.InvalidOperationException, saying that I cannot run more than one Application-Instance in the same AppDomain.
For saving ram, it must be closed between the operations.
I also tried to create the System.Windows.Application in the constructor.
But as soon as I run the application the second time, I get a very similiar exception.
The InitializeComponents() method of the UI throws an System.InvalidOperationException, saying that the Object is going to be terminated.
The StackTraces shows that the error appears when the xaml is parsed, so I conclude it cannot open it, because it is still opened by the first execution.
Neither calling ui.Close() nor calling application.Shutdown() solves the problem (Environment.Exit() closes everything, including my Console Application).
The ram profiler indicates, not everything was closed correctly because it shows an higher use after the Window was closed, than before it was opened in the firts place.
How do I properly close the Application instance, or how do I re-use it to run an Wpf Application multiple times?
Having looked at the source code for the Application class, it doesn't look like you will be able to work around this, as various static fields are initialized by the class constructor:
public Application()
{
...
lock(_globalLock)
{
if (_appCreatedInThisAppDomain == false)
{
...
_appInstance = this;
...
_appCreatedInThisAppDomain = true;
}
else
{
throw new InvalidOperationException(...);
}
}
}
...
static private object _globalLock;
static private bool _appCreatedInThisAppDomain;
static private Application _appInstance;
...
Basically the constructor sets _appCreatedInThisAppDomain to true, and as that field is private you have no way of setting it back*.
I think the only way of achieving something similar to what you want is to write a separate WPF application, then use the Process class to launch that from your console application. Alternatively, you could theoretically create a separate AppDomain to host your WPF stuff but that would be a lot more complicated.
[*] other than using Reflection, but let's not go there!
You may create a class that derives from MarshalByRefObject:
public class AppDomainWrapper : MarshalByRefObject
{
public void openUI()
{
var application = new System.Windows.Application();
var ui = new Window();
application.Run(ui);
application.Shutdown();
}
}
...and execute its openUI() method in its own application domain:
[STAThread]
static void Main(string[] args)
{
const int n = 2;
for (int i = 0; i < n; ++i)
{
AppDomain appDomain = AppDomain.CreateDomain("AppDomain");
AppDomainWrapper application = appDomain.CreateInstanceAndUnwrap(typeof(AppDomainWrapper).Assembly.FullName, typeof(AppDomainWrapper).FullName) as AppDomainWrapper;
application.openUI();
AppDomain.Unload(appDomain);
}
}
Have a look at this question:Does a WPF Application Actually Need Application.Run?.
Basically it says, that you can open windows using window.ShowDialog() method without Application instance
The think is that Application.Run does not do anything important but run Dispatcher loop. ShowDialog have its own Dispatcher. You can create Application singleton instance however, since it contains some shared resources.
Hack(run it after application.Shutdown()). I use this in tests:
var field = typeof(Application).GetField(
"_appCreatedInThisAppDomain",
BindingFlags.Static | BindingFlags.NonPublic) ??
throw new InvalidOperationException(
"Field is not found: _appCreatedInThisAppDomain.");
field.SetValue(null, false);
Steven Rands shows the problem.
I have the same problem in an external add-in. But I need an application object for xaml resources and a valid Application.Current.
In my eyes this is a bug. If you call Shutdown() this member should also be reset to false.
So Im developing an addon with a UI that is used inside an application (main application). To make my UI responsive when the main application is working I'm starting my UI in its separate thread like this:
public void ShowDialog(IIFCConverter ifcConverter)
{
thread = new Thread(x =>
{
thread.Name = "UI-thread";
window = new MainWindow();
var mainViewModel = ServiceLocator.Current.GetInstance<MainWindowViewModel>();
mainViewModel.SetIFCConverter(x as IIFCConverter);
ViewModelLocator.MainWindow = window;
window.ShowDialog();
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start(ifcConverter);
}
The first time I start my addon it all works. The second time I start it and it tries to raise events (like OnCollectionChanged) I get a NotSupportedException with the message: "This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread"
This is one of my methods:
private void AddNewFile(AddNewFileMessage obj)
{
if (!(obj.Sender is ButtonViewModel)) return;
if (string.IsNullOrEmpty(obj.Path)) return;
var ifcFileViewModel = new IFCFileViewModel(new Common.Model.IFCFile { Path = obj.Path, Active = true });
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
ListBoxItems.Insert(ListBoxItems.Count - 1, ifcFileViewModel);
});
}
I get this eventhough Im using the DispatcherHelper from MVVM light.
I have tried using the "normal" dispatcher to, but that gives me the same result.
First of all Im curious to know the mechanics of why its doing this?
I've checked my threads and I can see that the OnOllectionChanged is called from my UI-thread.
I cant seem to find any differences to the thread structure between the first run (that works) and the following.
Second, what can I do about this?
Things that I've tested that didnt help:
Im using the IoC container from MVVM light and its registered as a LocatorProvider, but I create a new IoC container everytime I initialize the UI and set that instance as the LocatorProvider.
Im initializing the DispatcherHelper in the constructor of my window. So that should be on the correct thread.
Something that does actually work is wrapping the action in a try Catch-block like this:
private void AddNewFile(AddNewFileMessage obj)
{
if (!(obj.Sender is ButtonViewModel)) return;
if (string.IsNullOrEmpty(obj.Path)) return;
var ifcFileViewModel = new IFCFileViewModel(new Common.Model.IFCFile { Path = obj.Path, Active = true });
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
try
{
ListBoxItems.Insert(ListBoxItems.Count - 1, ifcFileViewModel);
}
catch(Exception ex)
{
}
});
}
However I find this very ugly and would like to avoid it and why does that even work? Everything seems to work fine when I wrap all my actions in try Catch-blocks.
Ok, so I found the real problem.
The viewmodel was indeed created on the same thread as my addon UI.
HOWEVER: I was using the Messenger that is avaliable in the MVVMLight toolkit to handle communication between viewmodels and I forgot to unregister the viewmodel when my windows was closed. So the second time I opened my window and started sending messages between my viewmodels. The first viewmodel reacted and that was what was causing the problem.
That is also why the try-Catch-block worked, because there was a second call from the correct thread that was working.
I added a datetime to the constructor of the viewmodel and there are indeed two different viewmodels being called. I do not understand why I get that exception. If anything the first viewmodel should be connected via datacontext to the first view. Its like the view has hooked up to the events of two different viewmodels.
Anyway, it works now: I just unregister the viewmodel from the message service when my window is closed and it works like a charm.
Take a look at the BindingOperations.EnableCollectionSynchronization and read this topic: Using BindingOperations.EnableCollectionSynchronization.
I'm currently trying to use a WPF component that makes use of Application.Current from a WPF application, however due to several reasons I never call Application.Run (nor is that an option). The result is a NullReferenceException.
I'm basically trying to display multiple instances of the same WPF window from what would be a console application.
Any advice (and code samples in C#/F#) would be welcome!
Thanks in advance
Just to offer an alternative solution.
It is possible to keep an application running without any windows open. To me this feels less 'hackish'. :) http://msdn.microsoft.com/en-us/library/system.windows.application.shutdownmode.aspx
public class AppCode : Application
{
// Entry point method
[STAThread]
public static void Main()
{
AppCode app = new AppCode();
app.ShutdownMode = ShutdownMode.OnExplicitShutdown;
app.Run();
...
app.Shutdown();
}
}
EDIT:
Ok this got a bit cumbersome. Application.Run will block, so it needs to run in its own thread.
When it does run in its own thread, any interaction between your main thread and your ui thread had best be done by Application.Current.Dispatcher.Invoke. Here is some working code, that assumes you have a class that inherits from Application. I'm using a modified App.xaml/App.xaml.cs that a WPF project template creates for you, to get nice handling of ResourceDictionaries for free.
public class Program
{
// Entry point method
[STAThread]
public static void Main()
{
var thread = new System.Threading.Thread(CreateApp);
thread.SetApartmentState(System.Threading.ApartmentState.STA);
thread.Start();
// This is kinda shoddy, but the thread needs some time
// before we can invoke anything on the dispatcher
System.Threading.Thread.Sleep(100);
// In order to get input from the user, display a
// dialog and return the result on the dispatcher
var result = (int)Application.Current.Dispatcher.Invoke(new Func<int>(() =>
{
var win = new MainWindow();
win.ShowDialog();
return 10;
}), null);
// Show something to the user without waiting for a result
Application.Current.Dispatcher.Invoke(new Action(() =>
{
var win = new MainWindow();
win.ShowDialog();
}), null);
System.Console.WriteLine("result" + result);
System.Console.ReadLine();
// This doesn't really seem necessary
Application.Current.Dispatcher.InvokeShutdown();
}
private static void CreateApp()
{
App app = new App();
app.ShutdownMode = ShutdownMode.OnExplicitShutdown;
app.Run();
}
}
The following is the intended behavior of Application class:
The first open window is the MainWindow.
The only window in the list becomes the MainWindow (if others are to
be removed).
Application Class is designed to exit if no windows are present in
windows list.
Check this link.
So basically you cannot run an Application, without any window open. Keep a window open but hidden.
If I have misunderstood your problem, then perhaps the following similar cases might help:
Managing Application Resources when WPF is Hosted.
When running unit tests in Visual Studio 2008
I have a silverlight application. When some action is executed, i want to show a wait cursor.
The problem is that i'm working with threads, and all my actions are executed in a thread.
So i have a threading helper that invokes and awaits all the threads - this works fine.
I need to access the main window element to change its cursor. How can i achieve this?
This:
ThreadingHelper.Invoke(() => App.Current.MainWindow.Content.Cursor = cursorStyle);
Throws me System.NotSupportedException: Out-of-browser specific settings do not affect in-browser applications.
How can i do the same for In-browser?
I have managed to get this working by this code:
ThreadingHelper.Invoke(() => {
var page = (MainPage)Application.Current.RootVisual;
page.Cursor = cursorStyle;
});
But maybe someone will offer more type-safe method?