3
votes

Im creating a small application for managing online racing leagues. For this i will have a database that accesses data from web and exposes data objects through interface. The database doesn't exist yet but i created a Mockup which uses local XML files as datasource.

Small example for a league interface:

public interface ISchedule
{
    string Name { get; set; }
    List<IRaceSession> Races { get; }
    // and some more properties …

    IRaceSession AddRace();
    void RemoveRace(IRaceSession race);
    // and some more Methods …
}

public interface IRaceSession
{
    uint RaceId { get; }
    DateTime Date { get; set; }
    TimeSpan Duration { get; set; }
    // and some more properties …
}

Now to get this into my WPF with MVVM pattern i created a Model for each object the database is exposing and implemented the INPC there. *Note: ContainerModel<> and ObservableModelCollection<> are classes i created to handle updates from the database while still keeping the INPC intact.

public class ContainerModel<T>
{
    T Source { get; set; }

    public ContainerModel(T source)
    {
        Source = source;
    }

    void UpdateSource(T source)
    {
        // handle updates …
    }
}

public class ScheduleModel : ISchedule, ContainerModel<ISchedule>
{
    public string Name { get => Source.Name ; set { Source.Name = value; NotifyPropertyChanged(); } }

    public ObservableModelCollection<RaceSessionModel, IRaceSession> Races { get; }
    List<IRaceSession> ISchedule.Races => Source.Races

    public ScheduleModel(ISchedule source) : base(source)
    {
        Races = new ObservableModelCollection<RaceSessionModel, IRaceSession>(Source.Races);
    }

