0

I have a VERY SIMPLE application which is supposed to read a file selected by the user and display it on the screen. For that I created two views, one with a button for the user to select the file and another view for displaying the contents of the file.

I also have two ViewModels one for each of the views, now, when the user clicks the button to select a file the code in the ViewModel will use OpenFileDialog and open the file, my question is should I call OpenFileDialog from the viewModel or from the Model for a MVVM project?

sanya
  • 111
  • 11
  • Related: https://stackoverflow.com/questions/14297312/wpf-messagebox-with-mvvm-pattern – Serg Apr 09 '22 at 22:57
  • Do whatever makes sense for your application. Like all architectures, Mvvm has it's limits and slavishly using it even when the alternative is easy to maintain and read isn't worth it. As long as you can test it, do what you want. – John V Apr 10 '22 at 06:51
  • 1
    @Serg Such a service can never solve the MVVM violation. You don't want your view model care about any view related issues. It's not the responsibility of the view model to communicate with the user. Messages must be generated on view level only. – BionicCode Apr 10 '22 at 10:02
  • @BionicCode wouldn't that mean writing code in the view.cs file? Which I thought was onnly supposed to contain InitializeComponent();. I thought you should have no code behing in the view – sanya Apr 10 '22 at 12:30
  • 1
    Yes, you will find yourself writing code-behind. And that's fine. MVVM is not about code-behind. It's a good practice to avoid code-behind. because most of the UI related code can be implemented using XAML. But XAML has its limitations. For example, you can't invoke methods from XAML (in WPF). When you write a control like a ListBox, then all the complex logic is written in C#. – BionicCode Apr 10 '22 at 12:36
  • 1
    MVVM is a design pattern which is independent from languages or compiler constructs like `partial class` (which basically enabled code-behind). Data binding or commands have nothing to do with MVVM. A Button.Click handler does not violate MVVM. Please read this post [Open File Dialog MVVM](https://stackoverflow.com/a/64861760/3141792) to get an idea. – BionicCode Apr 10 '22 at 12:36
  • @BionicCode wow, you blew my mind with that answer. I'll stop using services for this kind of thing from now on. Thank you! – sanya Apr 10 '22 at 13:00
  • 1
    You can always do what ever you want. Just don't do it based on misconceptions. If you know the sacrifice or trade-off and you can accept them, then go ahead. In my opinion, if there is a perfect solution to your problem, then you should not accept trade-offs. In software development, reverting a decision you have made at the beginning of your project is almost impossible when the project progresses. I guess this is true for any kind of project - and sometimes even life. This means we shouldn't be too generous with trade-offs in general. – BionicCode Apr 10 '22 at 13:16

1 Answers1

1

Dialog boxes don't fit into the MVVM paradigm very well, due to the tight coupling they have with the OS. As a general rule though, anything you want directly unit-tested belongs in the view model, while anything that creates Windows GUI objects at runtime belongs in your view layer. With that in mind, the view is the appropriate layer for calling OpenFileDialog. You may find that you still need to break the clean MVVM architecture to do this, so abstracting it away into a service that can be injected will at least keep it away from the rest of your code and maintain good seperation of concerns.

If you really want to do this properly then you have to implement some boiler-plate code similar to what the WPF team wrote for "regular" windows. I wrote a long article about it here, along with a library for easily adding dialog box functionality to your own MVVM projects:

https://www.codeproject.com/Articles/820324/Implementing-Dialog-Boxes-in-MVVM

Mark Feldman
  • 15,731
  • 3
  • 31
  • 58
  • 1
    i'm kind of annoyed though about how much MVVM compliant code you have to write simply to implement what was a single line OpenFileDialog – sanya Apr 10 '22 at 00:34
  • I have to say though, despite being a hard-core MVVM advocate, I do agree with what @John-v says. Every time you add layers of abstraction to an application's architecture you are trading something (simplicity, ease of use) for something else (stability, unit-testability, maintenance). If your application really is "super simple", as you say, then there's probably little to be gained from using MVVM. – Mark Feldman Apr 10 '22 at 07:21
  • 1
    @sanya The correct MVVM solution would be to let the view observe the view model and then generate the messages. Don't use a service too. It doesn't change anything in terms of MVVM violation. The message service, if you implement one, should be a pure view module and an observer of the view model. The view model should never care about UI messages. Such logic does not belong in the view model. – BionicCode Apr 10 '22 at 09:55
  • 1
    @sanya MVVM is more than just avoiding UI controls in your view model. It's primarily about responsibilities. And it's not the responsibility of the view model to communicate with the user. Let the view model publish events that the view can convert into messages. If you can't manage to separate the view concerns from your view model follow the approach suggested by Mark. But your first thoughts should be about cleaning up your class or application design to move responsibilities to the proper modules or application domains. It's really not hat difficult. – BionicCode Apr 10 '22 at 09:56
  • @BionicCode when you said "should be a pure view module and an observer of the view model" that means writing the viewmodel to implement INotifyPropertyChanged right? Like: `public class BaseViewModel : INotifyPropertyChanged` – sanya Apr 28 '22 at 23:50
  • 1
    @sanya Not `INotifyPropertyChanged` in particular. It can be any event. For example, if the view model encounters a problem like missing user rights, it should not actively show a login dialog or ask for it. The flow must be implemented in a way that the view model can continue to operate without depending/waiting for the missing information (authentication in this case): instead the view model should raise a e.g. `AuthenticationFailed` event and then abort the related routine. – BionicCode Apr 29 '22 at 08:43
  • 1
    @sanya The view can now handle this event e.g. by asking the user to authenticate. If the authentication has completed successfully, the view can kick-off the previously aborted routine of the view model once again. You can check Solution #3 of [this link](https://stackoverflow.com/a/64861760/3141792) for a very general example that should hopefully explain the idea. It uses the Event Aggregator. You can find an aggregator implementation in the Microsoft Prism library (or by searching with the NuGet Manager). – BionicCode Apr 29 '22 at 08:43
  • 1
    @sanya You don't have to use an event aggregator. You can subscribe to the relevant view model events explicitly. But an event aggregator helps to decouple this relationship, so that you can listen to events without having to know or need of a reference to the source instance (in order to register the event handler). This way you can add more view models to your application at any time without having to modify your view just to listen to the new event sources. – BionicCode Apr 29 '22 at 09:15