Sunday, January 17, 2010

Using Blend with ECO

Blend is the XAML-high-level tool. It is very powerful if you know what you are doing. Having that special designer eye that helps you to stop messing with something when it is done is probably a very useful skill when working with Blend – I do not have it, yet. But still here is my 5 minutes crash course into Microsoft Blend for ECO developers.

First: Blend is all about UI. Nothing about data. You can bind the UI to a datasource, but you cannot do anything else useful with the datasource in Blend – just use it.

Second: XAML is all about binding – in that sense it is very declarative. You do not do any imperative coding in Blend (ideally). Instead you declare rules that makes things happen for you; i.e. you declare bindings and animations.

I continued on the WPF-Hockey sample I wrote about earlier. To do anything cool in Blend you need to have an idea on what you want (that statement is sort of generic) – if that idea includes data then ECO is your best friend.

I find it useful to declare the processes in ECO, it helps me to keep my objectives clear and focused:

image

The SetUpGame thing I did in the previous post. Now I am going to do the “Show teams and players”.

I added LineUp and Player to the model:

image

And a view model that supports this model:

image

That was the easy part. The above steps are done quickly with Enterprise core objects. Good! More time left to spend in Blend (and boy will you need it!).

Start up Blend (I use Microsoft Expression Blend 3). File, Open, Project/Solution:

image

You can add UserControls just as you would have done in Visual studio:

image

So I did a user control looking like a hockey rink (took me forever!) ; that will come in handy. Doing stuff like this in Blend is just as complicated as in any other competent drawing tool (Photoshop or what have you). But Blend has an advantage with the split xaml view. You can fine tune stuff just by changing the xaml.

I then create a new WPF-Window and add my user control:

image

I add some comboboxes to show the lineups for each team. I also add some textblocks to show the players for each LineUp. I also add two images up on the ice, to show the team images. All of these controls should show data. So I need to bind…

   1: //This is how I bind the Image to my ViewModel
   2: <Image  x:Name="imageHome" Margin="153,111,224.54,0" 
   3:     Source="{Binding Path=Class[GameDay]/Home_Image,Mode=OneWay,Converter={StaticResource ImageBlobConverter} }" 
   4:     Stretch="Fill" RenderTransformOrigin="0.5,0.5" VerticalAlignment="Top" Height="107">
   5:  
   6: //This is how I bind one of the textblocks
   7: <TextBlock  x:Name="textBlock" 
   8:         HorizontalAlignment="Left" VerticalAlignment="Bottom" 
   9:     Text="{Binding Path=Class[LineUp]/CenterForward_Name}" 
  10:    TextWrapping="Wrap" Margin="76.088,0,0,88.08" RenderTransformOrigin="0.515,0.055" Style="{DynamicResource playerstyle}">
  11:  
  12: // And the comboboxes
  13: <ComboBox DisplayMemberPath="Name" 
  14:     IsSynchronizedWithCurrentItem="True" 
  15:     ItemsSource="{Binding Path=Class[LineUp]}"  
  16:     Height="23" Margin="225,0,140,110" Name="comboBox1" VerticalAlignment="Bottom" />

Ok, now I have said what I want to bind to, that is not enough, I need to get the data there as well.

   1: // Add a ViewModelContent component to the resource section, tell it what your ViewModel is called, and what type your EcoSpace has:
   2: <Window.Resources>    
   3:     <ecoVM:ViewModelContent x:Key="VM1" ViewModelName="GameDay" EcoSpaceType="{x:Type ecospace:HockeyContEcoSpace}" ></ecoVM:ViewModelContent>

