Click here to Skip to main content
Click here to Skip to main content

Showing Dialogs When Using the MVVM Pattern

By , 5 Oct 2010
 

Contents

Introduction

This article will address one of the problems a developer might run into when using the MVVM pattern, namely opening dialogs from ViewModels. Basic knowledge of the pattern is expected. Josh Smith has written a fantastic article in MSDN Magazine which can serve as the starting point for those that are unfamiliar with the pattern.

There already exist some solutions exploring this area, but none of them seem to be widely accepted among the MVVM community. Onyx on CodePlex is probably the most complete solution. Sacha Barber announced that he is working on a new MVVM framework, but hasn't set any release date. At the same time, WPF Disciples are teaming up to create a MVVM reference application, again with no release date yet.

My Interpretation of MVVM

I am possibly sticking my neck out here, but I would like to state some of my own interpretations of the MVVM pattern:

  • The fundamental core is the separation of UI and logic. ViewModel and View can communicate using bindings, making it possible for the View to be happily unaware of the ViewModel and vice versa. I know some of you might say: "well, actually the View knows about the ViewModel since the ViewModel is stored within the DataContext property, and the View is declaring its bindings to match properties on the ViewModel, surely the View must know the ViewModel?". I don't think so; we have to analyze what knows actually means. The ViewModel is stored in the DataContext property, that is correct, but DataContext is of type Object. From the View's point, the only thing it knows is whether the property has been set or not, nothing about the actual content. Let's get to the bindings declared in the View; do they say something about the ViewModel? I would have to say no here as well. The View might declare whatever bindings it wishes, but the ViewModel isn't enforced to implement them.
  • The ViewModel should serve the View with data, but one might argue where the separation between the View and ViewModel is. If for instance the ViewModel is holding a list of items presented in a ListView, I would argue that sorting, grouping, and filtering could be the responsibility of the View, not the ViewModel. Attached properties can solve this for us, making the ViewModel happily unaware of the item presentation.
  • The ViewModel should not contain anything used purely in tests; either a class is testable or it isn't.

The Dialog Service

The concept of letting services handle relations between the ViewModel and View seems to be the most accepted solution. My contribution is IDialogService.

/// <summary>
/// Interface responsible for abstracting ViewModels from Views.
/// </summary>
public interface IDialogService
{
  /// <summary>
  /// Gets the registered views.
  /// </summary>
  ReadOnlyCollection<FrameworkElement> Views { get; }

  /// <summary>
  /// Registers a View.
  /// </summary>
  /// <param name="view">The registered View.</param>
  void Register(FrameworkElement view);

  /// <summary>
  /// Unregisters a View.
  /// </summary>
  /// <param name="view">The unregistered View.</param>
  void Unregister(FrameworkElement view);

  /// <summary>
  /// Shows a dialog.
  /// </summary>
  /// <remarks>
  /// The dialog used to represent the ViewModel is retrieved from the registered mappings.
  /// </remarks>
  /// <param name="ownerViewModel">
  /// A ViewModel that represents the owner window of the dialog.
  /// </param>
  /// <param name="viewModel">The ViewModel of the new dialog.</param>
  /// <returns>
  /// A nullable value of type bool that signifies how a window was closed by the user.
  /// </returns>
  bool? ShowDialog(object ownerViewModel, object viewModel);

  /// <summary>
  /// Shows a dialog.
  /// </summary>
  /// <param name="ownerViewModel">
  /// A ViewModel that represents the owner window of the dialog.
  /// </param>
  /// <param name="viewModel">The ViewModel of the new dialog.</param>
  /// <typeparam name="T">The type of the dialog to show.</typeparam>
  /// <returns>
  /// A nullable value of type bool that signifies how a window was closed by the user.
  /// </returns>
  bool? ShowDialog<T>(object ownerViewModel, object viewModel) where T : Window;

  /// <summary>
  /// Shows a message box.
  /// </summary>
  /// <param name="ownerViewModel">
  /// A ViewModel that represents the owner window of the message box.
  /// </param>
  /// <param name="messageBoxText">A string that specifies the text to display.</param>
  /// <param name="caption">A string that specifies the title bar caption to display.</param>
  /// <param name="button">
  /// A MessageBoxButton value that specifies which button or buttons to display.
  /// </param>
  /// <param name="icon">A MessageBoxImage value that specifies the icon to display.</param>
  /// <returns>
  /// A MessageBoxResult value that specifies which message box button is clicked by the user.
  /// </returns>
  MessageBoxResult ShowMessageBox(
    object ownerViewModel,
    string messageBoxText,
    string caption,
    MessageBoxButton button,
    MessageBoxImage icon);

  /// <summary>
  /// Shows the OpenFileDialog.
  /// </summary>
  /// <param name="ownerViewModel">
  /// A ViewModel that represents the owner window of the dialog.
  /// </param>
  /// <param name="openFileDialog">The interface of a open file dialog.</param>
  /// <returns>DialogResult.OK if successful; otherwise DialogResult.Cancel.</returns>
  DialogResult ShowOpenFileDialog(object ownerViewModel, IOpenFileDialog openFileDialog);

  /// <summary>
  /// Shows the FolderBrowserDialog.
  /// </summary>
  /// <param name="ownerViewModel">
  /// A ViewModel that represents the owner window of the dialog.
  /// </param>
  /// <param name="folderBrowserDialog">The interface of a folder browser dialog.</param>
  /// <returns>The DialogResult.OK if successful; otherwise DialogResult.Cancel.</returns>
  DialogResult ShowFolderBrowserDialog(object ownerViewModel, 
               IFolderBrowserDialog folderBrowserDialog);
}

The actual implementation of IDialogServiceis is DialogService.

/// <summary>
/// Class responsible for abstracting ViewModels from Views.
/// </summary>
class DialogService : IDialogService
{
  private readonly HashSet<FrameworkElement> views;
  private readonly IWindowViewModelMappings windowViewModelMappings;

