2

Perhaps I am not even thinking about this in the right way so I am open to suggestions, but I would like to use exactly the same view for both creating and editing. I don't want to say create/edit an entity because it's more complicated than that. I have nested views (using Catel to achieve that) so each nested view has it's own viewmodel as well. However the internal state and commands executed are different depending on whether you are creating or editing (both in the parent view and in the nested views). Two ideas that immediately come to mind are:

1) Have a strategy patterned viewmodel, where the operations to execute (create related, or edit related) are passed in along with the message that activates the view.

2) Somehow have 2 viewmodels that can be associated with the same view and a way to switch between them.

Suggestions?

Anatoliy Nikolaev
  • 22,370
  • 15
  • 69
  • 68
Bitfiddler
  • 3,942
  • 7
  • 36
  • 51

2 Answers2

3

MVVM pattern lets you have several ViewModels for a single View. Usually it refers to the View, which can have multiple actions / states. Switching between ViewModels can be "manually" or by using different patterns, for example by using Strategy.

Below I will discuss both principles.

Using the strategy pattern

This pattern allows you to have a set of classes that implement algorithms for a specific strategy. A simple example: you need to get from home to work (place of study, etc.). This can be done in several ways:

  • Go on foot
  • Ride a bike
  • Travel by car / bus

All of this can be attributed to a single strategy, which includes several ways to solve a specific task. Allow a quote from the book "Gang of Four" on the applicability of the strategy pattern:

Use the Strategy pattern when:

  • many related classes differ only in their behavior. Strategies provide a way to configure a class with one of many behaviors.

  • you need different variants of an algorithm. For example, you might define algorithms reflecting different space/time trade-offs. Strategies can be used when these variants are implemented as a class hierarchy of algorithms.

  • an algorithm uses data that clients shouldn't know about. Use the Strategy pattern to avoid exposing complex, algorithm-specific data structures.

  • a class defines many behaviors, and these appear as multiple conditional statements in its operations. Instead of many conditionals, move related conditional branches into their own Strategy class.

Structure diagram:

enter image description here

Conclusion for Strategy pattern

If you are using multiple operations (Create Remove Update Delete) for different types, for example: add an image, add user information, add the file, which can also have subtypes, then I think the strategy is suitable for you.

Using several ViewModels for one View

As I mentioned above, MVVM pattern allows several ViewModels for one View. In my opinion, this is best done using a DataTemplate, and selector DataTemplateSelector, which will return the required template by its condition. DataTemplate ideal for visual presenation of ViewModel. Personally, I use dynamic DataTemplateSelector, example:

<ContentControl Name="DynamicContentRightPanel"                            
                Style="{StaticResource ContentControlRightPanelStyle}"
                Content="{Binding Path=ContentRightPanelModel.ContentType,
                                  Mode=TwoWay,
                                  UpdateSourceTrigger=PropertyChanged}" />

In the panel may have different content, which depends on the user selection.

ContentControlRightPanelStyle

<Style x:Key="ContentControlRightPanelStyle" TargetType="{x:Type ContentControl}">
    <Setter Property="ContentTemplateSelector" Value="{StaticResource MyTemplateSelector}" />                

    <Setter Property="DataTemplateSelectors:DynamicTemplateSelector.Templates">
        <Setter.Value>
            <DataTemplateSelectors:TemplateCollection>
                <DataTemplateSelectors:Template Value="DateCalculator" 
                                                DataTemplate="{StaticResource DateCalcTemplate}" />

                <DataTemplateSelectors:Template Value="Test" 
                                                DataTemplate="{StaticResource TestTemplate}" />
            </DataTemplateSelectors:TemplateCollection>
        </Setter.Value>
    </Setter>
</Style>    

DynamicTemplateSelector (taken and little reworked from CodeProject)

public class DynamicTemplateSelector : DataTemplateSelector
{
    #region Templates Dependency Property

    public static readonly DependencyProperty TemplatesProperty =
        DependencyProperty.RegisterAttached("Templates", typeof(TemplateCollection), typeof(DataTemplateSelector),
        new FrameworkPropertyMetadata(new TemplateCollection(), FrameworkPropertyMetadataOptions.Inherits));