I now switch back to Visual Studio to actually put a “Game” into the GameDay ViewModel:

   1: {
   2:     public WindowGameDay()
   3:     {
   4:         this.InitializeComponent();
   5:  
   6:         // Insert code required on object creation below this point.
   7:     }
   8:  
   9:     private EcoSpace _es;
  10:     internal void SetEcoSpace(EcoSpace es, Game aGame)
  11:     {
  12:         _es = es;
  13:         (Resources["VM1"] as Eco.ViewModel.WPF.ViewModelContent).SetEcoSpace(_es);
  14:         (Resources["VM1"] as Eco.ViewModel.WPF.ViewModelContent).RootObject = aGame;
  15:         (Resources["VM1"] as Eco.ViewModel.WPF.ViewModelContent).GetCollectionView("LineUp").MoveCurrentToFirst();
  16:         (Resources["VM1"] as Eco.ViewModel.WPF.ViewModelContent).GetCollectionView("LineUpVisitor").MoveCurrentToFirst();
  17:         LayoutRoot.DataContext = Resources["VM1"];
  18:  
  19:     }

Notice that I expect to be handed a EcoSpace and a Game for this window to display. Also I use the method GetCollectionView of the ViewModel so that I can position the LineUp combos to show the first line up when we start.

TestRun:

image

Ok. Now to Animations. This is how animations work: They change a value over time, from an initial value to a desired value. Animations can be grouped together into a Storyboard.

Switching back to Blend. Go to menu Window, Workspaces, Animation:

image

Now your Blend workspace switch and you will see this pane at the bottom of your screen:

image

Use the plus sign (I marked it with red) to add a new storyboard.

PAY ATTENTION TO THE LITTLE DOT MARKED WITH GREEN!!!! When this is red – recording is on, click it to turn off recording (it toggles, so you can start and stop).

When Recording is on: WHEN YOU MOVE AROUND CONTROLS, THE NEW POSITION IS THE DESIRED POSITION FOR THE TIME INDICATED BY THE YELLOW MARKER.

Animation will make a linear move from start to goal over time (you can control this if linear is not what you want).

So this is how you go about animating the Image Opacity from 100% to 0% in 3 seconds:
- Turn recording on
- Move the Yellow time line to zero
- Set Image Opacity to 100% (you use the standard panes to set the values)
- Move the yellow line to 3
- Set Image Opacity to 0%
- Turn Animation off.

You repeat this process for all the stuff you want to move around. This is a very powerful tool and you have every chance to destroy all the work you have done so far – but it is sort of fun.

The sample now animates the Players for the selection LineUp up onto the Ice, while the team images fade out and disappear:

image

And when you change the Line up, the names change no matter what the current position of the animation is.

I was about to create a video to show the animation, but I went for a ClickOnce install instead:

http://www.capableobjects.com/publishedsamples/Hockey/publish.htm

Define a valid Game, then click Start…

Monday, January 4, 2010

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”.

image

The Game class has a state machine:

image

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

image

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:

image

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:

   1: private Game CreateSomeTestData()
   2: {
   3:     // Game types
   4:     var gtboys15 = new GameType(_es) { Name = "15 years, Boys" };
   5:     var gtboys16 = new GameType(_es) { Name = "16 years, Boys" };
   6:     var gtgirls15 = new GameType(_es) { Name = "15 years, Girls" };
   7:  
   8:     // team types
   9:     var ttb15=new TeamType(_es) { Name = "Boys 15 years" };
  10:     var ttb16 = new TeamType(_es) { Name = "Boys 16 years" };
  11:     var ttg15 = new TeamType(_es) { Name = "Girls 15 years" };
  12:  
  13:     // Valid team-game combinations
  14:     gtgirls15.TeamTypes.Add(ttg15);
  15:  
  16:     gtboys15.TeamTypes.Add(ttb15);
  17:     gtboys15.TeamTypes.Add(ttg15); // girls can play in boys 15 year
  18:     gtboys16.TeamTypes.Add(ttb15); // 15 year boys can enter 16 year games 
  19:     gtboys16.TeamTypes.Add(ttb16);
  20:     gtboys16.TeamTypes.Add(ttg15); // girls can play in boys 16 year
  21:  
  22:  
  23:  
  24:     new Team(_es) { Name = "Brynäs",Image=GetImage(imagebrynäs), TeamType=ttb15 };
  25:     new Team(_es) { Name = "Brynäs", Image = GetImage(imagebrynäs), TeamType = ttb16 };
  26:     new Team(_es) { Name = "Brynäs", Image = GetImage(imagebrynäs), TeamType = ttg15 };
  27:  
  28:     new Team(_es) { Name = "Luleå", Image = GetImage(imageluleå), TeamType = ttb15 };
  29:     new Team(_es) { Name = "Luleå", Image = GetImage(imageluleå), TeamType = ttb16 };
  30:     new Team(_es) { Name = "Luleå", Image = GetImage(imageluleå), TeamType = ttg15 };
  31:  
  32:     new Team(_es) { Name = "Djurgården", Image = GetImage(imagedjurgården), TeamType = ttb15  };
  33:     new Team(_es) { Name = "Djurgården", Image = GetImage(imagedjurgården), TeamType = ttb16 };
  34:     new Team(_es) { Name = "Djurgården", Image = GetImage(imagedjurgården), TeamType = ttg15 };
  35:  
  36:     return new Game(_es)
  37:     {
  38:         ScheduledDate = DateTime.Now
  39:     };
  40:  
  41: }

Now I am ready to build the UI.

   1: <Window x:Class="WPFBinding.Window2"
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     xmlns:eco="clr-namespace:Eco.WPF;assembly=Eco.WPF"
   5:     xmlns:ecoVM="clr-namespace:Eco.ViewModel.WPF;assembly=Eco.WPF"
   6:     xmlns:Controls="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit"
   7:     xmlns:local="clr-namespace:WPFBinding"
   8:     xmlns:ecospace="clr-namespace:WPFBinding;assembly=WPFBinding.EcoSpace"
   9:     Title="Window2" Height="300" Width="500" >
  10:     <Window.Resources>
  11:         <ecoVM:ViewModelContent x:Key="VM1" ViewModelName="GameSetup" EcoSpaceType="{x:Type ecospace:WPFBindingEcoSpace}" ></ecoVM:ViewModelContent>
  12:         <local:ImageBlobConverter x:Key="ImageBlobConverter"/>
  13:     </Window.Resources>
  14:     <Grid>
  15:         <Grid Name="vmrootStackPanel" DataContext="{StaticResource VM1}">
  16:             <Grid.ColumnDefinitions>
  17:                 <ColumnDefinition></ColumnDefinition>
  18:                 <ColumnDefinition></ColumnDefinition>
  19:                 <ColumnDefinition Width="50"></ColumnDefinition>
  20:             </Grid.ColumnDefinitions>
  21:             <Grid.RowDefinitions>
  22:                 <RowDefinition></RowDefinition>
  23:                 <RowDefinition></RowDefinition>
  24:                 <RowDefinition></RowDefinition>
  25:                 <RowDefinition></RowDefinition>
  26:                 <RowDefinition></RowDefinition>
  27:                 <RowDefinition></RowDefinition>
  28:                 <RowDefinition></RowDefinition>
  29:                 <RowDefinition></RowDefinition>
  30:             </Grid.RowDefinitions>
  31:  
  32:             <TextBlock Grid.Row="0" Grid.Column="0" Text="GAME : " HorizontalAlignment="Right" ></TextBlock>
  33:             <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding  Path=Class[GameSetup]/Presentation,Mode=OneWay}"></TextBox>
  34:  
  35:             <TextBlock Grid.Row="1" Grid.Column="0" Text="Type of game : " HorizontalAlignment="Right" ></TextBlock>
  36:             <ComboBox Grid.Row="1" Grid.Column="1"  DisplayMemberPath="Name"  ItemsSource="{Binding Path=Class[GameType_PickListPresentation]}" SelectedValuePath="self"  SelectedValue="{Binding Path=Class[GameSetup]/GameType}"  ></ComboBox>
  37:  
  38:             <TextBlock Grid.Row="2" Grid.Column="0" Text="Home team : " HorizontalAlignment="Right" ></TextBlock>
  39:             <ComboBox Grid.Row="2" Grid.Column="1"  DisplayMemberPath="Name"  ItemsSource="{Binding Path=Class[Home_PickListPresentation]}" SelectedValuePath="self"  SelectedValue="{Binding Path=Class[GameSetup]/Home}"></ComboBox>
  40:             <Image Grid.Row="2" Grid.Column="2"  Source="{Binding Path=Class[GameSetup]/Home_Image,Mode=OneWay,Converter={StaticResource ImageBlobConverter} }"></Image>
  41:  
  42:  
  43:             <TextBlock Grid.Row="3" Grid.Column="0" Text="Visitor team : " HorizontalAlignment="Right" ></TextBlock>
  44:             <ComboBox Grid.Row="3" Grid.Column="1"  DisplayMemberPath="Name"  ItemsSource="{Binding Path=Class[Visitor_PickListPresentation]}" SelectedValuePath="self"  SelectedValue="{Binding Path=Class[GameSetup]/Visitor}"></ComboBox>
  45:             <Image Grid.Row="3" Grid.Column="2"  Source="{Binding Path=Class[GameSetup]/Visitor_Image,Mode=OneWay,Converter={StaticResource ImageBlobConverter} }"></Image>
  46:  
  47:             <TextBlock Grid.Row="4" Grid.Column="0" Text="Scheduled date : " HorizontalAlignment="Right" ></TextBlock>
  48:             <Controls:DatePicker Grid.Row="4" Grid.Column="1"  ></Controls:DatePicker>
  49:             
  50:             <Button Grid.Row="5" Grid.Column="0" IsEnabled="{Binding Path=Class[GameSetup]/CanStartGame}" Click="ButtonStartGame_Click">
  51:                 <TextBlock Text="Start Game"></TextBlock>
  52:             </Button>
  53:  
  54:             <Button  Grid.Row="5" Grid.Column="1"  IsEnabled="{Binding Path=Class[GameSetup]/CanEndGame}" >
  55:                 <TextBlock Text="End Game" ></TextBlock>
  56:             </Button>
  57:  
  58:         </Grid>
  59:          <StackPanel Orientation="Horizontal" VerticalAlignment="Bottom">
  60:             <Image Height="50" Name="imagebrynäs" Stretch="Fill" Width="50" Source="/WPFBinding;component/brynäs.jpg" />
  61:             <Image Height="50" Name="imagedjurgården" Stretch="Fill" Width="50" Source="/WPFBinding;component/djurgården.jpg" />
  62:             <Image Height="50" Name="imageluleå" Stretch="Fill" Width="50" Source="/WPFBinding;component/Luleå.jpg" />
  63:         </StackPanel>
  64:     </Grid>
  65: </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:

   1: (Resources["VM1"] as Eco.ViewModel.WPF.ViewModelContent).SetEcoSpace(_es);
   2:  
   3: (Resources["VM1"] as Eco.ViewModel.WPF.ViewModelContent).RootObject=CreateSomeTestData();

The UI looks like this:

image

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:

image

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

image

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

image

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:

   1: <ecoVM:ViewModelWPFUserControl Grid.Row="2" x:Name="VMU1" EcoSpaceType="{x:Type ecospace:WPFBindingEcoSpace}" ViewModelName="GameSetup" ></ecoVM:ViewModelWPFUserControl>

And the result is:

image

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.

 
Contact Us | Terms of Use | Privacy Statement © 2009 CapableObjects