I am developing a WPF-Application using PRISM/Unity and the MVVM approach. So far everything is fine, but I think I have a conceptual problem.
Since I need modal dialogs I implemented a dialogservice, which gets injected into the viewmodels and can be used to open up a modal dialog with a corresponding view. This dialog needs to be navigable ( = contains a region), so that I am able to switch views within the dialog. For this the service creates an instance of a window and navigates to the requestd view in a DialogRegion. This also works.
My problem now is, that at some point I need to open a second modal dialog which also needs to be navigable. If I use my dialogservice I would get a new instance of the dialog window and an exception is thrown, since the region was already registered with the regionmanager (It's done in the code behind of the dialog window). This makes sense, since the regionmanager would not be able to distinguish the regions otherwise, but prevents me from opening a second navigable dialog.
What would be a better approach? I can only think of creating a second dialog window with a different name for the region, but that seems to be a very ugly solution...
EDIT 1: Code for showing a dialog:
dialogService.ShowDialog<MyViewModel>();
Code in the dialog service for showing a dialog:
public void ShowDialog<TDialogViewModel>() where TDialogViewModel : class
{
IModalDialog dialog = ServiceLocator.Current.TryResolve<IModalDialog>();
dialog.Owner = Application.Current.MainWindow;
regionManager.RequestNavigate(typeof(TDialogViewModel), Regions.DialogRegion);
dialog.ShowDialog();
}
Code in the dialog-window:
public DialogWindow(IRegionManager regionManager) : this()
{
RegionManager.SetRegionManager(this, regionManager);
this.regionManager = regionManager;
}
private void Window_Unloaded(object sender, RoutedEventArgs e)
{
regionManager.Regions.Remove(Regions.DialogRegion);
regionManager.Regions.Remove(Regions.DialogStatusRegion);
}
Code in the view for construction, navigation and showing other dialogs
public MyViewModel(IRegionManager regionManager, IModalDialogService dialogService) : this()
{
this.regionManager = regionManager;
this.dialogService = dialogService;
}
void NavigateToSecondView()
{
regionManager.RequestNavigate<MyViewModel2>(Regions.DialogRegion);
}
void ShowDialog2()
{
// This is where a second dialog window with MyViewModel3 should be shown
dialogService.ShowDialog<MyViewModel3>();
}
EDIT 2: XAML for the Window:
<Window.Resources>
<DataTemplate DataType="{x:Type localModels:MyViewModel}">
<localViewsProject:MyView/>
</DataTemplate>
</Window.Resources>
<Grid>
<ContentControl Grid.Row="0" x:Name="DialogContent" Margin="10,10,10,10"
prismRegions:RegionManager.RegionName="{x:Static navi:Regions.DialogRegion}">
</ContentControl>
</Grid>
EDIT 3 - Code for IModalDialog: I got the idea for IModalDialog somewhere in the web, but I can't remember where. The idea is to have an interface that is implemented by some window-instances and allows for interaction with the user. My dialog only has one content-region (plus a region for status/progress notifications) that can be filled through prism.
Here some code to explain the idea:
public partial class DialogWindow : Window, IModalDialog
{
internal IRegionManager regionManager;
public DialogWindow()
{
InitializeComponent();
}
public DialogWindow(IRegionManager regionManager)
: this()
{
RegionManager.SetRegionManager(this, regionManager);
this.regionManager = regionManager;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// Load Model, when window is loaded
BaseViewModel model = this.DialogContent.Content as BaseViewModel;
if (model != null)
{
model.LoadData();
}
}
private void Window_Unloaded(object sender, RoutedEventArgs e)
{
regionManager.Regions.Remove(Regions.DialogRegion);
regionManager.Regions.Remove(Regions.DialogStatusRegion);
}
}
So in order to use it I have a static ModalDialogService, which receives a model and some action to be done after the dialog is closed. Something like this:
public void ShowDialog<TDialogViewModel>(Action<TDialogViewModel> initializeAction = null, Action<TDialogViewModel> actionAfterClose = null) where TDialogViewModel : class
{
IModalDialog dialog = ServiceLocator.Current.TryResolve<IModalDialog>();
if (actionAfterClose != null)
{
WeakEventManager<IModalDialog, EventArgs>.AddHandler(
dialog,
"Closed",
(sender, e) => dialogView_Closed(sender, e, typeof(TDialogViewModel), Regions.DialogRegion, actionAfterClose));
}
dialog.Owner = Application.Current.MainWindow;
regionManager.RequestNavigate(typeof(TDialogViewModel), Regions.DialogRegion);
TDialogViewModel model = null;
var region = regionManager.Regions[Regions.DialogRegion];
foreach (var view in region.ActiveViews)
{
if (view is TDialogViewModel)
{
model = (TDialogViewModel)view;
if (initializeAction != null)
{
initializeAction(model);
}
}
}
// Attach an event listener to the closing event in order to prevent closing
// if the form is not in a closeable state.
if (typeof(IConfirmNavigationRequest).IsAssignableFrom(typeof(TDialogViewModel)))
{
WeakEventManager<IModalDialog, CancelEventArgs>.AddHandler(
dialog,
"Closing",
(sender, e) => dialogView_Closing(sender, e, Regions.DialogRegion));
}
// This event can be published by a viewmodel in order to close the dialog
eventAggregator.GetEvent<DialogCloseRequestedEvent>().Subscribe((containingDialog) =>
{
if (containingDialog == model)
{
dialog.Close();
}
});
dialog.ShowDialog();
}
}