  /// <summary>
  /// Initializes a new instance of the <see cref="DialogService"/> class.
  /// </summary>
  /// <param name="windowViewModelMappings">
  /// The window ViewModel mappings. Default value is null.
  /// </param>
  public DialogService(IWindowViewModelMappings windowViewModelMappings = null)
  {
    this.windowViewModelMappings = windowViewModelMappings;

    views = new HashSet<FrameworkElement>();
  }

  #region IDialogService Members

  /// <summary>
  /// Gets the registered views.
  /// </summary>
  public ReadOnlyCollection<FrameworkElement> Views
  {
    get { return new ReadOnlyCollection<FrameworkElement>(views.ToList()); }
  }

  /// <summary>
  /// Registers a View.
  /// </summary>
  /// <param name="view">The registered View.</param>
  public void Register(FrameworkElement view)
  {
    // Get owner window
    Window owner = GetOwner(view);
    if (owner == null)
    {
      // Perform a late register when the View hasn't been loaded yet.
      // This will happen if e.g. the View is contained in a Frame.
      view.Loaded += LateRegister;
      return;
    }

    // Register for owner window closing, since we then should unregister View reference,
    // preventing memory leaks
    owner.Closed += OwnerClosed;

    views.Add(view);
  }

  /// <summary>
  /// Unregisters a View.
  /// </summary>
  /// <param name="view">The unregistered View.</param>
  public void Unregister(FrameworkElement view)
  {
    views.Remove(view);
  }

  /// <summary>
  /// Shows a dialog.
  /// </summary>
  /// <remarks>
  /// The dialog used to represent the ViewModel is retrieved from the registered mappings.
  /// </remarks>
  /// <param name="ownerViewModel">
  /// A ViewModel that represents the owner window of the dialog.
  /// </param>
  /// <param name="viewModel">The ViewModel of the new dialog.</param>
  /// <returns>
  /// A nullable value of type bool that signifies how a window was closed by the user.
  /// </returns>
  public bool? ShowDialog(object ownerViewModel, object viewModel)
  {
    Type dialogType = 
      windowViewModelMappings.GetWindowTypeFromViewModelType(viewModel.GetType());
    return ShowDialog(ownerViewModel, viewModel, dialogType);
  }

  /// <summary>
  /// Shows a dialog.
  /// </summary>
  /// <param name="ownerViewModel">
  /// A ViewModel that represents the owner window of the dialog.
  /// </param>
  /// <param name="viewModel">The ViewModel of the new dialog.</param>
  /// <typeparam name="T">The type of the dialog to show.</typeparam>
  /// <returns>
  /// A nullable value of type bool that signifies how a window was closed by the user.
  /// </returns>
  public bool? ShowDialog<T>(object ownerViewModel, object viewModel) where T : Window
  {
    return ShowDialog(ownerViewModel, viewModel, typeof(T));
  }

  /// <summary>
  /// Shows a message box.
  /// </summary>
  /// <param name="ownerViewModel">
  /// A ViewModel that represents the owner window of the message box.
  /// </param>
  /// <param name="messageBoxText">A string
  /// that specifies the text to display.</param>
  /// <param name="caption">A string that specifies
  /// the title bar caption to display.</param>
  /// <param name="button">
  /// A MessageBoxButton value that specifies which button or buttons to display.
  /// </param>
  /// <param name="icon">A MessageBoxImage value
  /// that specifies the icon to display.</param>
  /// <returns>
  /// A MessageBoxResult value that specifies which
  /// message box button is clicked by the user.
  /// </returns>
  public MessageBoxResult ShowMessageBox(
    object ownerViewModel,
    string messageBoxText,
    string caption,
    MessageBoxButton button,
    MessageBoxImage icon)
  {
    return MessageBox.Show(FindOwnerWindow(ownerViewModel), 
                           messageBoxText, caption, button, icon);
  }

  /// <summary>
  /// Shows the OpenFileDialog.
  /// </summary>
  /// <param name="ownerViewModel">
  /// A ViewModel that represents the owner window of the dialog.
  /// </param>
  /// <param name="openFileDialog">The interface of a open file dialog.</param>
  /// <returns>DialogResult.OK if successful;
  /// otherwise DialogResult.Cancel.</returns>
  public DialogResult ShowOpenFileDialog(object ownerViewModel, 
                      IOpenFileDialog openFileDialog)
  {
    // Create OpenFileDialog with specified ViewModel
    OpenFileDialog dialog = new OpenFileDialog(openFileDialog);

    // Show dialog
    return dialog.ShowDialog(new WindowWrapper(FindOwnerWindow(ownerViewModel)));
  }

  /// <summary>
  /// Shows the FolderBrowserDialog.
  /// </summary>
  /// <param name="ownerViewModel">
  /// A ViewModel that represents the owner window of the dialog.
  /// </param>
  /// <param name="folderBrowserDialog">The interface
  ///           of a folder browser dialog.</param>
  /// <returns>The DialogResult.OK if successful;
  ///       otherwise DialogResult.Cancel.</returns>
  public DialogResult ShowFolderBrowserDialog(object ownerViewModel, 
                      IFolderBrowserDialog folderBrowserDialog)
  {
    // Create FolderBrowserDialog with specified ViewModel
    FolderBrowserDialog dialog = new FolderBrowserDialog(folderBrowserDialog);

    // Show dialog
    return dialog.ShowDialog(new WindowWrapper(FindOwnerWindow(ownerViewModel)));
  }

  #endregion

  #region Attached properties

  /// <summary>
  /// Attached property describing whether
  /// a FrameworkElement is acting as a View in MVVM.
  /// </summary>
  public static readonly DependencyProperty IsRegisteredViewProperty =
    DependencyProperty.RegisterAttached(
    "IsRegisteredView",
    typeof(bool),
    typeof(DialogService),
    new UIPropertyMetadata(IsRegisteredViewPropertyChanged));

