0

I am working on a Xamarin application (Mvvm using Prism), I am also leveraging OData for communication between my backend and the mobile application.

The structure of my Mobile app is as follows:

  • View
  • View Model
  • Service Layer
  • OData Proxy to return Models

It is a little funky with OData because my domain/DTO models are the same and I end up exposing those directly to the View layer however I am the only developer and anymore abstraction would be cumbersome to maintain... At least that was my thought up until this point. I am now faced with a situation where I must perform additional logic on my Model object before the presentation layer:

The example of the model (MyComplexModel) from OData looks something like this:

public class MyComplexModel
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<MyComplexModelFeedback> Feedback { get; set; }
}

public class MyComplexModelFeedback
{
    public int Id { get; set;}
    public int PersonId { get; set; }
    public int Vote { get; set; }
}

Usually I would just Bind the view directly to MyComplexModel and present it via a ListView but now I want to flatten it/sum the Vote property before I present. Is it appropriate to map this object to a new object directly in my ViewModel?

Or another thought I had was to add a facade pattern for my view models to use/expose, that I would map to in the service layer, however my code base would increase by >30%... Is there a simple way around this or is this problem a symptom of a larger design issue?

1 Answer 1

1

There's a couple things that you'll want to remedy:

  • Make your list read-only and make sure it is initialized.
  • Use an ObservableList in your model, particularly if you intend to bind or react to changes.

Example (NOTE: assumption that your model is INotifyPropertyChanged and notifies for changes to Id, Name, etc.

public class MyComplexModel
{
    public int Id { get; set; }
    public string Name { get; set; }
    public ObservableList<MyComplexModelFeedback> Feedback { get; } = new ObservableList<MyComplexModelFeedback>();
}

This allows your ViewModel to update and summarize your votes as needed. Example (assumes callback when model is changed):

public class MyComplexModelViewModel : ViewModel<MyComplexModel>
{
    public float Votes { get; }

    protected void OnModelChanged(MyComplexModel oldModel, MyComplexModel newModel)
    {
        oldModel?.Feedback.CollectionChanged -= OnFeedbackChanged;
        newModel?.Feedback.CollectionChanged += OnFeedbackChanged;
    }

    private void OnFeedbackChanged(object source, NotifyCollectionChangedEventArgs args)
    {
       Votes = CalculateVotes(Model.Feedback);

       RaisePropertyChanged(nameof(Votes));
    }
}

I'll leave it to you to implement the CalculateVotes method. This is just one way to show how to trigger the recalculation of the votes any time there is a change to Feedback.

The layers don't have to change here, just take advantage of the events that happen with INotifyPropertyChanged or INotifyCollectionChanged classes. An alternate method is to avoid the events altogether and periodically recalculate things as necessary. Both can work depending on how complicated it is to calculate your total vote.

Your View takes care of the direct binding to both the ViewModel and the Model, depending where you need to update the display. You can still keep it simple.

2
  • I like it - this makes sense. So my mental block was coming from the fact that my ViewModel (MyComplexModelsViewModel) contained an ObservableList<MyComplexModel>. So really what I need is to create a MyComplexViewModel (as in your answer) and put it within MyComplexModelsViewModel? Commented Jun 7, 2018 at 15:27
  • 1
    @40Alpha, I believe that is correct. It's not uncommon to have a View and ViewModel for everything displayed. That way you can have an ItemsControlPanel that shows off the individual elements in your observable list. You could "technically" just subscribe to the events in your MyComplexModel class, but I just laid out the usual scenario. Commented Jun 7, 2018 at 16:40

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.