0

How to call MessageBox.Show() based on the result of ViewModel method?

ViewModel

Public Class PersonViewModel
    Inherits ViewModels.ViewModelBase 'basic INotifyPropertyChanged staff

    Private _Model As Person
    Public Property FirstName As String
    Public Property LastName As String

    'BasicSub is class implemented ICommand taking Execute method as parameter
    Public Property SaveCommand As New Commands.BasicSub(AddressOf Me.SaveModel)

    Private Sub SaveModel()
        If _Model.Save() = False Then
            'Here inform View about saving wasn't successful
        End If
    End Sub
End Class

View

Public Class PersonView 
    Inherits Form

    'I use BindingSource for bindings
    Private BindingSourceViewModel As BindingSource

    Public Sub New(viewmodel As Object)
        Me.InitializeComponents()

        Me.BindingSourceViewModel.DataSource = viewmodel
    End Public

End Class

View have a button which Click event bounded to the Command property

I understand MVVM as simple separating of concerns.
View (In Winforms this is Form) have only own logic. Doesn't matter designer code or codebehind.
ViewModel know about Model but didn't know about View.

Now I am little bid stack about how MessageBox can be called based on the result of Save Command/Method while keeping View and ViewModel separeted?
Because MessageBox.Show is obviously a part of the View

At this moment i use workaround which on my opinion breaking MVVM pattern.
MessageBox.Show executed from ViewModel inside SaveModel() method if _Model.Save returns false

I have checked answer WPF MessageBox with MVVM pattern?, but at this moment wasn't influenced by using some intermediate types. I trying to keep Views and ViewModels in the different project, and Views haven't any references to other application libraries excepts resources

@HighCore, I know difference between Winforms and WPF :)

Community
  • 1
  • 1
Fabio
  • 31,528
  • 4
  • 33
  • 72
  • I don't know if this is the best way but I handled this by sending a Message from the ViewModel and register a handler in the View. Usually you'll have more housekeeping to do than just show the MessageBox so registering handlers listening for those error messages sent from the ViewModel is quite handy imho ... but I do not claim to be a MVVM expert. – Filburt Oct 25 '15 at 16:44
  • 1
    As described by Filburt, you should use the messenger pattern, i wrote an article explaining this pattern and other mvvm topics: https://blog.rsuter.com/recommendations-best-practices-implementing-mvvm-xaml-net-applications/ – Rico Suter Oct 25 '15 at 20:26
  • Just in case you don't already use it: [MVVM FX / Caliburn.Micro](http://mvvmfx.codeplex.com/) seems to offer a nice mvvm framework for Windows Forms. – Filburt Oct 25 '15 at 20:34
  • @Filburt, eventhandler approach, if I understand right, mean that View need to know about ViewModel type for subscribing to the event. In my case View takes ViewModel as parameter of type `Object` in the constructor. – Fabio Oct 25 '15 at 22:24
  • @Fabio No, the View just needs to know about the Message Type and, if needed, about (Model) types contained inside the message. Message Types can/should be located in a dedicated project and referenced just as your Model types are. And, strictly speaking, you are declaring *Message Handlers* which are different from *Commands* used for button click events and such. – Filburt Oct 26 '15 at 11:46
  • I cover this extensively in [an article I wrote on code project](http://www.codeproject.com/Articles/820324/Implementing-Dialog-Boxes-in-MVVM), it's a commonly asked question on SO. It can be done, you basically have to use a custom behaviour and an intermediate "presenter" class to decouple the inherent "code-behind" present in the system dialog box. – Mark Feldman Oct 27 '15 at 10:13

1 Answers1

3

As a visual element, any message box is in fact, part of a View. Thus, if you show your message box directly from a ViewModel (define a command that calls the MessageBox.Show() method), this simple code will break the main MVVM concept - ViewModels must not refer to Views - and make it impossible to write unit-tests for your ViewModel. To get around this difficulty, the you can use services concepts. A service is a kind of IOC concept that removes any references between a ViewModel and View layers. In code, a service is an interface used within the ViewModel code without any assumption of "when" and "how" this interface is implemented:

Public Class PersonViewModel
    Protected ReadOnly Property MessageBoxService() As IMessageBoxService
        Get 
            Return Me.GetService(Of IMessageBoxService)()
        End Get 
    End Property
    Public Sub Save()
        If Not _Model.Save() Then
            MessageBoxService.Show("Something wrong!")            
        End If
    End Sub 
End Class

You can provide you View Model with IMessageBoxService implementation using DI/IoC.

P.S. If you're using DevExpress WinForms controls you can extend your MVVM app with cool things like POCO-ViewModels, bindings&commanding, services, messenger and etc. - all the fully adapted for WinForms environment.

DmitryG
  • 17,677
  • 1
  • 30
  • 53
  • Does `service` concept mean that I need to implement `IMessageBoxService` interface for every type of View? For example for WPF, for Winforms and for testing – Fabio Oct 25 '15 at 22:09
  • 1
    @Fabio Exactly. You can also provide a different implementations of the concrete service depending on your app UI-type (For example you can use standard, skinned of flyout message box). And then just register the specific implementation of this service in your DI/IoC container for changing the messaging-UI across the entire application. Without any changes in ViewModels/Views code. – DmitryG Oct 26 '15 at 06:43
  • View and ViewModels are in the different projects in my case, so I did put Interface in the ViewModel project and implementation of the interface is on the View or in the Test projects, thanks – Fabio Nov 07 '15 at 07:38