none
MVVM newbie question: simple modal dialogs

    Question

  • Hi group, I'm just starting with MVVM, using as a starting point the tutorials in MS MVVM Toolkit, and I have a trivial question to start with: say I have a file-open command of some sort: say I have a view with a button which should open the standard file-open dialog to let user choose the file; my viewmodel class in turn has an OpenFile command receving the file name as a parameter.
    So the question is: where should I place the code for the OpenFileDialog? I cannot place it in the viewmodel, which requires to be tested without user interventions, so I must place it in the view; but this means that I have to handle the button click event and add some code behind which first opens the dialog and then invokes the command, rather than simply binding the button to the command in XAML. Of course every MVVM approach must face some practical compromises, I'd just like to know if there is any suggested way of handling these trivial cases.
    Thanks!
    Thursday, August 6, 2009 9:55 AM

Answers

  • Hi Naftis,

    Could you have an additional class which implements the dialog behaviour (inversion of control if you like) contained by the existing ViewModel which is currently opening your dialog? During your unit tests you could then pass a different implementation of this class (with a common interface or base class) which doesn't rely on user interaction but just passes back the desired value.

    I hope this makes sense (it's quite early in the morning, I don't think I've quite woken up yet!)

    Gareth


    • Marked as answer by Bruce.Zhou Friday, August 14, 2009 8:08 AM
    Tuesday, August 11, 2009 7:26 AM
  • Some people do this by introducing the concept of a "View service", which is an interface that the VM holds and can call into to display messageboxes or dialogs; the interface can be stubbed/mocked for testing. Others feel that this violates the spirit of MVVM because the VM does have a reference to a view-like object (not its actual view, mind you, but something that can affect the view).

    I don't see anything wrong with the interface approach. However, there's a lot of work being done on using the Mediator pattern to make this more pure (Google for MVVM Mediator). The VM can have a reference to a mediator object, optionally using a form of dependency injection, and use that to send messages. To me, this doesn't seem much different because logically there's still a concept of a "View service" - it's just addressed using messages instead of an interface.

             -Steve
    Programming blog: http://nitoprograms.blogspot.com/
      Including my TCP/IP .NET Sockets FAQ
    MSBuild user? Try out the DynamicExecute task in the MSBuild Extension Pack source; it's currently in Beta so get your comments in!
    I will be at the Grand Rapids BarCamp 4 unconference - come on by, fellow Michiganders!
    • Marked as answer by Bruce.Zhou Friday, August 14, 2009 8:08 AM
    Tuesday, August 11, 2009 12:48 PM

All replies

  • Hi Naftis,

    At present, I have no ideas with the suggested way of this scenario. But as in my view, you can add the code which needs user interaction at last. When testing the OpenFile command method, we can pass the FileName to the method from test method. And after we assure the logic has no problems, we can then add the interaction code.

    As an alternative, as you said, we can handle the button click event and add the code to open a file dialog. After that, we can pass the opened filename to the command through CommandParameter. In case you need, I just wrote the following code for your reference.

    Xaml code:

                    <MenuItem Command="{Binding ReadmeCommand}" Header="Readme" Click="MenuItem_Click">
                        <MenuItem.CommandParameter>
                            <Binding Source="{StaticResource data}" Path="FileName" Mode="TwoWay"></Binding>
                        </MenuItem.CommandParameter>
                    </MenuItem>

    C# code:

        public partial class MainView : Window
        {
    
    
            CommandData data;
            public MainView()
            {
                InitializeComponent();
                data = this.FindResource("data") as CommandData;
     
            }
    
            private void MenuItem_Click(object sender, RoutedEventArgs e)
            {
                OpenFileDialog dialog = new OpenFileDialog();
                if (dialog.ShowDialog() == true)
                {
                    data.FileName = dialog.FileName;
                }
                    
            }
        }
        public class CommandData:INotifyPropertyChanged 
        {
            private string fileName;
    
            public string FileName
            {
                get
                {
                    return fileName;
                }
                set
                {
    
                    fileName = value;
                    if (null != this.PropertyChanged)
                    {
                        PropertyChanged(this, new PropertyChangedEventArgs("FileName"));
                    }
                }
            }
    
            #region INotifyPropertyChanged Members
            public event PropertyChangedEventHandler PropertyChanged;
            #endregion
     
        }

    If you have any problems with this, please feel free to let me know.


    Best regards,
    Bruce Zhou


    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    Monday, August 10, 2009 11:14 AM
  • Thank you very much for the reply!
    Anyway, it seems there is no simple solution for such trivial tasks: breaking rules using code behind, or some "spaghetti" code (somewhat like setting a hidden field in a HTML form to be later used by code). No sample of MVVM applications I've looked at until now deal with things like message boxes and the like, yet it's a rather trivial scenario.

    Another one is exception handling: typically in my traditional WPF apps I catch some exception in code behind and display some message box; here instead I cannot put them in the viewmodel (which must remain testable without interactivity; and also, there is -as expected- no way the VM can know about the window consuming it); so I'm left with either (a) handle exception in code behind, with the same problems already seen (I must handle the click event and wrap the code in a try...catch block) or (b) use a generic unhandled exception handler at the application level, which is not appealing in many cases.

    Anyway, I'll try going further moving from MVVM toolkit to the much more mature (and complex :) Prism and see if there I can find patterns for handling such scenarios. Probably it's just that I'm still thinking in an uncorrect way when looking at these models...

    Thank you again
    Monday, August 10, 2009 3:53 PM
  • Hi Naftis,

    The problem comes to you due to the requirement that test can't be blocked by user interaction. As before, I think such kind of code can be added at last. For example, exception handling, we can log the exception into a file instead of displaying a messagebox in the test phase, and after all test passed, we can replace the log code with message box popup code.


    Best regards,
    Bruce Zhou


    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    Tuesday, August 11, 2009 2:29 AM
  • Hi Naftis,

    Could you have an additional class which implements the dialog behaviour (inversion of control if you like) contained by the existing ViewModel which is currently opening your dialog? During your unit tests you could then pass a different implementation of this class (with a common interface or base class) which doesn't rely on user interaction but just passes back the desired value.

    I hope this makes sense (it's quite early in the morning, I don't think I've quite woken up yet!)

    Gareth


    • Marked as answer by Bruce.Zhou Friday, August 14, 2009 8:08 AM
    Tuesday, August 11, 2009 7:26 AM
  • Thanks guys! This gives me different options, even if all require some "trick", but it's useful to know that I'm not missing something capital in my learning of this pattern. At any rate, as for your suggestions, such things (dialogs or exception messages) would be placed in the VM (either as conditional code or as Gareth suggests), not in the view, and this would be fine for allowing us to directly link user interface controls to VM commands without "polluting" the view with code behind.
    Anyway, displaying e.g. a message box or a dialog from the VM seems to me (but maybe I'm just misunderstanding) somewhat a pattern-breaker, as I was inclined to think the VM as a totally view-agnostic, UI-less presenter layer; of course, the alternative is breaking the direct V-VM connection via binding and add some code behind in the view, but I don't know which would be "best" (or maybe it's just a matter of taste). Apart from this generic issue, I might find problems in using UI pieces in the VM: say for instance I must center a dialog in the owner window: in code behind I just set the Window's Owner property = this (or use a GetWindow(this) if it's a user control), but of course in a VM there is no reference to the view and thus no notion of the owner window. This would be another reason to add some code behind in the view, rather than coding in the VM. Anyway, thanks to all for the discussion, this allows newbies like me to have some more understanding of this pattern!

    Tuesday, August 11, 2009 7:55 AM
  • Some people do this by introducing the concept of a "View service", which is an interface that the VM holds and can call into to display messageboxes or dialogs; the interface can be stubbed/mocked for testing. Others feel that this violates the spirit of MVVM because the VM does have a reference to a view-like object (not its actual view, mind you, but something that can affect the view).

    I don't see anything wrong with the interface approach. However, there's a lot of work being done on using the Mediator pattern to make this more pure (Google for MVVM Mediator). The VM can have a reference to a mediator object, optionally using a form of dependency injection, and use that to send messages. To me, this doesn't seem much different because logically there's still a concept of a "View service" - it's just addressed using messages instead of an interface.

             -Steve
    Programming blog: http://nitoprograms.blogspot.com/
      Including my TCP/IP .NET Sockets FAQ
    MSBuild user? Try out the DynamicExecute task in the MSBuild Extension Pack source; it's currently in Beta so get your comments in!
    I will be at the Grand Rapids BarCamp 4 unconference - come on by, fellow Michiganders!
    • Marked as answer by Bruce.Zhou Friday, August 14, 2009 8:08 AM
    Tuesday, August 11, 2009 12:48 PM
  • Thanks Stephen, I'll definitely have to look further into your suggestions, the mediator pattern and its current implementations; as for these, there are even too many implementations and frameworks floating around, but I'd like to stick with the most mature and MS-supported as in my projects I must avoid 3rd party solutions. So after a start with MVVM Toolkit, which is probably too immature to be used, I was thinking of learning Prism, which seems to have a solid foundation even if it's documentation looks a little intimidating. Probably I'll have to eat the elephant one bit at a time... Do you guys know any useful tutorial or resource about this framework?
    Wednesday, August 12, 2009 7:53 AM
  • Hi,
    In codeplex where you download Prism you have good tutorials. Check the link:

    http://www.codeplex.com/CompositeWPF

    Regards,
    Federico Benitez
    My blog
    Wednesday, August 12, 2009 8:54 AM
  • We are using the solution Gareth and Stephen mentioned for a while (Separate services for Open-, SaveFileDialog and MessageBox). We have different implementations of this services for application and unit testing (mocks). It works fine for us. We are using it also withWindows.Forms (MVP).

    As you sayed there are several implementations addressing your problem, take one that fits and go on.

    You should not fear to break MVVM, because each one is taking a different view on this pattern. MVVM will evolve over time and we will see how this things should be done the right (best) way in the future.

    And YES, we have codebehind we using TypeConverters and sometimes we bind the Model directly to the View. MVVM is not the Holy Grail, its only a pattern.
    Wednesday, August 12, 2009 9:06 AM
  • Just my 2 cents worth, but I can't help but be skeptical about all this MVVM ideology. Is tying your hands and jumping through hoops to do the simplest thing going to produce more sophisticated, intuitive software? I guess time will tell.

    cz
    Wednesday, August 12, 2009 11:57 PM
  • I was also skeptical at first. MVVM still has a few rough edges, like multiselection, and nicely sidesteps serious concerns like background processing and transaction logic by pushing them to the business layer.

    However, MVVM more than makes up for it with its testability, IMO. That is the single most important advantage, so much so that I'm pushing my company to adopt MVVM on a mobile device (which doesn't support WPF, so we would end up with Windows forms connected to a ViewModel via code-behind). Other people stress the designer/developer separation that MVVM achieves, but I just haven't seen the reality of that one in my projects (yet).

    I view MVVM as a set of good guidelines, not a strict set of rules. That's why I have yet to use a dependency injection framework - it just seems like overkill to introduce all that complexity just to hook up a few classes. So I end up with a dozen lines of initialization code for my MVVM projects, and to be honest it doesn't really bother me that much. :)

    Back to the original point: I use MVVM because of its testability. Moving the view logic to the ViewModel enables unit testing (and regression testing!) of the view without having to use "program runner" programs. If you already have a decent business layer (and you should), then supporting MVVM is not much more than introducing ObservableCollection and INotifyPropertyChanged to the business layer and pulling all the code behind into a data-oriented ViewModel.

           -Steve
    Programming blog: http://nitoprograms.blogspot.com/
      Including my TCP/IP .NET Sockets FAQ
    MSBuild user? Try out the DynamicExecute task in the MSBuild Extension Pack source; it's currently in Beta so get your comments in!
    I will be at the Grand Rapids BarCamp 4 unconference - come on by, fellow Michiganders!
    Thursday, August 13, 2009 4:05 AM
  • In the ViewModel layer, through the following line code, you should be able to find the Main window

    System.Windows.Application.Current.MainWindow

    -Jane
    Wednesday, September 23, 2009 1:18 AM
  • Hi, I have the same task and would like to double check with you: So, I could have some interface like:

     

    interface IModalViewService
    {
        void Activate(ModalViewModel viewModel);
    }

     

    and I could have 2 implementations of this interface - the one is to be used in normal application operation and another one is in unit testing (which will not show any UI).

    The normal application operation implementation could probably look like this:

    class ApplicationModalViewService : IModalViewService
    {
        public void Activate(ModalViewModel viewModel)
        {
            ModalView view = new ModalView();
            viewModel.Close += delegate
            {
                view.Close();
            };
            view.Content = viewModel;
            view.ShowDialog();
        }
    }
    The ModalView, in the code above, is just a window which is supposed to be a modal container for any View:

    <Window x:Class="ModalView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" ShowInTaskbar="False"> </Window>
    Then, the constructor of the view model, which will initiate appearance of the modal dialolg, (let's call it parent view model) should get reference to the IModalViewService implementation (in application case it will be an instance of ApplicationModalViewService) and then, in one of the CommandExecuted methods of the parent view model I could show my dialog view with help of the following call:

    modalViewService.Activate(viewModelOfModalView);
    This approach assumes that there will be an application resources level DataTemplate for each ViewModel (which is passed to the method Activate), but I am actually not sure that it is good idea, as an option I could pass type of the view to the method Activate as well, but then it will look like coupling viewmodel and even different view (which is not supposed to work with this viewmodel). Could you please tell me you thoughts about this specific concern and about entire, outlined in this post, possible implementation above?

    So, the call to Activate method will not return until the dialog closes, and, as I understand (please correct me if I am wrong), I should continue processing the flow in my parent view model once the method returns, at this point I have modified data in the viewModelOfModalView instance, which now can be used in the parent view model.

    P. S.
    Trying to apply this.
    Thursday, October 8, 2009 6:42 PM
  • It looks like this works, I have placed DataTemplates into the resources section of my modal window. But since this window is supposed to be used as a container for all the modal views of the application, I don't very like the fact that I put DataTemplates for all the "modal" viewmodels in that window. How do you guys do this in your applications?
    Friday, October 9, 2009 7:19 PM
  • Hi,

    I think no-one has mentioned events so far. If you don't want to break the MVVM pattern, you could have the ViewModel have define events that it raises when it wants to show a messagebox or a common dialog. One could group these events into VM-related interfaces, so that one could use generalized views and test classes to connect to them.

    Basically this is the same as passing in ViewServices, but since the interfaces which are getting called are defined by the ViewModel layer and not the View layer, the coupling goes into the other, "right" direction.
    Friday, October 30, 2009 11:57 AM