  /// <summary>
  /// Gets value describing whether FrameworkElement is acting as View in MVVM.
  /// </summary>
  public static bool GetIsRegisteredView(FrameworkElement target)
  {
    return (bool)target.GetValue(IsRegisteredViewProperty);
  }

  /// <summary>
  /// Sets value describing whether FrameworkElement is acting as View in MVVM.
  /// </summary>
  public static void SetIsRegisteredView(FrameworkElement target, bool value)
  {
    target.SetValue(IsRegisteredViewProperty, value);
  }

  /// <summary>
  /// Is responsible for handling IsRegisteredViewProperty changes, i.e. whether
  /// FrameworkElement is acting as View in MVVM or not.
  /// </summary>
  private static void IsRegisteredViewPropertyChanged(DependencyObject target,
    DependencyPropertyChangedEventArgs e)
  {
    // The Visual Studio Designer or Blend will run this code when setting the attached
    // property, however at that point there is no IDialogService registered
    // in the ServiceLocator which will cause
    /// the Resolve method to throw a ArgumentException.
    if (DesignerProperties.GetIsInDesignMode(target)) return;

    FrameworkElement view = target as FrameworkElement;
    if (view != null)
    {
      // Cast values
      bool newValue = (bool)e.NewValue;
      bool oldValue = (bool)e.OldValue;

      if (newValue)
      {
        ServiceLocator.Resolve<IDialogService>().Register(view);
      }
      else
      {
        ServiceLocator.Resolve<IDialogService>().Unregister(view);
      }
    }
  }

  #endregion

  /// <summary>
  /// Shows a dialog.
  /// </summary>
  /// <param name="ownerViewModel">
  /// A ViewModel that represents the owner window of the dialog.
  /// </param>
  /// <param name="viewModel">The ViewModel of the new dialog.</param>
  /// <param name="dialogType">The type of the dialog.</param>
  /// <returns>
  /// A nullable value of type bool that signifies how a window was closed by the user.
  /// </returns>
  private bool? ShowDialog(object ownerViewModel, object viewModel, Type dialogType)
  {
    // Create dialog and set properties
    Window dialog = (Window)Activator.CreateInstance(dialogType);
    dialog.Owner = FindOwnerWindow(ownerViewModel);
    dialog.DataContext = viewModel;

    // Show dialog
    return dialog.ShowDialog();
  }

  /// <summary>
  /// Finds window corresponding to specified ViewModel.
  /// </summary>
  private Window FindOwnerWindow(object viewModel)
  {
    FrameworkElement view = 
      views.SingleOrDefault(v => ReferenceEquals(v.DataContext, viewModel));
    if (view == null)
    {
      throw new ArgumentException("Viewmodel is not referenced by any registered View.");
    }

    // Get owner window
    Window owner = view as Window;
    if (owner == null)
    {
      owner = Window.GetWindow(view);
    }

    // Make sure owner window was found
    if (owner == null)
    {
      throw new InvalidOperationException("View is not contained within a Window.");
    }

    return owner;
  }

  /// <summary>
  /// Callback for late View register.
  /// It wasn't possible to do a instant register since the
  /// View wasn't at that point part of the logical nor visual tree.
  /// </summary>
  private void LateRegister(object sender, RoutedEventArgs e)
  {
    FrameworkElement view = sender as FrameworkElement;
    if (view != null)
    {
      // Unregister loaded event
      view.Loaded -= LateRegister;

      // Register the view
      Register(view);
    }
  }

  /// <summary>
  /// Handles owner window closed, View service should then unregister all Views acting
  /// within the closed window.
  /// </summary>
  private void OwnerClosed(object sender, EventArgs e)
  {
    Window owner = sender as Window;
    if (owner != null)
    {
      // Find Views acting within closed window
      IEnumerable<FrameworkElement> windowViews =
        from view in views
        where Window.GetWindow(view) == owner
        select view;

      // Unregister Views in window
      foreach (FrameworkElement view in windowViews.ToArray())
      {
        Unregister(view);
      }
    }
  }

  /// <summary>
  /// Gets the owning Window of a view.
  /// </summary>
  /// <param name="view">The view.</param>
  /// <returns>The owning Window if found; otherwise null.</returns>
  private Window GetOwner(FrameworkElement view)
  {
    return view as Window ?? Window.GetWindow(view);
  }
}

The design is pretty straightforward. A View is registering itself as part of the MVVM pattern by setting the attached property IsRegisteredView on any element in the logical tree.

<Window
  x:Class="MVVM_Dialogs.View.MainWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:service="clr-namespace:MVVM_Dialogs.Service"
  service:DialogService.IsRegisteredView="True">

The View is now remembered by the dialog service. When a ViewModel is in need to show some kind of dialog, the correct View is found by the service, and its owning Window is used as the owner.

Showing a Dialog from a ViewModel

There exist two overloads for using the ShowDialog method. The first one lets the developer specify the dialog type.

public class MainWindowViewModel : ViewModelBase
{
  ...

  private void ShowInformation(object o)
  {
    // Get the selected person ViewModel
    PersonViewModel selectedPerson = persons.Single(p => p.IsSelected);

    // Create the PersonDialog ViewModel
    PersonDialogViewModel personDialogViewModel = 
              new PersonDialogViewModel(selectedPerson.Person);

    // Showing the dialog, alternative 1.
    // Showing a specified dialog. This doesn't require any form of mapping using 
    // IWindowViewModelMappings.
    dialogService.ShowDialog<PersonDialog>(this, personDialogViewModel);
  }
}

This overload specifies that a PersonDialog should be displayed with this as the owner and personDialogViewModel as DataContext. However, some of you might find it unsuitable that a View is specified in a ViewModel. For you, there is a second overload.

public class MainWindowViewModel : ViewModelBase
{
  ...

