The ViewModel

In large applications with large models the UI will soon get filled with business logic that does not belong there. The logic is left there but the developer is someday going to return and clean it all up (Sure!).

Every developer knows that the degrees of freedom rapidly decrease once you fill your UI with business logic:

  1. You cannot easily reuse the logic placed in a UI so you copy it and increase the maintenance (BAD!)
  2. You forget about rules placed in UI so your system gets a life of its own (BAD!)
  3. Once you have logic in the UI you cannot be expected to know it all so you get afraid to make changes to your system because something will or may break (BAD!)
  4. You dare not give the UI to that brilliant designer because the designer will break logic for sure (BAD!)

Still I see business logic in the UI everywhere I go. When I ask about it I always get answers like: “Well this is not finished yet”, or “Well this is really special stuff, only used in one place”, or “Yea I know it sucks, I will fix that later”. But “Later” never comes, does it? It will always be just “Later”.

I am no superman for sure. I see business logic in UI I have written myself too.

If everyone or at least most of us is getting into these situations could it be that we are doing things the wrong way? Could it be that doing things the right way is just a tad bit too complicated?

These are some strategies to make developers do the correct thing and actually follow the coding guidelines:

  1. Automated review tools like FXCop
  2. Peer review (that usually will be done “later”)
  3. Some other strategy that will force violators (you and me) to mend our ways – even if the correct way is really complicated (maniac team leader with rabies foam in the face)
  4. Make it easier and faster to follow the “coding guidelines” then to be quick and dirty.

To no surprise I am in favor of making things easier. But to be able to make things easier we need to understand what cause things to go wrong in the first place: Why is the UI filling up with business logic? I think there are a couple of reasons, depending on how far you are from being model driven some of these reasons will apply:

  1. The UI will need to transform data (could be business logic) in order to display it according to specification.
  2. Actions performed in UI act on selections from UI-components, that has data in the transformed presentation format, and need to be transformed back (could be business logic) to model-scope in order to let the model work on the data (business logic). And you also need to check that the parameters sent to the model method are valid (could be business logic).
  3. The existing business logic may not match 100% what your action should do, you may want to call two, three of more methods to get the job done (new business logic) and you want to do some small checks based on the results of each method (could be business logic).
  4. Validation – your UI will do a lot of small checks to see that the data fulfills the overall rules for your application (business logic)

How do we make these reasons go away?

  1. We do it by offering a good and easy way to transform model-elements (or data if you will) into data elements suitable for render to match the specification.
  2. We do it by making it real easy to add business logic where it belongs (in the model), and make it easy to call it.
  3. And we offer a clean and easy way to add validation logic.

By setting the model in focus, and making it dirt simple to add methods, derived attributes and derived associations you can do everything you need for a specific UI in the model. This is sort of a good thing . The problem is that if you have a 100 UI’s your model will get filled with 100 times x derived attributes and derived links. And that does not sound like a good thing to me. It will eventually get hard to see the trees for the forest in a model like that.

And further more when a UI is dropped and deleted for some reason, will we remember to drop the derived associations and derived attributes we added to the model to accommodate it? Probably not.

A transformation layer between the UI and the Model can fix this. The transformation layer is allowed to have business logic, as long as it is unique for the context it works on. If the logic is not unique, it should go in the model ready to be re-used. We call this transformation Layer for a ViewModel.

A ViewModel transforms a piece of the model for a specific purpose. The purpose is often, but not always, to prepare the model information for interaction with a user for a specific use case – a UI. I said often but not always; it can also be for creating data transfer objects that are suitable for exposure for another application or system, or for some reporting engine or the like.

Why is it having the rules in a ViewModel is much better than having them in the UI? There are a lot of reasons:

  1. Testing; it is a good thing to be able to test the logic separated from the UI, because it is awkward and error prone to test and read the results back from the UI.
  2. ViewModel re-use ; you may have different UI’s for the exact same use case (beginner/advanced, Web-api/Rich client etc).
  3. Design time type checking; most UI-binding strategies rely on using strings that can only be checked at runtime (true for winforms, WPF, Silverlight and ASP.NET), whereas a good ViewModel is type checked at design or compile time.
  4. The designer working on the UI can harm important logic if the logic is in the UI.
  5. If we have dedicated designers we will not want to wait for them to release a UI file in order to fix business logic within.
  6. The UI may be on the other side of a network (another physical tier) so the UI cannot have access to the domain layer tier
  7. UI and logic have very different motivators and hence will often change for different reasons (looking good versus working correctly), mixing them up add confusion regarding the distinction between these important aspects.
  8. Security, designer that get access to the ViewModel cannot go beyond the ViewModel and unintentionally expose information that should not get exposed in the use case at hand.

