Dialog Window for configuration in MVVM - wpf

i'm relative new to MVVM. My current problem is a modular dialog which should "autostart" at the beginning.
I've followed the example of WAFs Email Client for modular dialogs. Is it right that the only important thing is to set the Owner Property of the dialog to the instance of the main window of the application (and of course show the window with ShowDialog() instead of Show()?
If you close this dialog without configuration the application will shutdown. But now, if I open the main window in visual studios designer mode the configuration dialog comes up and if I close it visual studio crashes.
This is because I call the ShowDialog() of the configuration dialog in the constructor of my main windows view model.
To avoid this i can check for DesignerProperties.IsInDesignTool Property, but this is more a workaround as good code style, right?
Do you have any suggestions? Thanks.

The problem here is that you are showing a dialog in the constructor of a class. That's something you don't want to do.
I would solve it like this:
Don't specify a StartupUri in your app.xaml but override OnStartup. There you check whether the configuration dialog should be shown or not. If it should be shown, show it and after it has closed with OK, show you main window.
Something like this:
override void OnStartup(...)
{
if(configurationNotComplete)
{
ConfigDialog cfg = new ConfigDialog();
if(!(cfg.ShowDialog() ?? false))
{
Shutdown();
return;
}
}
MainWindow window = new MainWindow();
window.Show();
}
You have another problem with your current approach: Your ViewModel shows a modal dialog. This means it knows at least about one View: That of the modal dialog. MVVM is one way: The View knows about the ViewModel, the ViewModel knows about the Model. There should be no connection in the other direction.

Related

Dialog appears behind other windows

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?
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.
Application Windows
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.
Custom Dialog 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.

Starting a WPF application as hidden

I'm writing a WPF application and want it to start as a hidden window. I've created the Window object and set its Visibility property to Visibility.Hidden before calling Application.Run(). Then, I have an event handler for Window.Loaded that also sets the visibility to Visibility.Hidden. Between the call to Application.Run() and the callback to OnWindowLoaded(), there is a black outline of the window that flashes up on the screen and then disappears. It's like the window manager is creating a drop shadow for the window or something and then hides it immediately.
After running my project through instrumentation, I finally found that Window.Show() was somehow getting called. So, I looked into the source code at http://www.dotnetframework.org/Search.aspx:
Application.Run() ends up calling a private method named Application.RunInternal().
RunInternal() checks the visibility of the Window object that was passed in to the Run() method.
If the Visibility property is not Visibility.Visible, a call to Window.Show() is made.
I then looked at the source for System.Windows.Window:
Window.Show() sets the Visibility property on itself (the window) to be Visibility.Visible.
Based on this, I don't see how to force the window to stay hidden. By trying to make the window invisible at startup, I'm causing the Application object to call Window.Show(); I don't understand why the Application object even cares about the window's visibility. It's been a frustrating experience... :-(
I've seen other answers that say to not call Application.Run() and to instead set up your own event dispatchers, but that seems like overkill for something that should be easy. I just want the main window to stay hidden, for no "flicker" to appear at app startup, and for the window to become visible when I'm ready for it to do so (which happens later in my application logic).
Can anyone offer a suggestion?
Did you remove the StartupUri entry in App.xaml? If you do, the App class won't instantiate the window for you and show it. You can do this by yourself by overwriting the App.OnStartup method.
Basically, I build a composition root in this OnStartup method and just create a window at the end of the process:
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
// Do your custom initialization code here
MainWindow = new MainWindow();
MainWindow.Show();
}
If you really want to omit the whole application build up process (which I wouldn't recommend, as you won't have features like the fallback to Application Resources), you can create a Dispatcher by yourself using this code:
var dispatcher = Dispatcher.CurrentDispatcher;
var synchronizationContext = new DispatcherSynchronizationContext(dispatcher);
SynchronizationContext.SetSynchronizationContext(synchronizationContext);
Dispatcher.Run();
The comment on this Answer finally led me to find the solution to this issue. I needed to display multiple windows on multiple screens at once, and by minimizing the window it gives me the performance I needed. Thanks.

I'm Getting Cannot set Visibility or call Show, ShowDialog Exception

