locked
ViewModel First with nested ViewModels and nested Views RRS feed

  • Question

  • I am new to using MVVM and am trying to find a reasonably elegant approach using a ViewModel First strategy while creating an interface of nested Views.  I have taken a brute force stab at making a workable solution but I can't imagine this is an ideal way to handle this challenge.  I have been unsuccessful in finding a solution in any forum.  I have included some limited function bogus classes and code to demonstrate my working idea.  Any help would be appreciated.

    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Windows.Controls;
    
    namespace SP.Player.WPF.Bogus
    {
        public class BogusStart
        {
            public static Lesson GetLesson(int id)
            {
                //entity and children would be called from repository
                //return LessonRepository.Get(id)
                //.Include("Activity)
                //.Include("Activity.SubActivity)
                //.FirstorDefault();
    
                //Dummy Return
                return new Lesson();
            }
            public UserControl BuildLessonAndChildViews()
            {
                var _LessonViewModel = LessonViewModel.ViewModelFactory(GetLesson(1));
                var _LessonView = new LessonView();
                foreach (ActivityViewModel _ActivityViewModel in _LessonViewModel.ActivityViewModels)
                {
                    var _ActivityView = new ActivityView();
                    _ActivityView.DataContext = _ActivityViewModel;
    
                    foreach (SubActivityViewModel _SubActivityViewModel in _ActivityViewModel.SubActivityViewModels)
                    {
                        var _SubActivityView = new SubActivityView();
                        _SubActivityView.DataContext = _SubActivityViewModel;
                        _ActivityView.ChildrenSP.Children.Add(_SubActivityView);
                    }
                    _LessonView.ChildrenSP.Children.Add(_ActivityView);
                }
    
                return _LessonView;
    
    
            }
    
        }
        public class Lesson
        {
            public int id { get; set; }
            public string Title { get; set; }
            public string Desc { get; set; }
            public ICollection<Activity> Activities { get; set; }
        }
    
        public class Activity
        {
            public int id { get; set; }
            public string Title { get; set; }
            public string Desc { get; set; }
            public int ScoreWeight { get; set; }
            public ICollection<SubActivity> SubActivities { get; set; }
        }
        public class SubActivity
        {
            public int id { get; set; }
            public string Title { get; set; }
            public string Desc { get; set; }
            public int ScoreWeight { get; set; }
        }
    
    
        public class LessonViewModel
        {
            public Lesson _Lesson { get; set; }
            public ObservableCollection<ActivityViewModel> ActivityViewModels { get; set; }
            public LessonViewModel(Lesson _lesson)
            {
                _Lesson = _lesson;
    
    
                foreach (Activity _Activity in _Lesson.Activities)
                {
                    ActivityViewModels.Add(ActivityViewModel.ViewModelFactory(this, _Activity));
                }
            }
            public static LessonViewModel ViewModelFactory(Lesson _lesson)
            {
                return new LessonViewModel(_lesson);
            }
        }
        public class ActivityViewModel
        {
            public Activity _Activity { get; set; }
            public LessonViewModel _LessonViewModel { get; set; }
            public ObservableCollection<SubActivityViewModel> SubActivityViewModels { get; set; }
            public ActivityViewModel(LessonViewModel _lessonViewModel, Activity _activity)
            {
                _Activity = _activity;
                _LessonViewModel = _lessonViewModel;
    
                foreach (SubActivity _subActivity in _Activity.SubActivities)
                {
                    SubActivityViewModels.Add(SubActivityViewModel.ViewModelFactory(this, _subActivity));
                }
            }
    
            public static ActivityViewModel ViewModelFactory(LessonViewModel _lessonViewModel, Activity _Activity)
            {
                return new ActivityViewModel(_lessonViewModel, _Activity);
            }
    
        }
        public class SubActivityViewModel
        {
            public SubActivity _SubActivity { get; set; }
            public ActivityViewModel _ActivityViewModel { get; set; }
            public SubActivityViewModel(ActivityViewModel _activityViewModel, SubActivity _subActivity)
            {
                _ActivityViewModel = _activityViewModel;
                _SubActivity = _subActivity;
            }
            public static SubActivityViewModel ViewModelFactory(ActivityViewModel _activityViewModel, SubActivity _subActivity)
            {
                return new SubActivityViewModel(_activityViewModel, _subActivity);
            }
        }
    
        ///example of simple format for each view control
        /// <Grid>
        //    <StackPanel x:Name="MainSP" Orientation="Vertical">
        //        <TextBlock Text = "{Binding id}" ></ TextBlock >
        //        < TextBlock Text="{Binding Title}"></TextBlock>
        //        <TextBlock Text = "{Binding Desc}" ></ TextBlock >
        //    </ StackPanel >
        //    < StackPanel x:Name="ChildrenSP" Orientation="Vertical"></StackPanel>
        //</Grid>
        
        
    }
    

    Wednesday, December 9, 2015 4:04 AM