The thing is that you do not have to use a ViewModel pattern to create a great application, it is just that is a good idea to use the ViewModel pattern when building applications that are going to be worked on for a long time, released in several versions over several iterations, and will most likely see different developers and UI-designer, and may very well be radically changed with regards to presentation framework during its lifespan. In short – it is always a good idea for successful applications to use a ViewModel pattern.

The declarative ViewModel

Presented with a model that I got from Rick Weyrauch, that incidentally helps out as a Hockey Referee when he is not coding, I wanted to create a ViewModel for the use-case “Set up new Hockey game”.

View Model - 1.png

The Game class has a state machine:

View Model - 2.png

I took a piece of paper and draw the UI I wanted to achieve:

View Model - 3.png

Now I know what the requirements are on the ViewModel since I can see from the drawing what data the ViewModel needs to hold.

And then created this ViewModel That I named GameSetup:

View Model - 4.png

Notice that it is just a series of named ocl expressions. Some expressions are nested to other list definitions like Home_PickList that states that if the Game has a picked GameType, then we know what teams that can be picked – namely those teams that are associated to that GameType

I created some test data so that UI can show something:

Code snippet

private Game CreateSomeTestData() {
  
 // Game types
   var gtboys15 = new GameType(_es) { Name = "15 years, Boys" };
   var gtboys16 = new GameType(_es) { Name = "16 years, Boys" };
   var gtgirls15 = new GameType(_es) { Name = "15 years, Girls" };

   // team types
   var ttb15=new TeamType(_es) { Name = "Boys 15 years" };
   var ttb16 = new TeamType(_es) { Name = "Boys 16 years" };
   var ttg15 = new TeamType(_es) { Name = "Girls 15 years" };

   // Valid team-game combinations
   gtgirls15.TeamTypes.Add(ttg15);

   gtboys15.TeamTypes.Add(ttb15);
   gtboys15.TeamTypes.Add(ttg15); // girls can play in boys 15 year
   gtboys16.TeamTypes.Add(ttb15); // 15 year boys can enter 16 year games
   gtboys16.TeamTypes.Add(ttb16);
   gtboys16.TeamTypes.Add(ttg15); // girls can play in boys 16 year



   new Team(_es) { Name = "Brynäs",Image=GetImage(imagebrynäs), TeamType=ttb15 };
   new Team(_es) { Name = "Brynäs", Image = GetImage(imagebrynäs), TeamType = ttb16 };
   new Team(_es) { Name = "Brynäs", Image = GetImage(imagebrynäs), TeamType = ttg15 };

   new Team(_es) { Name = "Luleå", Image = GetImage(imageluleå), TeamType = ttb15 };
   new Team(_es) { Name = "Luleå", Image = GetImage(imageluleå), TeamType = ttb16 };
   new Team(_es) { Name = "Luleå", Image = GetImage(imageluleå), TeamType = ttg15 };

   new Team(_es) { Name = "Djurgården", Image = GetImage(imagedjurgården), TeamType = ttb15  };
   new Team(_es) { Name = "Djurgården", Image = GetImage(imagedjurgården), TeamType = ttb16 };
   new Team(_es) { Name = "Djurgården", Image = GetImage(imagedjurgården), TeamType = ttg15 };

   return new Game(_es)
   {
       ScheduledDate = DateTime.Now
   };


Now I am ready to build the UI.

Code Snippet

<Window x:Class="WPFBinding.Window2"   
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:eco="clr-namespace:Eco.WPF;assembly=Eco.WPF"
    xmlns:ecoVM="clr-namespace:Eco.ViewModel.WPF;assembly=Eco.WPF"
    xmlns:Controls="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit"
    xmlns:local="clr-namespace:WPFBinding"
    xmlns:ecospace="clr-namespace:WPFBinding;assembly=WPFBinding.EcoSpace"
    Title="Window2" Height="300" Width="500" >
    <Window.Resources>
        <ecoVM:ViewModelContent x:Key="VM1" ViewModelName="GameSetup" EcoSpaceType="{x:Type ecospace:WPFBindingEcoSpace}" ></ecoVM:ViewModelContent>
        <local:ImageBlobConverter x:Key="ImageBlobConverter"/>
    </Window.Resources>
    <Grid>
        <Grid Name="vmrootStackPanel" DataContext="{StaticResource VM1}">
            <Grid.ColumnDefinitions>
                <ColumnDefinition></ColumnDefinition>
                <ColumnDefinition></ColumnDefinition>
                <ColumnDefinition Width="50"></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition></RowDefinition>
                <RowDefinition></RowDefinition>
                <RowDefinition></RowDefinition>
                <RowDefinition></RowDefinition>
                <RowDefinition></RowDefinition>
                <RowDefinition></RowDefinition>
                <RowDefinition></RowDefinition>
                <RowDefinition></RowDefinition>
            </Grid.RowDefinitions>
 
            <TextBlock Grid.Row="0" Grid.Column="0" Text="GAME : " HorizontalAlignment="Right" ></TextBlock>
            <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding  Path=Class[GameSetup]/Presentation,Mode=OneWay}"></TextBox>
 
            <TextBlock Grid.Row="1" Grid.Column="0" Text="Type of game : " HorizontalAlignment="Right" ></TextBlock>
            <ComboBox Grid.Row="1" Grid.Column="1"  DisplayMemberPath="Name"  ItemsSource="{Binding Path=Class[GameType_PickListPresentation]}" SelectedValuePath="self"  SelectedValue="{Binding Path=Class[GameSetup]/GameType}"  ></ComboBox>
 
            <TextBlock Grid.Row="2" Grid.Column="0" Text="Home team : " HorizontalAlignment="Right" ></TextBlock>
            <ComboBox Grid.Row="2" Grid.Column="1"  DisplayMemberPath="Name"  ItemsSource="{Binding Path=Class[Home_PickListPresentation]}" SelectedValuePath="self"  SelectedValue="{Binding Path=Class[GameSetup]/Home}"></ComboBox>
            <Image Grid.Row="2" Grid.Column="2"  Source="{Binding Path=Class[GameSetup]/Home_Image,Mode=OneWay,Converter={StaticResource ImageBlobConverter} }"></Image>
 
 
            <TextBlock Grid.Row="3" Grid.Column="0" Text="Visitor team : " HorizontalAlignment="Right" ></TextBlock>
            <ComboBox Grid.Row="3" Grid.Column="1"  DisplayMemberPath="Name"  ItemsSource="{Binding Path=Class[Visitor_PickListPresentation]}" SelectedValuePath="self"  SelectedValue="{Binding Path=Class[GameSetup]/Visitor}"></ComboBox>
            <Image Grid.Row="3" Grid.Column="2"  Source="{Binding Path=Class[GameSetup]/Visitor_Image,Mode=OneWay,Converter={StaticResource ImageBlobConverter} }"></Image>
 
            <TextBlock Grid.Row="4" Grid.Column="0" Text="Scheduled date : " HorizontalAlignment="Right" ></TextBlock>
            <Controls:DatePicker Grid.Row="4" Grid.Column="1"  ></Controls:DatePicker><Button Grid.Row="5" Grid.Column="0" IsEnabled="{Binding Path=Class[GameSetup]/CanStartGame}" Click="ButtonStartGame_Click">

              <TextBlock Text="Start Game"></TextBlock>
            </Button>
            <Button  Grid.Row="5" Grid.Column="1"  IsEnabled="{Binding Path=Class[GameSetup]/CanEndGame}" >

