Reputation: 8662
I'm using the Prism dialog service to create an unhandled exception dialog. I have a splash screen window that, while it doesn't have the TopMost
property set (tried that and it was totally miserable :), I use Activate
on it to get it to rise to the top so it appears over the main window as it loads up.
The problem is when an exception is thrown and the unhandled exception dialog that I made pops up, it appears behind the splash screen, leaving the user wondering what happened until they notice an extra button in the task bar.
The dialog is a UserControl
, as instructed by the documentation, and it has no Activate
function. And when I try to get a handle to the parent window in the constructor, the Parent
property is null
.
How can I bring a dialog to the top of other windows?
Upvotes: 0
Views: 732
Reputation: 22089
Prism's dialog service sets the Owner
of a dialog automatically to the first active Window
that is found in the Application.Current.Windows
collection if the dialog window does not already have set an owner. It does not expose any methods to access or activate windows directly. In an MVVM service you would not do that.
In Prism <= 7.x, you can only have a single dialog window host. By default this is a regular Window
. You have to share this dialog host window type for all of your dialogs, so setting a different Owner
in a custom Window
is not an option. Since Owner
is null
when a Window
is instantiated, its owner will be the first active window.
This behavior is problematic if you want the Owner
of your dialog to be a different window like your splash screen, so it appears on top of it automatically or modally or have no owner at all.
You cannot activate the dialog in the constructor of your UserControl
, as it cannot be set as the Content
of the parent dialog host Window
before it is instantiated. You either have to use the Application.Current.Windows
collection to find the dialog or create a custom dialog service for now for both activating or setting an Owner
.
If you want to find the dialog host window in Application.Current.Windows
, you can filter this collection after showing the dialog and check the Content
of each window, one will contain your UserControl
type. If you only show the unhandled exception dialog once, there will be a single window instance with this UserControl
. If you show it multiple times, you could identify the current instance via a property on its view model accessible by DataContext
that you have set before by passing a DialogParameter
in Show
. Consider creating a service.
Personally, I prefer creating a custom dialog service, as it gives you more control over what you are doing. To create a custom interface that exposes the methods of the built-in IDialogService
, as well as your own methods, depeding on your requirements.
public interface ICustomDialogService : IDialogService
{
public void ShowOrphan(string name, IDialogParameters parameters, Action<IDialogResult> callback);
}
I simply create a new method ShowOrphan
to show a dialog that has no owner and is activated automatically when shown, so it is in the foreground. Adapt it to your requirements, like passing in an explicit owner or just an owner type, so that the dialog service searches an instance in the application window collection itself.
Next, implement the ICustomDialogService
in a CustomDialogService
type. You can just copy the code from the public repository. Be sure to use the code for your version of Prism, see Tags.
You do not have to change much. Just pass appropriate parameters from your new public method down the ConfigureDialogWindowProperties
method, which is responsible for setting the window owner. If activating the dialog window is really necessary apart from showing it, you can do that in ShowDialogInternal
.
public class CustomDialogService : ICustomDialogService
{
// ...other public members.
public void ShowOrphan(string name, IDialogParameters parameters, Action<IDialogResult> callback)
{
// Appended a new parameter "isOrphan"
ShowDialogInternal(name, parameters, callback, false, true);
}
// ...other private members.
// Appended a new parameter "isOrphan"
void ConfigureDialogWindowProperties(IDialogWindow window, FrameworkElement dialogContent, IDialogAware viewModel, bool isOrphan)
{
// ...other code.
if (isOrphan)
return;
if (window.Owner == null)
window.Owner = System.Windows.Application.Current.Windows.OfType<Window>().FirstOrDefault(x => x.IsActive);
}
}
If you are done, you have to overwrite the registration for the old dialog service.
containerRegistry.RegisterSingleton <CustomDialogService>();
containerRegistry.Register<IDialogService, CustomDialogService>();
containerRegistry.Register<ICustomDialogService, CustomDialogService>();
Now you can resolve the ICustomDialogService
and use its ShowOrphan
method to show the window.
Upvotes: 3