1

Is anything really wrong with a ViewModel opening additonal dialogues? Lets say I have a MainView and a MainViewModel. The MainViewModel is the datacontext for the MainView and does not, in fact, know or have any dependency on the mainview itself.

However, there are cases when the main view need to open dialogues that will affect the ViewModel data. For example, I may want show a dialogue and diplay some items to allow the users to select from. So, what I have settled on is this:

In my ViewModel, I have the following methods: AddItem, EditItem, and DeleteItem. However, in order to supply the items to add or edit, I need to present a list in some dialogue for the user to to choose from. Right now I have the ViewModel doing this only because I don't want to implement additional levels of abstraction for such simple tasks. Having the ViewModel do this means it can provide the list to be displayed to the user and, when the user finishes editing or selecting, it can easily update its member collections/properties.

Should I be shot for settling with this approach?

Jim D'Angelo
  • 3,952
  • 3
  • 25
  • 39
mike01010
  • 5,226
  • 6
  • 44
  • 77

5 Answers5

4

Shot? No. But there are good reasons for not doing this.

First, it kills testability of your ViewModel, as there's now a visual component in place. When you try to write automated unit tests against it, you'll still have to interact with it. You could mock it out, but it becomes more difficult to do so when you're calling UI methods.

Second, your viewmodel shouldn't care about what gets displayed. There's a real "separation of concern" issue when you start combining these things.

Third, it just has a "code smell."

There are a few things you can do to circumvent this issue. The first thing I would suggest is Don't use dialogs. Dialogs have their place, but programmers tend to overuse them. Rethink your design, and try to figure out how you can get the job done without interrupting the user.

Second, consider using a messaging framework to send messages between your viewmodel and view to do the navigation to the dialogs (if you absolutely have to use them). Messages are very easy to mock out and/or write unit tests around.

Robaticus
  • 22,857
  • 5
  • 54
  • 63
  • yeah, i aware of test-ability..and i figured that would be what most point out. the dilema i'm fighting with is that of simplicity and delivering quicker, vs these isolated breaches of coding ethics :). having this in a small method in the viewmodel makes it very quick and simple, very readable and isolated. having to use delegates, messaging, etc..and now a simple functionality is likely not as easy to read as you have to follow the events/messages. i'm thinking it's not so bad in my case and as we move forward and have time, we can always refactor when the time comes. – mike01010 May 24 '12 at 19:41
  • I've been in that dilemma before and lived through it. What I've come up with, though, is a design pattern that I can now quickly implement and deliver this approach without refactoring later. It probably takes me less time to use the pattern (since I'm familiar with it now) than it would to call the view logic directly. – Robaticus May 24 '12 at 19:59
1

the easy way to do this: use a dialogservice - easy to use, easy to unittest!

see this.

Community
  • 1
  • 1
blindmeis
  • 22,175
  • 7
  • 55
  • 74
0

I don't see any problems with ViewModels communicating with each other. The problem is if they start accessing the Views or other Dialogs since that will affect the systems testability.

If you really want a more loosely coupled system you could use some sort of messaging system for communication, but I doubt you need that here :-)

Rune Grimstad
  • 35,612
  • 10
  • 61
  • 76
0

I always use a Seelctor service(just a basic dialog service) to do this - it's testable and mockable and keeps the code very SOLID.

class ViewModel
{
    public ICommand ShowListSelectorForCounterparties { get; set; }

    public IListSelectorService ListSelector { get; set; }

    public void OnExecuteShowCounterpartySelector()
    {
        this.Counterparty = this.ListSelector.Select<Counterparty>();
    }
}

where IListSelectorService can, at runtime, instantiate your dialog, present your list and return the selected item. The main good thing about running it this way is that your unit tests can mock the IListSelectorService.

Barracoder
  • 3,696
  • 2
  • 28
  • 31
0

I'm not sure if you are still looking for any help, but the approach that I have taken when it comes to dialogs is to have the view model raise an event that the view can then handle. The view can now do whatever it wants to get the data to the view model, so you can disply the dialog in the view without a problem. You pass the response from the dialog to the EventArgs of your event so that the view model has the data it is looking for in order to proceed.

For example:

Public Class View

   Private WithEvents _VM AS new ViewModel()

   Private Sub _VM_AddingItem(Sender AS Object, E AS ViewModel.ItemEventArgs)
      Dim Dialog As new SomeDialog()

      If Dialog.ShowDialog then 
         E.Item = Dialog.Item
      Else
         E.Cancel = True
      End If
   End Sub 

End Class


Public Class ViewModel 
   Public Sub AddItem(Item AS Object) 
       Do Some Work here 
    End Sub 

    Private Sub _AddItem() 
       Dim Args AS New ItemEventArgs()

       OnAddingItem(Args)

       If not Args.Cancel Then AddItem(Args.Item)
    End Sub 

    Protected Sub OnAddingItem() 
       RaiseEvent AddingItem(me, ItemEventArgs)
    End Sub

    Public Event AddingItem(Sender AS Object, E As ItemEventArgs)

    Public Class ItemEventArgs
       Public Property Item AS Object
       Public Property Cancel AS Boolean = false
    End Class
End Class

Then just wire up your command to the private _AddItem method which just raises the event to collect the necessary data for the AddItem method. I hope this helps :)

Chris Tremblay
  • 63
  • 1
  • 2
  • 7