I'm using WAF (Wpf Application Framework) to create a dialog as shown in the ModelView sample application. I am trying to put up a simple AboutBox by mirroring the code for putting up the CreateEmailAccountWizard dialog box. My About box shows up fine the first time, but when I call it again from the menu, it gives me the following exception:
Cannot set Visibility or call Show, ShowDialog, or WindowInteropHelper.EnsureHandle after a Window has closed.
First, I don't know what this message means. Second, what am I doing wrong? The sample application doesn't throw this exception when you close and reopen the CreateEmailAccountWizard dialog box. My code is nearly identical to it. Any help would be greatly appreciated.
Never mind. Found the source of the problem. I was creating a singleton from MEF and it was causing the same dialog instance to run twice. I solved the issue by doing the following:
var shellView = _container.GetExportedValue<IShellView>();
_aboutDialogViewModel = _container.GetExportedValue<AboutDialogViewModel>();
_aboutDialogViewModel.ShowDialog(shellView);
I also had to set the MEF attribute on the class to tell it to not use a singleton:
[Export, PartCreationPolicy(CreationPolicy.NonShared)]
public class AboutDialogViewModel : ViewModel<IDialogView>
{
[ImportingConstructor]
public AboutDialogViewModel(IDialogView view) : base(view)

WPF, Prism v2, Region in a modal dialog, add region in code behind

I have a composite WPF application. In one of my modules I want to make a wizard and have the steps show up in a region so I can switch between the steps easier. Originally I had this wizard showing up in a tab region and the nested region worked fine. Now I want to make it into a modal dialog box, but after I open it the inner region never gets registared with the region manager; So I can't add my wizard steps.
I was under the impression that the region manager was global, and just adding cal:RegionManager.RegionName="WizardSteps" would do it, but apparently not.
If i pass the region manager to the view I might be able to use it...Does anyone know how to add a region to a ContentControl in code behind?
The problem is that regions search up the visual tree for the RegionManager attached property, and then register themselves with that manager. In the main window that's fine, but in a child window this doesn't happen.
In the Bootstrapper, after the shell is created, the following code is performed.
RegionManager.SetRegionManager(shell, this.Container.Resolve<IRegionManager>());
RegionManager.UpdateRegions();
To get the region manager to work with your child window do the same thing right after you've created the window.
EDIT
To set the region name of a control, you also set the attached property of the RegionManager, like so...
RegionManager.SetRegionName(control, "MyRegion");
However you can do this in xaml aswell. The reason why your regions in a separate window don't work is because the RegionManager needs to be set on the base window, like I showed above.
It is actually quite simple.
In your popup xaml add a regionname as you do in the shell.
Then in the popups constructor, add the following call:
public Popup(IRegionManager regionManager)
{
InitializeComponent();
RegionManager.SetRegionManager(this,regionManager);
}
This works for me in Prism v.1 - shouldn't be too much different in later versions.
I found something thats almost working. I'm sure if i could bind the region's active view to the contentContol's content property then it would work, but I haven't managed that yet.
IRegionManager MyRegionManager = container.Resolve<IRegionManager>();
SingleActiveRegion newRegion = new SingleActiveRegion();
MyRegionManager.Regions.Add("WizardSteps", newRegion);
//Binding
Binding myBinding = new Binding("ActiveViews");
myBinding.Source = newRegion;
view.stepControl.SetBinding(ContentControl.ContentProperty, myBinding);

WPF ShowDialog and ElementHost

is it possible to display a Modal Window from a WPF User Control, that is a child of an ElementHost, and set the owner/parent of the Modal Window to the containing Form control?
I'm guessing you can't do this, as the Owner property takes an instance of Window, where as I want to set it to the parent of the Element Host control, which is an old Windows Forms Form control. Just wondering if there is a work around or alternative approach.
The problem is when the Modal Window is displayed and the user switches to another application, then back again, the Modal Window is hidden and the user is unable to interact with the main Window. This is due to Windows thinking the Modal Window is still displayed, when it isn't, as there is no Owner/Parent relationship set.
Cheers,
James.
I'm using WindowInteropHelper to solve that problem like this:
var wpfDialog = new MyWpfDialog();
var interopHelper = new WindowInteropHelper(wpfDialog)
{
Owner = winFormsDialog.Handle
};
wpfDialog.ShowDialog();
I know this post is old, but I came across a way to find the winform window that is hosting the ElementHost from the context of a wpf UserControl where you may not have access to the winform window. I found this to be useful so that I don't have to pass the host window around.
HwndSource winformWindow = (System.Windows.Interop.HwndSource.FromDependencyObject(wpfControlInElementHost) as System.Windows.Interop.HwndSource);
if (winformWindow != null)
{
var interopHelper = new WindowInteropHelper(wpfWindow)
{
Owner = winformWindow.Handle
};
}
Ok just found the solution using WindowInteropHelper.
http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/44c903fb-9514-401c-ba85-58acd5293c1b

Resources