    IRaceSession AddRace()
    {
        Races.Add(// new Race Object);
    }
    void RemoveRace(IRaceSession race)
    {
        Races.Remove(// select race object to remove);
    }
}

public class RaceSessionModel : IRaceSession, ContainerModel<IRaceSession>
{
    public uint RaceId => Source.RaceId;
    puglic DateTime Date { get => Source.Date; set { Source.Date = value; NotifyPropertyChanged(); } }
    TimeSpan Duration { get => Source.Duration; set { Source.Duration = value; NotifyPropertyChanged(); } }
    

    //--> here come some more logic im not sure About:
    TimeSpan DurationHours 
    { 
        get => Duration.Hours;
        set 
        {
            // set only Hours componennt of Duration
            Duration = Duration
                .Subtract(Duration.Hours)
                .Add(value);
            NotifyPropertyChanged();
        }

    TimeSpan DurationMinutes 
    { 
        get => Duration.Minutes;
        set 
        {
            // set only Minutes componennt of Duration
            Duration = Duration
                .Subtract(Duration.Minutes)
                .Add(value);
            NotifyPropertyChanged();
        }
}

Then i have a viewModel and a view that binds to the Model properties directly.

public class SchedulerViewModel : ViewModelBase //<-- just a Basic implementation of INPC
{
    private ScheduleModel _schedule;
    public ScheduleModel Schedule { get => _schedule; set { _schedule = value; NotifyPropertyChanged(); } }

    public SchedulerViewModel(ScheduleModel schedule)
    {
        Schedule = schedule;
    }

    // Commands and other properties communicating with the view
    // …
}

Now with this design i have a few concerns coming up. I am still in the process of getting my head around the whole design pattern and it is the first time i am Building it up from Scratch.

  1. My Models don't really contain data, but only expose properties from the database Am i Right in thinking that this is actually something a viewModel should do?

  2. As you can see my models also hold some Kind of calculation for property Input. Should this "Business logic" be put outside the model too? Or would that also be better put in a ViewModel?

After all im doubting what i present are "models" at all. Would it be Right to call them ViewModels and then act with the Object from the database as the model?

*Edit: When i began with this i read that for each View you should supply only one ViewModel, which is why i created my classes like this. But im not sure anymore if this is correct.

3
In a case like this you could also keep it simple: treat IRaceSession and ISchedule as Model, everything else (which isn't really a lot) goes in the ViewModel. I don't see why you'd add an extra layer (which practically doesn't do anything) in between just for the sake of it. - stijn
@stijn: thanks. That's basically where was going to with my question but i am concerned if this approach might hit back when the application eventually gets more complex. - S Schulze
Possibly, but you can always refactor later, and it would be fairly easy to do. See KISS and YAGNI principles: if simple, clean, readable code does the job now, in my opinion you'd have to have very good reasons not to use it. I've written fairly large amounts of MVVM code and looking back, trying to adhere strictly to MVVM or not recognizing what the Model is (or adhering to strictly one definition of 'model'), usualy just a representation of the data, leads to overcomplication. - stijn

3 Answers

1
votes

For the transfer of Data from the Model to the ViewModel, I would create simple Objects (Dto - DataTransferObject) without (or very minimal) Logic. A good rule of thumb is that you want unsaved Data in the ViewModel while saved Data - or data that is about to be saved - belongs in the Model.

//--> here come some more logic im not sure About:
TimeSpan DurationHours 
{ 
    get => Duration.Hours;
    set 
    {
        // set only Hours componennt of Duration
        Duration = Duration
            .Subtract(Duration.Hours)
            .Add(value);
        NotifyPropertyChanged();
    }

TimeSpan DurationMinutes 
{ 
    get => Duration.Minutes;
    set 
    {
        // set only Minutes componennt of Duration
        Duration = Duration
            .Subtract(Duration.Minutes)
            .Add(value);
        NotifyPropertyChanged();
    }

I would put these into the ViewModel. In the Model you want the whole Timespan Object, only the ViewModel should be aware of the restrictions that you can only set them separately in the View.

When i began with this i read that for each View you should supply only one ViewModel, which is why i created my classes like this. But im not sure anymore if this is correct.

Yes, it saves a lot of headache later on, when you have to maintain your code and make changes which only apply to a single View.

1
votes

It may depend largely on what you're attempting to do with the application, but generally I would approach it like this:

  • Model for Schedule
  • Model for RaceSession
  • ViewModel for RaceSession, containing a RaceSession Model
  • ViewModel for Schedule containing a Schedule Model a collection of RaceSessionViewModels

I've always regarded models as pretty much just representing a row from a database, essentially; a basic data entity that exists on its own outside of the application. The ViewModel is then anything that is relevant only within the application.

The reason I wouldn't have a collection of RaceSession Models in the Schedule Model, would be that if you were to do any kind of application-based manipulation of the RaceSessions, that's something that belongs in a ViewModel, so you'd then sort of be looking at a Schedule Model, with a collection of RaceSessionViewModels. So I'd keep Models strictly as single data entities, without any kind of entity relationships (joins) built in - these kinds of relationships, I'd build in to the ViewModel layer. Even if you didn't need a ViewModel for RaceSession, I'd still have a collection of RaceSession Models within the Schedule ViewModel.

As an example of the above, I believe that the AddRace method really belongs in the Schedule ViewModel, rather than the Schedule Model.

With regards to the TimeSpan calculations, I'd probably have the Hours and Minutes as get only properties (on the RaceSession ViewModel), with other methods on a RaceSessionViewModel that alter the Duration property directly. The exact implementation of this would depend on whether or not you were actually changing these in the database when they update.

Some Example Pseudocode

public class RaceSession : INPC
{
    INPCProperties

    RaceSession(inpcProperties)
    {
        INPCProperties = inpcProperties;
    }
}

public class RaceSessionViewModel : INPC
{
    public RaceSession RaceSession { get; set (INPC); }

    public int Hours => RaceSession.Duration.Hours;

    public RaceSessionViewModel(raceSession)
    {
        RaceSession = raceSession;
    }

    private void SetDurationHours(int hours)
    {
        RaceSession.Duration =
            Duration
            .Subtract(Duration.Hours)
            .Add(hours);

        NotifyPropertyChanged("Hours");
    }
}
0
votes
  1. No. A viewmodel connects the data (the models not the database) with the view. But it neither manipulates the view nor the database.
  2. I wouldn't leave that in the models. This kind of stuff belongs actually in the viewmodel. In every view you can have multiple viewmodels to present, but you definitely need at least one to begin with. I suggest, as you already started constructing your MVVM, that you now look up an example in the internet somewhere that implements this design pattern. I suppose you already read some of the theory behind it.