  private void ShowInformation(object o)
  {
    // Get the selected person ViewModel
    PersonViewModel selectedPerson = persons.Single(p => p.IsSelected);

    // Create the PersonDialog ViewModel
    PersonDialogViewModel personDialogViewModel = 
                new PersonDialogViewModel(selectedPerson.Person);

    // Showing the dialog, alternative 2.
    // Showing a dialog without specifying the type.
    // This require some form of mapping using 
    // IWindowViewModelMappings.
    dialogService.ShowDialog(this, personDialogViewModel);
  }
}

/// <summary>
/// Class describing the Window-ViewModel mappings used by the dialog service.
/// </summary>
public class WindowViewModelMappings : IWindowViewModelMappings
{
  private IDictionary<Type, Type> mappings;

  /// <summary>
  /// Initializes a new instance of the <see cref="WindowViewModelMappings"/> class.
  /// </summary>
  public WindowViewModelMappings()
  {
    mappings = new Dictionary<Type, Type>
    {
      { typeof(PersonDialogViewModel), typeof(PersonDialog) }
    };
  }

  /// <summary>
  /// Gets the window type based on registered ViewModel type.
  /// </summary>
  /// <param name="viewModelType">The type of the ViewModel.</param>
  /// <returns>The window type based on registered ViewModel type.</returns>
  public Type GetWindowTypeFromViewModelType(Type viewModelType)
  {
    return mappings[viewModelType];
  }
}

This overload doesn't specify the dialog type; however, you are forced to register the Window-ViewModel mappings in a class implementing IWindowViewModelMappings.

What Now?

I would like to hear your thoughts about the idea. Perhaps you have a better solution? Don't be afraid, leave a comment...

History

  • 5 October 2010: Code update.
    • Updated source according to comments by d302241.
  • 4 April 2010: Code update.
    • Updated source according to comments by Michael Sync.
    • Converted to .NET 4.
  • 18 June 2009: Code update.
    • Code no longer throws exception in Designer mode.
    • Fixed wrong interface summary.
  • 2 June 2009: Code update.
    • Added the ShowOpenFileDialog method to IDialogService.
    • Implemented a service locator instead of keeping DialogService as a Singleton.
  • 27 May 2009: Article update.
    • Updated introduction after comments from William E. Kempf.
  • 25 May 2009: Initial version.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

disore
Software Developer
Sweden Sweden
Member
Got my first computer in the 90's and loved it even though it sounded like a coffeemaker.
 
Now getting paid for designing cool WPF applications, and drinks the coffee instead of listening to it being made.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionFindOwnerWindow function: owner view never registeredmemberMember 990347113 Mar '13 - 0:03 
Hi,
Thanks for the samples. I am quite new at this and I have been looking for something like this for quite some time.
 
I used your classes and I get the following exception every time:
 
Viewmodel is not referenced by any registered View.
 
in the FindOwnerWindow method of the DialogService class. it seems that the views member of the DialogService is not populated with my MainWindow.
 
When I debug, the code never goes through IsRegisteredViewPropertyChanged
 
Am I declaring some in the wrong way?
 
 public class MainWindowViewModel : ViewModelBase
    {
 
        private readonly IDialogService dialogService;
 

        public MainWindowViewModel()
        {
            this.dialogService = ServiceLocator.Resolve<IDialogService>();
            OpenDialogCommand = new RelayCommand(OpenDialog, CanOpen);
        }
        
        public ICommand OpenDialogCommand { get; private set; }
        
        private void OpenDialog(object o)
        {
            //MessageBox.Show("Here");
            NewDialogViewModel newDiag = new NewDialogViewModel();
            dialogService.ShowDialog(this, newDiag);       
        }
 
        private bool CanOpen(object o)
        {
            return true;
        }
 
    }
 
 
public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
 
            // Configure service locator
            ServiceLocator.RegisterSingleton<IDialogService, DialogService>();
            ServiceLocator.RegisterSingleton<IWindowViewModelMappings, WindowViewModelMappings>();
 
            // Create and show main window
            MainWindow test = new MainWindow();
            test.Title = "New";
            test.DataContext = new MainWindowViewModel();
            test.Show();
        }
    }
 
Thanks in advance.
AnswerRe: FindOwnerWindow function: owner view never registeredmemberdisore13 Mar '13 - 5:00 
I am guessing that you haven't set the attached property DialogService.IsRegisteredView in the XAML.
GeneralRe: FindOwnerWindow function: owner view never registeredmemberMember 990347113 Mar '13 - 22:32 
Yes! Indeed I was missing this.
 
Thanks again for the samples and help!
 
Cheers.
GeneralMy vote of 5memberSergey A Alekseev5 Mar '13 - 19:35 
Very useful source code!
GeneralRe: My vote of 5memberdisore5 Mar '13 - 19:48 
Thanks!
QuestionRequire your help on Modal DialogmemberMember 93903114 Mar '13 - 23:42 
I am developing a WPF application that follows MVVM pattern. To display modal dialogs, I am trying to follow the way you have suggested.
 
Situation in my application is slightly different.
I have an ApplicationViewModel class that acts as ViewModel for MainWindow.xaml.
This ApplicationViewModel contains a ViewModel, say VM1 which in turn contains
another ViewModel, say VM1Child;

MainWindow.xaml contains a user control say ChildView1 which is binded with VM1. ChildView1 contains another user control say ChildView11 which is binded with VM1Child.
VM1Child contains AddCommand, and I need to display Modal Dialog when AddExecute method corresponding to AddCommand is called. How can I accomplish that?
AnswerRe: Require your help on Modal Dialogmemberdisore5 Mar '13 - 11:33 
If you register ChildView11 in DialogService, i.e.
 
<UserControl
    x:Class="Test.ChildView11"
    ...    
    service:DialogService.IsRegisteredView="True">
 
</UserControl>
 
you should be fine.
GeneralRe: Require your help on Modal DialogmemberMember 93903115 Mar '13 - 19:31 
That change alone was not enough.
I had to modify FindOwnerWindow method in DialogService class.
 
