1
votes

What is the best way of using the same viewmodel to support multiple views?

(Yes..I have read "MVVM (with WPF)-Binding Multiple Views to the SameViewModel", but if I read it correctly, the answer involved creating different viewmodels which is not what I want to do here).

In the below code the StringViewModel supports the InkStringView when the InkStringView consists of defined rows. I wish now to define a second view that consists of columns but must keep the same datacontext of StringViewModel. In the second view, the controls have different positions and sizes that the StringViewModel calculates but thier function and purpose remain the same. It would be ideal if the module that creates the StringViewModels in Strings, an observablecollection, does not know what view will be used leaving the final decision to the xaml of the usercontrol.

My question is how to design either the StringViewModel and/or the DataTemplate to allow for different views and calculations based on that view by changing only the DataTemplate.

(I tried inheriting the StringViewModel to different viewmodels, each viewmodel specific to its view, but it did not work).

Thanks in advance for any help or suggestions. Or is their a better way?

Example:

<DataTemplate DataType="{x:Type vm:StringViewModel}">
               <v:InkStringView_2 />          <----CHANGING THE VIEW TO COLUMNS. ViewModel needs
</DataTemplate>                                    to perform calculations specific to the view.

The usercontrol is:

<UserControl.Resources>
       <DataTemplate DataType="{x:Type vm:StringViewModel}">
               <v:InkStringView />
       </DataTemplate>
   </UserControl.Resources>

<Grid>
     <ItemsControl 
        ItemsSource="{Binding Strings}" >
        <ItemsControl.ItemContainerStyle>
            <Style>
                <Setter Property="Control.Margin" Value="{Binding Margin}"/>
            </Style>
        </ItemsControl.ItemContainerStyle>
    </ItemsControl>      
</Grid>
</UserControl>
3
Why did the inheriting not work?Roel van Westerop
@RoelvanWesterop Strings is ObservableCollection<StringViewModel>. Using children of StringViewModel did not fit into the Strings collection....I think?? Additionally, this would require the module that built the string collection to know what view is intended, which is not really what I wanted to do.Alan Wayne

3 Answers

1
votes

just a suggestion:

you have a viewmodel MyViewmodel which you wanna display with different DataTemplates. then for me a easy way would be to create "new" Classes with "no" implementation

 public class MyT1 : MyViewmodel {}
 public class MyT2 : MyViewmodel {}
 public class MyT3 : MyViewmodel {}

so now all MyT1, MyT2, MyT3 have the same methods and stuff, but you can create a datatemplate foreach of them

   <DataTemplate DataType="{x:Type vm:MyT1}">
           <v:T1View />
   </DataTemplate>
   <DataTemplate DataType="{x:Type vm:MyT2}">
           <v:T2View />
   </DataTemplate>
   <DataTemplate DataType="{x:Type vm:MyT3}">
           <v:T3View />
   </DataTemplate>

or you dont go the Viewmodel First approach and do View First and then you can choose the view you want with your Datacontext

1
votes

I've had a similar problem, the way I've tackled it is by using ControlTemplate so:
In your View:

<Control>
    <Control.Resources>
         <Style TargetType="Control">
              <Setter Property="Template">
                   <Setter.Value>
                        <ControlTemplate TargetType="Control">
                              <!-- Here you put you view it could be a UC if you want -->      
                         </ControlTemplate>
                   </Setter.Value>
              </Setter>
         </Style
    </Control.Resources>
</Control>  

Then in your Style.Resources you can define Triggers or DataTriggers to change the Template property of your Control. This way you keep the ViewModel as it is and you just change the Views that are getting the data. Hope this makes sense, if anything give us a shout and I'll put more info in.
HTH

0
votes

Not an answer, but a simple solution.

Redefine Strings, the ItemsSource, as

ObservableCollection<StringViewModelBase> Strings

then create children of StringViewModelBase as

public class StringByRowViewModel : StringViewModelBase

then change the DataTemplate in the resources to be

  <DataTemplate DataType="{x:Type vm:StringByRowViewModel}">
        <v:InkStringByRowView />
  </DataTemplate>
  <DataTemplate DataType="{x:Type vm:StringByColumnViewModel}" >
        <v:InkStringByColumnView />
  </DataTemplate>

and lastly, I see no choice but to change the module that builds the Strings (at least as far as the viewmodel goes). Instead of

 StringViewModelBase svm = new StringViewModelBase(text,color, w);  

use

 StringByColumnViewModel svm = new StringByColumnViewModel(text,color,w);

with

   Strings.Add(svm);

Apparently, the ObservableCollection only needs a common parent and the Data Type is still preservered in the XAML.