Answers

  • Hi jbanderson,

    I've used view first, viewmodel first and all sorts of variations

    I prefer view first.

    .

    I've looked at your code and tried to work out where you're going with this.

    You seem to have view orientated usings there and:

            public UserControl BuildLessonAndChildViews()
            {
                var _LessonViewModel = LessonViewModel.ViewModelFactory(GetLesson(1));


    That's not viewmodel first.

    The way vm first would work is you define a datatemplate per viewmodel. This is translated into a view in the view by specifying which view to use with what viewmodel DataType See:

    https://msdn.microsoft.com/en-us/library/system.windows.datatemplate.datatype(v=vs.95).aspx

    In a resource dictionary you merge using app.xaml you would define a load of entries like this:

          <DataTemplate DataType="local:XXViewModel">
            <local:xxView>
          </DataTemplate>

    So there you are doing XX somewhere. You define a usercontrol which is XXView and you will be presenting XXViewModel to some view somehow. It then templates that viewmodel with what you tell it.

    You can bind the content of a contentcontrol to a property which gives it an instance of xxViewModel or you can define the content of an item in an itemscontrol.

    I would advise against doing both at once. Usually the template for an itemscontrol ( listbox, datagrid etc ) is specific to a view actually. You're more likely to just be switching one or the other out in a view rather than a parent view and all it's children.

    I hope that's clear.

    Come back if I lost you anywhere along the way there.


    Hope that helps.

    Technet articles: WPF: Layout Lab; All my Technet Articles

    • Proposed as answer by Weiwei Cai Thursday, December 17, 2015 10:35 AM
    • Marked as answer by Weiwei Cai Tuesday, December 22, 2015 1:59 AM
    Friday, December 11, 2015 10:25 AM

All replies

  • Hi,

    MVVM pattern is the view-model responsible of handling UI logic for a view, Another view-model is not something for the UI, but to solve a problem where you have nested user controls and you want to be able to set a different view-model for that user control. I would suggest you start the development of Catel to solved this type issue, refer to https://catelproject.atlassian.net/wiki/display/CTL/Introduction for more information about Catel.

    Regards

    Friday, December 11, 2015 5:27 AM
  • Hi jbanderson,

    I've used view first, viewmodel first and all sorts of variations

    I prefer view first.

    .

    I've looked at your code and tried to work out where you're going with this.

    You seem to have view orientated usings there and:

            public UserControl BuildLessonAndChildViews()
            {
                var _LessonViewModel = LessonViewModel.ViewModelFactory(GetLesson(1));


    That's not viewmodel first.

    The way vm first would work is you define a datatemplate per viewmodel. This is translated into a view in the view by specifying which view to use with what viewmodel DataType See:

    https://msdn.microsoft.com/en-us/library/system.windows.datatemplate.datatype(v=vs.95).aspx

    In a resource dictionary you merge using app.xaml you would define a load of entries like this:

          <DataTemplate DataType="local:XXViewModel">
            <local:xxView>
          </DataTemplate>

    So there you are doing XX somewhere. You define a usercontrol which is XXView and you will be presenting XXViewModel to some view somehow. It then templates that viewmodel with what you tell it.

    You can bind the content of a contentcontrol to a property which gives it an instance of xxViewModel or you can define the content of an item in an itemscontrol.

    I would advise against doing both at once. Usually the template for an itemscontrol ( listbox, datagrid etc ) is specific to a view actually. You're more likely to just be switching one or the other out in a view rather than a parent view and all it's children.

    I hope that's clear.

    Come back if I lost you anywhere along the way there.


    Hope that helps.

    Technet articles: WPF: Layout Lab; All my Technet Articles

    • Proposed as answer by Weiwei Cai Thursday, December 17, 2015 10:35 AM
    • Marked as answer by Weiwei Cai Tuesday, December 22, 2015 1:59 AM
    Friday, December 11, 2015 10:25 AM