private Window FindOwnerWindow(object viewModel)
   {
         FrameworkElement view = null;
              
         // Windows and UserControls are
         // registered as view.
         // So all the active windows and
         // userControls are contained in views
                  foreach (FrameworkElement viewIterator in views)
                  {
                        // Check whether the view
                        // is an Window
                        // If the view is an window
                        // and dataContext of the
                        // window, matches with the
                        // viewModel, then set
                        // view = viewIterator
                        Window viewWindow = viewIterator as Window;
                        if (null != viewWindow)
                        {
                              if (true == ReferenceEquals(viewWindow.DataContext, viewModel))
                              {
                                    view = viewWindow;
                                    break;
                              }
 
                        }
                        else
                        {
                              // Check whether the view
                              // is an UserControl
                              // If the view is an UserControl
                              // and Content of the userControl,
                              // matches
                              // with the viewModel, then set
                              // view = userControl
                              // In case the view is an user
                              // control, then find the
                              // Window that contains the
                              // user control and set it as owner
                              System.Windows.Controls.UserControl userControl = viewIterator as System.Windows.Controls.UserControl;
                              if (null != userControl)
                              {
                                    if (true == ReferenceEquals(userControl.Content, viewModel))
                                    {
                                          view = userControl;
                                          break;
                                    }
 
                              }
                        }
                  }
               if (view == null)
               {
                    throw new ArgumentException("Viewmodel is not referenced by any registered View.");
               }
 
               // Get owner window
               Window owner = view as Window;
               if (owner == null)
               {
                    owner = Window.GetWindow(view);
               }
 
               // Make sure owner window was found
               if (owner == null)
               {
                    throw new InvalidOperationException("View is not contained within a Window.");
               }
 
               return owner;
          }    
 
Please have a look at it.
GeneralRe: Require your help on Modal Dialogmemberdisore5 Mar '13 - 19:57 
I fail to see why the code works in your situation. Isn't it identical to mine, with the only change that you only allow Window and UserControl being registered, while I allow any FrameworkElement.
 
In that regard, your code is more restrictive than mine, which is the reason I don't understand why your code works and not mine. Could you please help me understand? Is your code open-source, i.e. can I look at it?
GeneralRe: Require your help on Modal DialogmemberMember 93903115 Mar '13 - 20:13 
I Think the problem lies in the following line of code.
 
FrameworkElement view = views.SingleOrDefault(v => ReferenceEquals(v.DataContext, viewModel))
 
In my application,in case of user controls, the corresponding viewModel is content of that user control, not the DataContext.
GeneralRe: Require your help on Modal Dialogmemberdisore5 Mar '13 - 20:21 
The lines after the one you posted should take care of that:
 
// Get owner window
Window owner = view as Window;
if (owner == null)
{
    owner = Window.GetWindow(view);
}
 
If view isn't a Window, Window.GetWindow(view) will get the window. These lines are all part of the source code, can you verify that you have them?
GeneralRe: Require your help on Modal DialogmemberMember 93903115 Mar '13 - 20:23 
I corrected my post. Please have a look at it. The reason is something else. I edited my post accordingly.
GeneralRe: Require your help on Modal Dialogmemberdisore5 Mar '13 - 20:27 
If your changes work go with them, otherwise provide me with a sample that can compile that has the issue.
GeneralRe: Require your help on Modal DialogmemberMember 93903115 Mar '13 - 20:59 
It is working. Thank you very much for your help.
GeneralMy vote of 1memberabdurahman ibn hattab24 Jan '13 - 10:03 
Here is a better solution to that so called "problem":
 
Window.ShowDialog()
 
Yay!
GeneralMy vote of 5memberChad Strawinski18 Jan '13 - 6:35 
This saved me a lot of time figuring out how to use MVVM with dialogs myself. I only had to make a few minor changes to make it work with my View and ViewModel in separate projects. Thank you!!
QuestionPretty interesting!memberCandyBeat13 Dec '12 - 3:58 
Congratulations!
 
I have been looking to overcome this problem in MVVM for months. The sad reality is that the most important personalities I know (that are really good developers), have always been silent about this issue. I even contacted with Laurent Bugnion, author of MVVM Light. He is busy and presented a very basic approach to the problem, that was in fact, limited to Yes, No, Cancel dialogs. Josh Smith left .NET. Wrote to Tomer Shamam, didn't reply, yet. No other trustyworthy person want to say a word on this.
 
Well, I'm stuck with no solution. Yours seems to be a blow of fresh air. Can you please tell me if you plan to update this? Please, add me to your Twitter account or tell me your username I'm @SuperJMN.
 
I have high hopes on you.
Thanks a lot.
AnswerRe: Pretty interesting!memberdisore13 Dec '12 - 11:08 
Hi CandyBeat.
 
The problem of opening dialogs or message boxes is usually solved in one of two ways: either by using a mediator (like Laurent Bugnion) or a service that handles the problem. I prefer the latter. The only problem with going that route is how do one connect a view and a view model before opening it as a dialog? My solution enforces the developer to either declare the view type, which to some MVVM developers is a sacrilege, or to have a mapper class describing the relationship between views and view models.
 
I have no plans to update the article in the near future. I would propose you to have a look at Sacha Barber's MVVM framework called Cinch[^]. It is a competent framework with a lot of downloads, which hopefully means that it will be maintained.
 
If you have any specific questions, feel free to ask.
GeneralRe: Pretty interesting!memberCandyBeat14 Dec '12 - 22:55 
Hi! I see your point and I think you're right. Connecting the View and the ViewModel should be made by a third component in the relationship.
 
I saw in your simple App that you are using the Service Locator pattern. Do you think you could build a sample using MEF? Would it be too complex? About the Cinch Framework, I think I will make the step, but I'm afraid of using a Framework that is somewhat unofficial or unsupported. Anyways, I will look into it.
 
