Poser une questionPoser une question
 

TraitéeModel and ViewModel question

  • samedi 7 novembre 2009 16:51shtreber Médailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateur
     A du code

    I have model that represents a file on hard drive and it is implemented in class FileModel.
    One of the properties of FileModel is FileSize which represents the size of the file. This FileSize is of type "long" which is just a number of bytes, and in order to present it to the user friendlier for I wrote the CustomConvertToString(long size) method.

    Now, I create FileViewModel which is a VM for FileModel and it is implemented in the code below. My question is this: when FileSize property in FileModel is changed (eg. user has added some content to file) then in what way its corresponding FileViewModel should be notified about this? Should I implement a handler for NotifyPropertyChanged event in VM that will watch for changes in model? Or is there some other better way?

    I ask this because if FileSize in model is changed, TextBlock in View binded to the FileViewModel.FileSize wont be able to see this change unless VM notifies it.

     

    	public class FileModel : INotifyPropertyChanged
    	{
    		private long fileSize;          
    		public long FileSize
    		{
    			get { return fileSize; }
    			set
    			{
    				if (fileSize != value)
    				{
    					fileSize = value;
    					OnPropertyChanged("FileSize");
    				}
    			}
    		}
    		
    		#region INotifyPropertyChanged Members
    // interface implementation.... #endregion } public class FileViewModel { public FileModel Model {get;set;} // model to which this VM is associated // wrapper for Model's FileSize to show user friendly file size in GUI public string FileSize { return CustomConvertToString(Model.FileSize); } }

     

     

Réponses

  • samedi 7 novembre 2009 18:15HomeroThompson Médailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateur
     TraitéeA du code
    Hello,

    It is fine to implement INotifyPropertyChanged in your model, but in MVVM not always the model can implement this mechanism (we could be making a  new presentation layer for an existing model). So, generally it is responsability of the ViewModel to make notification changes, so I would create an event in the model that is raised when the size changes, then in the view model, add an event hanlder for this event to set the ViewModel's property FileSize with the new value. This property will raise PropertyChanged event and your textblock will be notified for this change.

    Don't forget to implement the setter:

    	public class FileViewModel
    {
    public FileModel Model {get;set;} // model to which this VM is associated

    // wrapper for Model's FileSize to show user friendly file size in GUI
    public string FileSize
    {
    get{return CustomConvertToString(Model.FileSize)}
    set { Model.FileSize = value; OnPropertyChanged("FileSize"); }
    }
    }
    Good Luck.

Toutes les réponses

  • samedi 7 novembre 2009 17:59Stephanne Médailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateur
     
    Hi,

    One solution would be to create a base VM class that implements INotifyPropertyChanged and holds a model (if you are planning to reuse, having more models and VMs in your app)...then, in this class you could listen to property changed notifications from your model. In any case, you will need to implement INotifyPropertyChanged in your VM somewhere. Also, try considering an IValueConverter and place it in your binding if you want to give user friendly display for your file size. This code should work - you change your model's FileSize property, thus raising the change notification up to your VM's property (that has the same property name) that is bound in your GUI somewhere

    public

     

     

    abstract class VMBase : INotifyPropertyChanged

    {

     

     

    public VMBase(BaseModel bm)

    {

    bm.PropertyChanged += fm_PropertyChanged;

    }

     

     

    void fm_PropertyChanged(object sender, PropertyChangedEventArgs e)

    {

    OnPropertyChanged(e.PropertyName);

    }

     

     

     

    public event PropertyChangedEventHandler PropertyChanged;

     

     

    void OnPropertyChanged(string prop)
    {

     

     

    if (PropertyChanged != null)

    {

    PropertyChanged(

     

    this, new PropertyChangedEventArgs(prop));

    }

    }

    }

     

     

    public class FileViewModel : VMBase

    {

     

     

    public FileViewModel(FileModel m) : base(m)

    {

    Model = m;

    }

     

     

    public FileModel Model {get;set;}

     

     

     

    public long FileSize

    {

     

     

    get

    {

     

     

    return Model.FileSize;

    }

     

     

    set

    {

    Model.FileSize =

     

    value;

    }

    }

    }

  • samedi 7 novembre 2009 18:00Guenter Schwaiger Médailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateur
     
    Hi shtreber,

    your FileModel.FileSize property is perfect for binding to the view (INotifyPropertyChanged).

    I would bind the TextBlock to this property using your Converter in the binding expression (... Text="{Binding Path=Model.FileSize, Converter={StaticResource YourConverterReference}}).

    Otherwise you have to listen and refire the PropertyChanged event for the FileSize property.

    Hope this helps

  • samedi 7 novembre 2009 18:15HomeroThompson Médailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateur
     TraitéeA du code
    Hello,

    It is fine to implement INotifyPropertyChanged in your model, but in MVVM not always the model can implement this mechanism (we could be making a  new presentation layer for an existing model). So, generally it is responsability of the ViewModel to make notification changes, so I would create an event in the model that is raised when the size changes, then in the view model, add an event hanlder for this event to set the ViewModel's property FileSize with the new value. This property will raise PropertyChanged event and your textblock will be notified for this change.

    Don't forget to implement the setter:

    	public class FileViewModel
    {
    public FileModel Model {get;set;} // model to which this VM is associated

    // wrapper for Model's FileSize to show user friendly file size in GUI
    public string FileSize
    {
    get{return CustomConvertToString(Model.FileSize)}
    set { Model.FileSize = value; OnPropertyChanged("FileSize"); }
    }
    }
    Good Luck.
  • samedi 7 novembre 2009 19:44shtreber Médailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateur
     
    Thanks for your answers guys.

    I have no problem implementing IPropertyNotifyChanged in both model and VM if that will make things easier for me even if that doesn't follow strictly the MVVM pattern. So I was just wondering what is the best way for model to notify VM about changes. And since both of them will be implementing IPropertyNotifyChanged I would like to use that rather than creating events in model. So I guess implementing handler for IPropertyNotifyChanged events in VM would be way to go for me in this scenario (an example of this would be nice if someone has it)?

    Regarding WPF XAML Converters, I was thinking about avoiding them because there is this post from WPF guru, Josh Smith where he advocates that in MVVM model there is no need for them... Check his post here: http://groups.google.com/group/wpf-disciples/browse_thread/thread/3fe270cd107f184f/86bce64b4546cf4d?#
  • samedi 7 novembre 2009 20:58Guenter Schwaiger Médailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateur
     

    Converters are not as bad as you may think (converters can be unit tested). If you convert everything in your ViewModel then a designer has to take it as you provide it. If you give him the original value he has the choice to use different converters to show that value on the view. A converter also can be reused the ViewModel not.

    In your situation you have full control over the implementation of the FileModel and you already implememented the INotifyPropertyChanged interface you didn't need to duplicate it in your ViewModel.

    What else binds the View from your ViewModel? Only the same properties as the FileModel provides?

  • samedi 7 novembre 2009 21:18shtreber Médailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateur
     
    In your situation you have full control over the implementation of the FileModel and you already implememented the INotifyPropertyChanged interface you didn't need to duplicate it in your ViewModel.

    What else binds the View from your ViewModel? Only the same property as the FileModel provides?

    View binds to properties from FileModel but some of them need to be converted to user friendlier form (like FileSize). But in general some other viewmodels I have might containt additional data about models that is not present in models it self. What do you have in mind?

    Also, duplicating INotifyPropertyChanged in VM is for the purpose of notifying View when the converted properties in VM are changed. Do you think this is not the best way to go? If so, how would be the best way to implement it?

    Thanks!
  • samedi 7 novembre 2009 21:56Guenter Schwaiger Médailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateurMédailles de l'utilisateur
     

    I am asking because sometime people believe they have to wrap every property of the Model in the ViewModel. That's not necessary.

    In the case of the FileSize I would implement a Converter, because it makes no sense to listen to the PropertyChanged event only to convert that value to a string.