I'm in MVVM ViewModel hell
Asked Answered
V

3

5

So far, I am really happy with the way things have gone in my app's transition from typical click event handlers everywhere, to complete GUI decoupling. Now I'm running into something I can't quite figure out, and it's related to a window that I want my GUI to pop up, and it needs to display information from the model.

I guess the short version of my question is, is it absolutely forbidden in MVVM to allow the model to have a reference to a ViewModel? Here's my scenario: I have a bank of LEDs that cycles through RGB values very quickly. I would like a window in my GUI to display the updated colors via databinding with a ViewModel. I have the Window + UserControl working fine with a mockup ViewModel in a test application, but now I have to put this Window into my real application.

The particular mode I'm running in one that simulates what the hardware is doing. When I command the model to cycle through the colors, it starts a thread that changes the necessary class member variables' values.

My current implementation of MVVM is basically polling all of the time. To get other LEDs to update elsewhere, I have a thread running that calls a function in the ViewModel. This updates the properties, and so the GUI automatically updates since I'm using databinding. The problem in my LED example is that simulating the color sequence is done in a thread, so if I need to have a ViewModel poll for values, it will likely be slow due to excessive locking of the LED variables.

Therefore, I'm hoping that someone can recommend another approach to this problem. So far, the only thing I could really think of is to have the Window datacontext be an LEDViewModel, and then also pass the LEDViewModel to the Model. Then when I call the RGB cycling function, it can change the necessary ViewModel properties as necessary, and I won't need to use any locking at all.

Does this make sense? Any advice would be really appreciated.

Vilmavim answered 20/1, 2010 at 21:5 Comment(0)
C
7

Have you tried just implementing the INotifyPropertyChanged interface on your model?

It would seem to me that this should perform well enough. When the color state changes on your model, you can fire off the PropertyChanged event, update the view model state from that notification, and have the view update via a binding on the view model.

Cryptanalysis answered 20/1, 2010 at 21:22 Comment(17)
I could do that, but I was thinking that it would violate the guidelines of MVVM. I thought that the only thing that should implement INotifyPropertyChanged is the ViewModel. ??Vilmavim
No, not at all. How else would your view models synchronize state which originates in the model?Cryptanalysis
I usually agree that the model shouldn't be modified just for the needs of a specific UI. However, in that case it might make sense, since the model is not only updated from the UI, it also updates itself... Anyway, it's the only alternative to polling in that caseRebuild
@Cryptanalysis - so far, I've had the ViewModel poll the Model. If I need information from hardware, the VM calls a method in the Model that queries the hardware, and databinding does the rest.Vilmavim
@Cryptanalysis - do you have any links to examples for this? I guess I only know the basic concept that you implement INotifyPropertyChanged to update something in the GUI that is connected via databinding. How would RaisePropertyChanged be "hooked" into the viewmodel so its property is also changed? Is passing the VM into the Model bad (I assume that doing so really is a violation of MVVM).Vilmavim
@Thomas - this isn't the model being modified for the view, it's the model sending out updates via an interface. The INotifyPropertyChanged interface is in System.ComponentModel for just such a reason. I often implement it on domain classes, since it allows me to follow the open/closed principle more strongly. @D. Matsumoto - I wouldn't poll. It just doesn't make sense. You essentially have an anemic domain model if you are forced to poll.Cryptanalysis
@D. Matsumoto - otherwise just use a regular .Net event in the domain model, and consume it in the view model, converting it into the appropriate property change.Cryptanalysis
@D. Matsumoto - also, right, you want to make the view model aware of and consume the domain model, and the view bind to the view model. The dependencies should be one-way here, and the lower layers shouldn't know about the upper ones. That's the most important principle. How you manage state change between layers is a more secondary concern.Cryptanalysis
excellent, thank you for the advice. I'll start working on it soon and we'll see how far I can get. I had considered using events, but actually I think using INotifyPropertyChanged will be a better approach. However, I don't know how to really use INotifyPropertyChanged other than implementing it and letting databinding do the heavy lifting for me. I will need to read up on that interface tonight to figure out how to get the ViewModel to listen for the property changes.Vilmavim
hmm... now that I think of it... isn't it silly to use MVVM to make the usercontrol values databound to the viewmodel? Isn't the point of a usercontrol to already provide that sort of separation? I should really have my model set the LED properties in the usercontrol via events or similar. I am so mixed up. :)Vilmavim
@D. Matsumoto - You can use a UserControl, and the same principles will be able to be applied. Use the view model to manage state and domain-model synchronization, and the view to bind to the view model. It doesn't matter if your view is a UserControl. The logic of the view goes in the view model.Cryptanalysis
@Cryptanalysis - so it's generally acceptable to have this ViewModel for the UserControl be in the UserControl itself (as a public class), and then have my GUI application just modify the ViewModel from the Model via any mechanism of my choosing, right?Vilmavim
@D. Matsumoto - the view model class which the UserControl would bind to would be a separate class. When you create one, you pass it (or inject) some domain object or objects, depending on how you want to display them. Then you bind the view model to the view, and display the view. You can also go the other way, which is to have the view get created, create the view model, and have the domain objects get injected.Cryptanalysis
@D. Matsumoto - another thing: don't worry so much about the details of the pattern. It is, after all, a pattern, not a recipe. In a pattern, things are described generally, and for a specific instance, details are needed to be worked through which make sense for that particular situation. There is no right way to do MVVM, or any other pattern; just some good principles, guidelines and well-understood tradeoffs.Cryptanalysis
The only problem with this approach is you have to be careful about memory leaks. If these Views are meant to leave and reappear, while the model stays constant, the application might bloat if you don't use the WeakEvent pattern for your INotifyPropertyChanged subscriptions from your ViewModel (or a similar Unsubscribe method).Equalitarian
@Cryptanalysis - ok, I have been going view->viewmodel->model, but it sounds like either way is fine. The main point is to make sure low-level classes have no knowledge of higher-level ones. My question about the ViewModel class was whether or not it should be in the UserControl dll itself (not separate DLL). So when I move this UC from app to app, each app would create the View (the UC GUI), the View creates the ViewModel, and the app calls a method in the UC to inject the model. I think I we're saying the same thing, and I'm just getting caught up too much in the implementation details.Vilmavim
@D. Matsumoto - yes, that sounds right. Be sure to note @Anderson's point about event wiring. I usually have a 1:1 view to view model correspondence, and the lifetimes of each are the same, so as to avoid this problem. If you have a reason to maintain a view model, though, across view lifetimes, make sure to detach the event handlers or use a weak event pattern.Cryptanalysis
E
3