Regarding the need of using Windows from the ViewModel this is my point of view:
You are in a Window (main) and the user chooses to edit some type of entity, for example, the user wants to Options/Preferences… Then in an abstract way of seeing this, the user want to temporarily change the context (the focus of his actions) and the unique reason for it is to modify data related with preferences. This is: the user wants to edit some Preferences ítem and when it's done, he will go back to where the actions started.
 
In fact, I see the workflow like calling an external program that modifies the required information and returns. This is valid only for Dialogs (Modal Windows). So the ViewModel may start up the action just with a method like:
 
var returnValue = DialogService.Edit(preferences);
 
The DialogService then should execute the necessary actions to edit that entity. Of course it would have some overloads so the developer would choose what kind of "Edit" he will be using (over the and a default one).
 
Please, tell me your opinion. And THANKS a lot for your comment!
GeneralRe: Pretty interesting!memberdisore17 Dec '12 - 8:21 
You can download the code using MEF from here. I think you need Visual Studio 2012 in order to open it.
 
Regarding your thoughts on how the dialog service should operate, here is my spin on it. I think the DialogService needs to be generic and ignorant of anything else than opening dialogs. This basically translates well into the single responsibility principle. That means that some other class needs to take responsibility of:
 
1. Opening the dialog using the dialog service
2. Wait until dialog is closed and investigate whether the user wishes to proceed with saving the changes
3. Save the changes
 
To me this sound like the kind of code I would place in a command, e.g. a command called PreferencesCommand that would be called from the application menu or whatever.
 
Does this sound reasonable?
GeneralMy vote of 5memberkarl-barkmann5 Dec '12 - 15:34 
good work!
GeneralRe: My vote of 5memberdisore5 Dec '12 - 19:55 
Thanks!
QuestionWhat Is The Service For???memberKevin Marois7 Nov '12 - 5:31 
I see no reason for a service.
 
See this[^]
If it's not broken, fix it until it is

AnswerRe: What Is The Service For???memberdisore7 Nov '12 - 10:18 
The core of the service is dialog management. A view model in my perspective should not hold a instance of a dialog and call its Window.ShowDialog, instead it lets the service open the dialog. This, among other things, makes writing unit tests easier.
 
I glanced at your code and nowhere do I see you opening a dialog from a view model, which is the whole purpose of this article. Could you elaborate on the validity of your comment, and how you propose to accomplish the task with your code?
GeneralRe: What Is The Service For???memberKevin Marois7 Nov '12 - 10:34 
disore wrote:
I glanced at your code and nowhere do I see you opening a dialog from a view model

 
and
 
disore wrote:
A view model in my perspective should not hold a instance of a dialog

 
You're saying two different things here.
 
A ViewModel should never open a View. That couples them together, which isn't separation of concerns. Use a factory instead.
If it's not broken, fix it until it is

GeneralRe: What Is The Service For???memberdisore7 Nov '12 - 11:45 
Let me make myself perfectly clear since you obviously are putting a lot of effort in trying to misunderstand me.
 
Fact 1: ViewModelA is data context to the view DialogA
Fact 2: In view DialogA there is a button bound to a command in ViewModelA
Fact 3: ViewModelB is data context to the view DialogB
 
Scenario: When the user clicks the button in DialogA, DialogB should be displayed
 
Please explain how you would do this in MVVM with referring to the code in your article.
GeneralRe: What Is The Service For???memberKevin Marois7 Nov '12 - 12:02 
disore wrote:
Let me make myself perfectly clear since you obviously are putting a lot of effort in trying to misunderstand me.

 
I'm not putting effort into anything. I just think your 'solution' is over-engineered. I'm entitled to my opinion, and based of the votes you're getting, I'm not alone. If you can't take constructive criticism, then you're not going to learn.
 
The click event should never create an instance of a view. That couples the ViewModel for View A to the View B. As I said, look up factory patterns. I have a code example at home. I'll try to remember to post it later tonight.
If it's not broken, fix it until it is

GeneralRe: What Is The Service For???memberdisore7 Nov '12 - 12:34 
Constructive criticism means you should provide me with at least one alternative solution solving the same problem in a more elegant/optimized/cleaner way, and still you haven't presented one yet.
 
I asked you for a solution based on three facts and one scenario, and you give me ramblings about click events? Read my previous post again. The button is bound to a command in the view model, no events anywhere. You use commands in your own article, surely you must understand the basics of them?
 
You are also saying that ViewModelA isn't suppose to create ViewModelB. I can live with that. Let the view models inherit interfaces and have a dependency injection framework create them for us. But indifferent of whether we use a DI-framework or the factory pattern, the problem of opening dialogs still exist.
 
This is a challenge: provide a solution for the three facts and one scenario. I'll see what you've come up with first thing tomorrow.
GeneralRe: What Is The Service For???memberKevin Marois7 Nov '12 - 13:03 
disore wrote:
Constructive criticism means you should provide me with at least one alternative solution solving the same problem in a more elegant/optimized/cleaner way, and still you haven't presented one yet.

 
I'v done that already twice. For the third time, look up factory patterns.
 
I'm not going to do your research for you.
 
We're done here.
If it's not broken, fix it until it is

GeneralRe: What Is The Service For???memberdisore7 Nov '12 - 19:32 
The factory pattern is a creational pattern, thus have nothing to do with opening dialog from a view model, which is what this article aims to provide a solution for.
 
With all due respect sir, I don't think you know what you are talking about. Please don't post any more messages until you know what you are talking about, they are clearly worthless.
GeneralRe: What Is The Service For???memberKevin Marois8 Nov '12 - 4:30 
I use a factory patter to open dialogs in my MVVM apps, so it certainly is not me who is clueless.
 
You're an arrogant ass and I'll have nothing more to do with you.
 
Vote 1
If it's not broken, fix it until it is

