Is it OK to subscribe to the ViewModel's .NET events from the View in MVVM?
Asked Answered
D

3

1

I am writing this major memory trainer using the MVVM pattern by animating labels containing 2 digit numbers across the screen and asking the user to quickly type in the corresponding mnemonic for each number. It is entirely up to the View how the animation is done so there will be some code behind for this. When the correct mnemonic is typed into the UI or when the number disappears off the screen Commands will be executed from the View to relay that this has happened.

In the ViewModel I want to periodically fire off new numbers with which the View animates (as it pleases).

What's the best way of achieving this? I can have an ObservableCollection<> in the ViewModel but I want to do more than simply bind to it, I will need to execute a method in the code behind when numbers are added and removed.

Is it in accordance with MVVM to use simple .NET events in the ViewModel and subscribe to them with: DataContext.NumberAdded += new NumberAddedEventHandler(....) or is there another way I should be doing it?

Diaphane answered 30/4, 2013 at 12:30 Comment(1)
Additionally, I believe you don't even need an event. You should be able to bind to a collection of "current challenges" from the view, render each challenge using a DataTemplate and handle the animation with triggers. No event required.Proparoxytone
I
2

A View is meant to be a user-friendly reflection of the ViewModel. If you have view-specific logic to run (such as triggering an animation), there's no reason not to use code-behind to run it.

Providing you keep your UI and data layers separate, you're fine.

That said, providing a NumberAdded event from the ViewModel doesn't really make sense to me if you're only using it from the View layer. That's mixing up your layers.

Instead I would simply use the regular CollectionChanged

((MyViewModel)this.DataContext).Numbers.CollectionChanged += 
    new CollectionChangedEventHandler(....);

Depending on how your collection is bound to the UI, you may also be able to use a UI event, or possibly triggers instead.

I thought elements with an ItemsSource raised an event when an item got added or removed, or you could simply cast the ItemsSource property into a collection and hook up to the CollectionChanged event there without needing to reference MyViewModel

void SomeItemsControl_DataContextChanged(...)
{
    var collection = (SomeItemsControl.ItemsSource as ObservableCollection);
    if (collection != null)
        collection.CollectionChanged += new CollectionChangedEventHandler(....);
}
Invar answered 30/4, 2013 at 12:57 Comment(4)
The only thing I'd think in terms of preference here is as I mentioned to have a DP than directly cast the DataContext and subscribe to the Numbers collection. this way if the binding was removed in the future or the ViewModel switched, your not going to have an exception and also just looking at the xaml you know there is a dependency this View expects from it's DataContext. Again that's just a preference and can be ignored. Just gives a slight future-proof to make the view reusable with different VM'sIvanna
@Ivanna Yes, a DP is definitely another good solution to the problem, especially if the entire collection isn't bound somewhere in the UI. :)Invar
Yeh for me having that flexibility to attach this view to another ViewModel without much change than a possible binding property name just seems to trump over dependency subscription :)Ivanna
Gonna use Viv's method. I'm using a Canvas to animate the numbers on so the Labels will be stored in canvas1.Children and there will be two ObservableCollection<int>s, one in the View which is bound to the one in the ViewModel.Diaphane
I
1

Why do you reckon binding to it is bad?

I would probably bind to the ObservableCollection<T> from the View.

Create a DependencyProperty of ObservableCollection<T> in the View and on the PropertyChanged Callback subscribe to the OnCollectionChanged event which can then process the animation accordingly based on the contents of the ObservableCollection<T> in the View.

This way the VM just updates the collection with these numbers that you mention and the View is hooked up to action whenever the collection changes in the manner it desires fit

Ivanna answered 30/4, 2013 at 12:57 Comment(1)
Phew! Finally managed to wire this up, I'm rather rusty after a long coding break. I love this way of doing it.Diaphane
E
0

Use Attached Behaviours to bind your viewmodel properties to any custom behaviour you need.

Engobe answered 30/4, 2013 at 12:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.