    public static TemplateCollection GetTemplates(UIElement element)
    {
        return (TemplateCollection)element.GetValue(TemplatesProperty);
    }

    public static void SetTemplates(UIElement element, TemplateCollection collection)
    {
        element.SetValue(TemplatesProperty, collection);
    }

    #endregion

    #region SelectTemplate

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        string myStringItem = (string)item;

        if (!(container is UIElement))
        {
            return base.SelectTemplate(item, container);
        }

        TemplateCollection templates = GetTemplates(container as UIElement);

        if (templates == null || templates.Count == 0)
        {
            base.SelectTemplate(item, container);
        }

        foreach (var template in templates)
        {
            if (myStringItem.Equals(template.Value.ToString()))
            {
                return template.DataTemplate;
            }
        }

        return base.SelectTemplate(item, container);
    }

    #endregion
}

#region TemplateCollection

public class TemplateCollection : List<Template>
{

}

#endregion

#region Template Dependency Object

public class Template : DependencyObject
{
    public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(string), typeof(Template));

    public static readonly DependencyProperty DataTemplateProperty =
       DependencyProperty.Register("DataTemplate", typeof(DataTemplate), typeof(Template));

    public string Value
    { get { return (string)GetValue(ValueProperty); } set { SetValue(ValueProperty, value); } }

    public DataTemplate DataTemplate
    { get { return (DataTemplate)GetValue(DataTemplateProperty); } set { SetValue(DataTemplateProperty, value); } }
}

#endregion

Conclusion for several ViewModels / one main View

If the user's choice is limited to one type of operation, which may be several, you can simply create for each ViewModel / View (in the case of the DataTemplate), since it will be easier and more convenient.

Anatoliy Nikolaev
  • 22,370
  • 15
  • 69
  • 68
  • Although this a very well structured answer and a very cool implementation (which I will file in the back of my mind), it is much more complicated than what I am needing to do. I still think this will be valuable to other people so I have upvoted it. I don't have different datatypes needing different templates, I essentially have view with related entities displayed in sub-views. I only need the commands being executed and triggered events to be different depending on whether you are editing or creating. In any event thank you for your answer. – Bitfiddler Jan 14 '14 at 21:42
  • @BitFiddler: I basically understood it, so I wrote in which case you need to use a pattern, and in which you can apply `One View-N ViewModels`. As I understand it in your question, you asked for advice, instead of specific solutions. `DataTemplateSelector` is just an example of how you can apply multiple `ViewModels`, and how it can be convenient and simple solution for dynamic content of `View`. If you need a specific solution, please edit your question, which would contain new questions. – Anatoliy Nikolaev Jan 15 '14 at 06:57
0

I would try to re-use as much logic as possible where option 1 gives you the most options. You can even think of this scenario:

public void PersonViewModel()
{
    // Will be used when no model is available - create mode
    public PersonViewModel()
        : this(null) { }

    // Will be used when a model is available - edit mode
    public PersonViewModel(IPerson person) 
    { 
        // Here you check if you have a person object, if so, edit 
    }
}
Geert van Horrik
  • 5,689
  • 1
  • 18
  • 32
  • I am passing in a UnitOfWork factory to all of my models. The unit of work has the ability to fetch a repository (not sure if this is the right way to do things) for any of my entity types. What this means is that my viewmodel responds to a 'create' message or an 'edit' message. The body of the create message is empty (the model uses the an injected factory to create new instances with certain required defaults) and the body of the edit contains a reference to the entity in need of changes. Ideally, I'd like all the commands in the viewmodel to change depending on the incoming message. – Bitfiddler Jan 14 '14 at 21:47
  • What I'm doing now is creating a 'CommandSet' interface. Two implementations, one for edit one for create. This commandset would be sent as part of create/edit message, that way the viewmodel only needs to receive the message and store the commandset, invoking the commands it receives upon itself (Visitor pattern where the viewmodel 'visits' the command). This might be crazy but I'll see if it does the job without getting to hairy. – Bitfiddler Jan 14 '14 at 21:51