GeneralMy vote of 5memberAdminpass20104 Oct '12 - 5:42 
Thanks!
Questionquite disappointedmemberCodeLinguist4 Oct '12 - 3:47 
i'm quite disappointed sir.
i saw the sample project and i noticed that you need to make those many classes and interfaces for each viewmodel. Confused | :confused:
AnswerRe: quite disappointedmemberdisore4 Oct '12 - 3:57 
Can you please elaborate more on your thought regarding the code? Which are the 'many classes', and which specific interfaces do you have a problem with?
GeneralRe: quite disappointedmemberCodeLinguist10 Oct '12 - 21:05 
all i want is to avoid referencing view classes in the view model.
i see that you can do it by registering a view class to the view model.
but what i am disappointed with, is that i need to create Service Interface (i.e. IPersonService) for every Model Class and create a class(i.e. PersonService) that implements it.
GeneralRe: quite disappointedmemberdisore10 Oct '12 - 21:19 
CodeLinguist wrote:
i see that you can do it by registering a view class to the view model.

Yes, the implementation of IWindowViewModelMappings which in my case is WindowViewModelMappings contains the relationships between views and view models. But no view model interfaces are involved in this stage, i.e. MainWindowViewModel and PersonDialogViewModel doesn't have to inherit some special interface.
 
CodeLinguist wrote:
but what i am disappointed with, is that i need to create Service Interface (i.e. IPersonService) for every Model Class and create a class(i.e. PersonService) that implements it.

This has nothing to do with MVVM or the solution I am presenting when it comes to opening dialogs from a view model. You don't have to have a service with a Load method that returns the persons, how you instantiate your models is up to you. For all I know the models in your application can be provided by a RIA-service and sent over HTTPS to your client. I am not presenting a solution on how to load models, I am presenting a solution on how to open dialogs.
GeneralRe: quite disappointedmemberCodeLinguist10 Oct '12 - 21:56 
how about defining an IView interface, put a property of that type for every view model and set the view at application startup?
 
in this way i you can avoid mentioning(referencing) the view classes and
the view models can directly communicate to each other..
 
does this make sense?
 
interface IView
{
    object DataContext;
    void Show();
    bool? ShowDialog();
}
 
class PersonViewModel: ViewModelBase
{
    public static Type _View;
    public static Type View
    {
        get{return _View;}
        set{
            if (value.GetInterface("IView"))
                throw new ArgumentException("does not implement IView interface")
            _View=value;}
    }
 
    //invoked by other view model instaces
    public bool? ShowDialog()
    {
        var view = (IView)System.Activator.CreateInstance(View);
        view.DataContext = this;
        return view.ShowDialog();        
    }
}
 
class PersonDialog : Window, IView
{
    ...
}
 
class App
{
    protected override void OnStartup(StartupEventArgs e)
    {
        PersonViewModel.View = typeof(PersonDialog);
    }
}

GeneralRe: quite disappointedmemberdisore10 Oct '12 - 22:12 
This is a perfectly viable solution as well, but I would recommend having another class opening the dialogs, not the view models them self. Otherwise unit tests could hang because a dialog was opened.
GeneralRe: quite disappointedmemberCodeLinguist10 Oct '12 - 23:08 
i see. Smile | :)
problems in unit testing is one of the reasons why there is MVVM.
i dont know what unit testing is so i will just for the time being,
follow my approach to get my job done immediately (with separation of concerns).
 
thanks alot! i will consider your approach. Thumbs Up | :thumbsup:
GeneralRe: quite disappointedmemberscy27875351717 Oct '12 - 21:15 
Hi CodeLinguist,I am interesting in your above code, I am a new learner about MVVM, would you pls show me detailed info to showdialog ? thx.
I can do!

QuestionDoes this really solve the problem?memberMongalong16 Aug '12 - 15:17 
I don't see how this facilitates separation of concerns at all. I have an application in which the View and ViewModel layers are in completely separate projects. Having the View project dependent on the ViewModel project would be undesirable, albeit not the end of the world, but I'm sure we can all agree that having the ViewModel dependent on the View would totally break the MVVM pattern.
 
In the solution you've provided your WindowViewModelMappings needs to go in the View because it needs to know about the dialog windows (i.e. PersonDialog). DialogService uses WindowViewModelMappings directly so it too must go in the View. But DialogService is used by MainWindowViewModel in order to invoke the dialog window, and since it can't be moved out of ViewModel you wind up with the ViewModel needing the reference the View solely to use the dialog service. The only reason your example project compiles at all is by virtue of the fact that the two layers appear in the same project. True separation of concerns hasn't really been achieved because the architecture breaks once the two layers are separated.
 
Please correct me if I'm wrong or have missed something obvious.
AnswerRe: Does this really solve the problem?memberdisore16 Aug '12 - 20:23 
Hi Mongalong.
 
Lets start with the issue of two projects. I usually place views and view models in the same project. Why? Because even though they aren't referencing each other, changes in the view most often means changes in the view model. For convenience, if the two classes are located on the same place it means less browsing in the solution explorer for me. Does this mean I am breaking separation of concern? No. Most MVVM samples are having views and view models in different projects, but I fail to see the real benefit of that structure. You could perhaps enlighten me?
 
Lets continue with the remarks about references between projects. Here is a project structure which I think satisfies your needs:
 
ViewProject - Your project with views. This should only reference MvvmProject.
ViewModelProject - Your project with view models. This should only reference MvvmProject.
MvvmProject - This project should contain all MVVM service classes and their implementation, and also the interfaces they are referring to in my code sample, e.g. IWindowViewModelMappings. It should be considered as the project where classes helping you develop efficient MVVM code is located.
ApplicationProject - This is the final project, and should act as the entry point of the application. It is referencing all other projects and is responsible for setting up the service locator. This means that the implementation of WindowViewModelMappings is located here, since this project knows of both views and view models.
 
This architecture doesn't break separation of concern since the views are unaware of view models, and view models are unaware of views.
 
Did I make any sense?
GeneralRe: Does this really solve the problem?memberMongalong20 Sep '12 - 16:23 
The project structure you propose is indeed what we use ourselves but I haven't yet been able to shoehorn your solution into that structure. I'll keep trying though because in principle I do like many aspects of your implementation.
 