<TextBlock Text="End Game" ></TextBlock> <nowiki></Button>
 </Grid>
        <StackPanel Orientation="Horizontal" VerticalAlignment="Bottom">
           <Image Height="50" Name="imagebrynäs" Stretch="Fill" Width="50" Source="/WPFBinding;component/brynäs.jpg" />
           <Image Height="50" Name="imagedjurgården" Stretch="Fill" Width="50" Source="/WPFBinding;component/djurgården.jpg" />
           <Image Height="50" Name="imageluleå" Stretch="Fill" Width="50" Source="/WPFBinding;component/Luleå.jpg" />
       </StackPanel>
   </Grid>
</Window>


What happens in the xaml above is that we have a ViewModelContent component in the resource section. We initiate it to the name of the ViewModel, and we also provide the Type of the ecospace.

The ViewModelContent object, now keyed as VM1, is put in the DataContext of the Grid where I place the UI components. If a WPF Binding does not get an explicit source it will use whatever it finds in the DataContext (the datacontext is propagated down the logical tree, so for us it is everywhere).

In code behind we hook up the EcoSpace and the rootobject-property (dependencyproperty so that you bind as target and as source) to our demo Game object:

Code Snippet

(Resources["VM1"] as Eco.ViewModel.WPF.ViewModelContent).SetEcoSpace(_es);
(Resources["VM1"] asEco.ViewModel.WPF.ViewModelContent).RootObject=CreateSomeTestData();

The UI looks like this:

View Model - 5.png

Yes it looks bad; but hey, you can hand it to any WPF savvy designer in the world –  the data and the rules are safe in the ViewModel.

It already shows some of the good effects of separating UI from logic. If you run the sample you will see and hopefully appreciate that:

  1. The PickLists for Home and Visitor are filtered based on Type of Game
  2. The Picklist for Home team filters away the Visitor team if set (and vice versa)
  3. Start game is enabled only after both home and visitor are set
  4. The End game button is disabled until the Game is started

These are some examples of business logic that would have easily ended up in the UI if we did not have a good place to define it.

Taking it further still

If the cost of creating and maintaining a ViewModel is high fewer ViewModels will be created. So our mission is to reduce the cost of creating and maintaining them. Can we do more? I will argue that we can.

WPF is a declarative way to describe the UI. This mean that the same basic lookless components like TextBlock, TextBox, CheckBox, Combobox and Image etc will be used again and again and they will be given a look by an external style or template.

What if we use this fact and provide some basic rendering/placing hints for the ViewModel columns? We could then use those clues to spill out the correct lookless control in the intended relative position so we would not need to mess about with xaml every 5 minutes… I am excited… Xaml is a bit too scripty for my old strongly typed ways…

This is what the ViewModel-Editor looks like without rendering hints:

View Model - 6.png

And this is the way it looks when I have checked the “Use Placing Hints” checkbox:

View Model - 7.png

Given the extra fields for “Presentation”, “Column”,”Row”,”Span” etc I can work the ViewModel – preview to look like this:

View Model - 8.png

Now I really need to stress this so that I do not get misunderstood: We do not mix presentation with UI, we do however allow for adding optional placing hints or clues on what you have in mind while designing the ViewModel.

Having a ViewModel with placing hints, you can add a ViewModelWPFUserControl  to your form with just one row:

<ecoVM:ViewModelWPFUserControl Grid.Row=”2″ x:Name=”VMU1″

EcoSpaceType=”{x:Type ecospace:WPFBindingEcoSpace}”

ViewModelName=”GameSetup” ></ecoVM:ViewModelWPFUserControl>

And the result is:

View Model - 9.png

And remember that these auto layouted controls also adheres to external set styles. (Notice the datetime picker is gone? Datetime picker is in WPFToolkit so we do not use it by default. Implement ViewModelWPFUserControl.OnColumnUIOverride to add your own components to its layout engine.)

Summing it up

This has been a brief overview of the ViewModel concept. Things intentionally left out for now was Actions, Validation-rules, Variables, Master-detail (since there were no details in the sample ), Style references and Tab order.

I have written about the benefits of having a ViewModel in the first place. Then I wrote about the Modlr approach with a strictly declarative ViewModel. We looked at the sample using such a ViewModel and some of the effects it gave in separating logic from UI. Then I showed you a ViewModel with placing hints – a bit un-orthodox for sure, but efficient and easy to maintain.

Thanks goes out to everyone that has been involved in the Modlr ViewModel approach by giving feedback, and to Rick for providing the sample model – Hope I did not abuse it.

This page was edited 74 days ago on 02/13/2024. What links here