Why not use eventing on some sort of message broker for your application?

The easiest way to do this would be to use the Messenger in MVVMFoundation: http://mvvmfoundation.codeplex.com/

An example of this would be:

public class MyHardwareModel
{
     private void OnHardwareLEDChanged() // or whatever
     {
          SharedMessages.Messenger.NotifyColleagues(SharedMessages.LEDCHANGED);
     }
}

And then in your view model, when it spins up, you register for notification of these messages while that instance of the view model is alive:

public class MyHardwareViewModel
{
     public MyHardwareViewModel()
     {
          SharedMessages.Messenger.Register(SharedMessages.LEDCHANGED, UpdateLeds);
     }

     private void UpdateLeds()
     {
          //Update ObservableCollection here.
     }
}

The message mediator/broker pattern is really useful in these situations for so much more than just this. The Messenger built into MVVMFoundation is pretty powerful... in my sample I'm using pretty generic messages, but you can sent more typed messages with parameters.

There is a similar function built into Prism / Composite Application Guidance if you are using that called the EventAggregator. It's used in a similar way.

Hope this helps.

Equalitarian answered 21/1, 2010 at 4:39 Comment(1)
NICE! My approach to MVVM has been to try to learn how to do it without the aid of a framework on this version of my app, but then gradually ease into it as time progresses. Prism and the MVVM Framework from MS are two that I have been thinking about researching. I will definitely give Prism a try soon. Thank you for the suggestion!Vilmavim
R
0

A simple approach would be to perform the polling regularly, say every 50ms. This can be done very easily using a timer, and will be less resource consuming than constant polling from a thread. 50ms seems a reasonable interval, even if your LEDs actually cycle faster, because the user won't have the time to see the color change anyway...

Rebuild answered 20/1, 2010 at 21:49 Comment(1)
I think I'll just modify the model, and we'll see how it goes. :) I am using a DispatcherTimer in a few other places, and for low update frequencies, it's quite good.Vilmavim

© 2022 - 2024 — McMap. All rights reserved.