With respect to separating ViewModels and Views into different projects, I found this became an issue during unit testing. Like a lot of other projects we have a DLL project for unit testing that NUnit can load and run as well as a console application that loads that same DLL, fires off the unit tests and performs various reports. The tests themselves consist of standard domain level tests (CRUD etc) but we also do full integration testing in which the unit tests effectively run the application (i.e. the ViewModels) and simulate user input by firing off the various ICommands etc that GUI elements are normally responsible for. For example, a typical test might be for the user to select an option from the menu and for the correct page (i.e. SomeChildPageViewModel) to be added to the MainWindowViewModel's collection of open pages. The only way you can do this is if you can run the ViewModel layer standalone.
 
With respect to dependencies, my experience has been that the View project has to be dependent on the ViewModel project, there's not really any way around that in a real application. At the very least the View is dependent upon the ViewModel in an abstract conceptual sense since it has to adhere to the object interfaces in order for data binding to work, but it also needs to be dependent upon it in practice as well. Many of the data types that your Views will pass to the ViewModels will be things like typed enums etc that are declared in, and ultimately used by, the ViewModel objects, so that reference has to be there in order to refer to them in the XAML.
GeneralRe: Does this really solve the problem?memberdisore20 Sep '12 - 19:49 
I'm sorry but have only minor experience in full integration testing, I usually only unit test and leave the UI testing for the Quality Assurance team. Although, I would be interested in what your experiences are with integration tests. Are they hard to write? Are they prone to be updated due to changes in UI? Etc...
 
I agree with you that there is a dependency between views and view models in the respect of bindings. And I find it perfectly acceptable for views to know about their view models. When writing XAML I always define d:DataContext="{d:DesignInstance local:SomeViewModel}" to get validation that the bindings are correct, and this is only possible if the view have a reference to the view model.
 
Good luck, I really hope you find a solution to your problems.
QuestionCode Contracts Nunit and MoqmemberDirkster997 Aug '12 - 23:27 
Hi Disore,
 
I've seen that you use code contracts in your code and have setup an NUnit and Moq project. I am a Visual Studio 2010 Express user and was wondering if the code contracts can be tested with Nunit or Moq (independently of VS Express)? Do you have any experience with that?
 
Thanks for the great article
 
Dirkster99
AnswerRe: Code Contracts Nunit and Moqmemberdisore8 Aug '12 - 0:21 
Hi.
 
No, sorry, I have no experience of that. I only used code contracts for a short period of time, since I think they are too restricted. It was a good idea, but I am finding myself perfectly ok without them.
NewsRe: Code Contracts Nunit and MoqmemberDirkster999 Aug '12 - 11:32 
Hi Disore,
 
Thanks for the info. I cam across a similar problem as you try to solve in your article - I have also written an article
 
<a href="http://www.codeproject.com/Articles/413517/Closing-Windows-and-Applications-with-WPF-and-MVVM">Closing Windows and Applications with WPF and MVVM</a>[<a href="http://www.codeproject.com/Articles/413517/Closing-Windows-and-Applications-with-WPF-and-MVVM" target="_blank" title="New Window">^</a>]
 
to see if I can get a simple reference application that could be MVVM conform in WPF (I did not know your article then). My first version was not received too well. So, I researched service patterns in more detail and found your article and incorporated the DialogService into a second code sample in my article.
 
Please have a look and let me know if there is something crucial missing or wrong. Thanks again for the service sample here - I find your article quit useful indeed Smile | :)
QuestionPretty elegant, but complicatedmemberPhilB101020 Apr '12 - 3:57 
This demonstrates an effective way to separate concerns, but it seems to me a bit overly complicated just to invoke a dialog.
 
In the framework we use (built in-house), if a view wants to show another view such as a dialog, it invokes an ICommand on its viewmodel, which can then do any preliminary work as needed (e.g., run stuff through domain objects), and raise an event for the view to handle (event data contains a new viewmodel needed by the new view), at which point the view creates and opens the new view. The view does indeed know its viewmodel (but not vice versa), which I find completely acceptable, and we have base classes for views and viewmodels that provide common behavior. There's some code-behind in the base view class to wire this up, but I also find that completely acceptable, so long as concrete views have very little or none.
 
Again, though, your solution is pretty elegant and does keep things decoupled.
AnswerRe: Pretty elegant, but complicatedmemberdisore20 Apr '12 - 8:21 
Hi Phil.
 
I've seen view driven examples to open dialogs and message boxes, and have nothing against them. In your example the view runs a command to indirectly open the dialog, but I assume the view model could run that command as well based on its own decisions, i.e. the operation of opening a new dialog doesn't have to be started by the view, it could be started by the view model as well?
 
I am unsure whether you can talk about your solution since it in-house (I developed this code on a dare by a co-worker that some kind of registration between view and view model had to be made before opening dialogs could be accomplished, and we ended up using this code at work after I won the dare) but how does the view model know when the new dialog has been closed? A event is sent when the dialog should be opened, is another command executed by the view when the new dialog closes?
 
As a final remark, I implemented this solution with one requirement in mind: opening dialogs should be accomplished with minimal code in view and view model, i.e. the footprint of the dialog framework should be kept at a minimal. Setting one attached property in the view, and call one method on a already implemented interface is pretty simple according to be. No boiler plate code, nothing strange. Just a synchronous call to a service and then investigate the result, pretty simple don't you think?
QuestionBeing able to Edit data in a dialog and Save...memberMarcelo Areal22 Nov '11 - 8:41 
Like what you have done. I have been following what you have done and adapting it so it fits my project. How can we go about making the popup dialog Edit data; in other words, when we select a Person from the list and click on "Show Information", ideally we can change data and click Save.
 
So sending the modified data to the PersonViewModel is what I am after...
 
Thanks in advance...

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130516.1 | Last Updated 5 Oct 2010
Article Copyright 2009 by disore
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid