4

I'm trying to accomplish a correct MVVM architecture in WPF.

I have a player, in the "Model" section there is a Boolean property that says if we are "playing" right now.

public bool IsPlaying
    {
        get
        {
            return isPlaying;
        }
        set
        {
            isPlaying = value;
            OnPropertyChanged("IsPlaying");
        }
    }

(Notice I implemented the "INotifyPropertyChanged" interface, so the OnPropertyChanged function reports the change)

in my ViewModel, I have a ImageSource property called "ToggleButtonIcon":

public ImageSource ToggleButtonIcon
    {
        get
        {
            if (Model.IsPlaying)
                return pauseIcon;
            else
                return playIcon;
        }
    }

Which I bind to a "TogglePlayButton" in the view:

<cc:IconButton x:Name="TogglePlayButton" 
               Style="{StaticResource playButton}" 
               ImageSource="{Binding Path=ToggleButtonIcon,
               UpdateSourceTrigger=PropertyChanged}"  
               Command="{Binding Path=TogglePlayCommand}"/>

(It's a custom control, but it's working, I checked) Of course I want the button to change its image icon according to if it is playing (pause) and if it is not playing (play).

Problem is the ToggleButtonIcon does not notify when it changes, and I can't implement the INotifyValueChanged in the ViewModel section because a. I understood that's not a part of the MVVM architexture, and b. I don't know when it changes since it depends on the IsPlaying property of Model.

I thought about putting the ToggleButtonIcon property on the Model section, but that's not "Business Logic" so I don't think that's the right way.

I also thought about using a converter and bind the IconButton directly to "IsPlaying", which would probably work, but I read here: How can WPF Converters be used in an MVVM pattern? that you should not use converters at all in MVVM because you can do any convertion you want in the "ViewModel" Section.

What's the best way to accomplish this in MVVM architecture?

Shachar Har-Shuv
  • 666
  • 6
  • 24
  • 1
    Where did you get the idea "that's not how you do it"? ViewModels commonly implement INotifyPropertyChanged. I'd say it is actually uncommon for models to do so, but you have apparently done so. If you go that way then you will have to subscribe to the model's PropertyChanged event in the viewmodel and raise the appropriate changes dependent properties. Another approach would be to just bind to the model object and use a DataTrigger to change the imagesource depending on the state of IsPlaying. – Mike Zboray Nov 25 '17 at 22:28
  • "Where did you get the idea..." - I've seen an example on youtube. "you will have to subscribe to the model's PropertyChanged event..." - how do I do that? "...and use a DataTrigger to change the imagesource depending on the state of IsPlaying" - what does that mean and how do I do it? – Shachar Har-Shuv Nov 27 '17 at 08:33

3 Answers3

2

To me, IsPlaying should be in the ViewModel, with change notification implemented, as it represents an application state of sorts.

To solve the issue I would recommend taking the ToggleButtonIcon out of the ViewModel, and creating a DataTrigger on the IconButton control (via its Style), that binds to the IsPlaying property (on the ViewModel) and alters the ImageSource property based on that.

Chris Mack
  • 5,148
  • 2
  • 12
  • 29
0

The model of MVVM should only hold class entities and those entities can on occasion have an INotiftPropertyChanged, but generally they do not.

What your intent though is that it is to convey a status and that should be on your viewmodel.

I would recommend that you have the status of IsPlaying be on the View Model (VM) and bind to that. Then in the command of TogglePlayCommand, it will set that property on the VM.

That way both items update propertly on a change to either. You can still new up your original object in the model and on the Setter of the VM's IsPlaying set the class instance property to its value if needed.


Take a look at my blog post Xaml: ViewModel Main Page Instantiation and Loading Strategy for Easier Binding. Take note on how I use OnPropertyChanged to push change messages on other operations which can all for the flexibility you seek as well as having the viewmodel hold statuses and not the models.

ΩmegaMan
  • 29,542
  • 12
  • 100
  • 122
  • So you're proposing putting the IsPlaying BOTH in the model and in the view model? So "ViewModel.IsPlaying" will get/set its value from the "Model.IsPlaying"? – Shachar Har-Shuv Nov 27 '17 at 08:31
  • I would prefer to keep any view operations in the ViewModel, but its unclear why the OP is using it on the Model. – ΩmegaMan Nov 28 '17 at 00:49
0

You should put bool on class which implements interface INotifyPropertyChange: Here an example:

public class Game : INotifyPropertyChanged
{
    private bool _isPlaying;

    public string IsPlaying
    {
        get { return _isPlaying; }
        set {
            _isPlaying = value;
            this.NotifyPropertyChangedBool();    
        }
    }
    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChangedBool()
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new 
                           PropertyChangedEventArgs("IsPlaying"));
        }
    }
Federico Rizzo
  • 183
  • 2
  • 9