views:

42

answers:

1

I've three models (Contacts, Notes, Reminders). I want to search all these and produce the filtered result in a single listview and depending upon the selection I've to display the corresponding view(UserControl) to its right.

I want the right way of implementing the design or atleast alternatives to this method that I've tried.

Now I've tried it using a IntegratedViewModel having all the properties from all the three models.

public class IntegratedViewModel
{
    ContactModel _contactModel;
    NoteModel _noteModel;

    public IntegratedViewModel(ContactModel contactModel)
    {
        _contactModel = contactModel;
    } // similarly for other models also

    public string DisplayTitle    // For displaying in ListView
    {             
        get
        { 
            If(_contactModel != null) 
                return _contactModel.Name; 
            If(_noteModel != null)
                return _noteModel.Title;
        }
    }

    //  All other properties from the three models includin the Name/Title properties for displaying them in the corresponding views(UserControl)
}

Now I set the itemsSource as the List<IntegratedViewModel>.

I've to now bind the visibility of the views to some properties in the MainViewModel. I tried setting bool properties like IsContactViewSelected, IsNoteViewSelected using the setter of SelectedEntity property which is bound to the ListView's SelectedItem.

public SelectedEntity
{
  //get
  set
  {
    oldvalue = _selectedEntity;
    _selectedEntity = value;
    // now i find the Type of model selected using `oldvalue.ModelType`
    // where ModelType is a property in the IntegratedViewModel
    // according to the type, i set one of the above bool properties to false
    // and do the same for _selectedEntity but set the property to true
    // so that the view corresponding to the selectedEntityType is visible 
    // and others are collapsed
  }
}

[[The visibility problem stated here was resolved]]

+1  A: 

I believe strongly in MVVM, but I don't believe in creating view models except when necessary. As long as your model objects properly support change notification and your view has no view state, you can use the model objects directly.

That is the case here. Your ListView can bind directly to the models with no mess, so that is the way I would go.

Here is what I would have written several years ago to solve this problem:

<ListView ItemsSource="{Binding AllSelections}">
  <ListView.View>
    <GridView>

      <!-- First column -->
      <GridViewColumn Header="Title" DisplayMemberBinding="{Binding}">
        <GridViewColumn.CellTemplate>
          <DataTemplate>
            <DataTemplate.Resources>

              <!-- First column content for ContactModel objects -->                  
              <DataTemplate DataType="{x:Type local:ContactModel}">
                <TextBlock Text="{Binding Name}" />
              </DataTemplate>

              <!-- First column content for NoteModel objects -->                  
              <DataTemplate DataType="{x:Type local:NoteModel}">
                <TextBlock Text="{Binding Title}" />
              </DataTemplate>

              ...

            </DataTemplate.Resources>

            <!-- This selects one of the above templates and applies it -->
            <ContentPresenter /> 

          </DataTemplate>
        </GridViewColumn.CellTemplate>
      </GridViewColumn>

      <!-- Second column -->
      <GridViewColumn ...>
        ...
      </GridViewColumn>

    </GridView>
  </ListView.View>
</ListView>

Where "AllSelections" is a property in your ViewModel that contains an ICollection that includes a mixture of ContactModel, NoteModel and ReminderModel objects and also implements INotifyCollectionChanged.

This way of implementing a view is quite clear and makes it easy to customize your presentation of the various object types.


Today I use a library I wrote called Emerald Data Foundation that makes it much easier to deal with switching the source property name based on the class:

<!-- First column -->
<GridViewColumn Header="Title"
                DisplayMemberBinding="{edf:ExpressionBinding
                  context is local:ContactModel ? Name : Title}" />

<!-- Second column -->
<GridViewColumn ... />

I hope to release my library sometime soon for others to use, or you could write your own. In the meantime the solution with multiple DataTemplates works and is much cleaner than creating a ViewModel and mirroring your properties in it.

Ray Burns
@Ray Burns: First of all, I would like to thank you for the solution especially two things 1)switching DataTemplate according to DataType 2)Implementing a common Interface. As AllSelection should be a mixture of models, I've created an interface for namesake, having just the ID property. The AllSelection is now well bound with the listview.Eventhough I know about both, we need somebody to prick us to use it in the correct situation. SO is great and so are you.
Veer