Sunday, June 6, 2010

ViewModel validations

Validations are there to enforce a set of rules that apply to a specific use case. A use case is implemented by a ViewModel, so the ViewModel needs Validations.

This is how you add them in the ViewModelEditor:

image

You will get a new row in the Validations list. Fill in the rule name, the expression that should evaluate to true when the rule is fulfilled, and false when data breaks the rule. You also provide a text with the error message you want to show. 

The context of the rule is the context of the Main ViewModelClass; this means that you reach variables etc.

image

You can have as many rules as you need.

The rule state (true of false) can then be displayed by one or multiple columns. You right click a ViewModelColumn and check the correct rule to make an association between the two:

image

Once the association is made and a rule break is discovered in runtime a standard WPF ValidationError will occur. How this ValidationError is displayed is defined by styles.

In WECPOF I added a default style – override it if you need – that brings up a tool tip, draws an exclamation mark behind the offending control and draws a red border around the value.

The default style is defined like this:

   1: <ControlTemplate x:Key="validationTemplate">
   2:     <DockPanel>
   3:         <Border BorderBrush="Red" BorderThickness="2" CornerRadius="4,4,4,4"  >
   4:             <AdornedElementPlaceholder/>
   5:         </Border>
   6:         <TextBlock Foreground="Red" VerticalAlignment="Center" FontWeight="ExtraBold" Margin="4">!</TextBlock>
   7:     </DockPanel>
   8: </ControlTemplate>
   9:  
  10: <Style TargetType="TextBox">
  11:     <Setter Property="Validation.ErrorTemplate" Value="{StaticResource validationTemplate}"></Setter>
  12:     <Style.Triggers>
  13:         <Trigger Property="Validation.HasError" Value="true">
  14:             <Setter Property="TextBox.ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}"/>
  15:         </Trigger>
  16:     </Style.Triggers>
  17: </Style>
  18:  

And when the rule is broken the it looks like this in runtime:

image

The multi variable seeker

To get anything done you need to find things. The normal software system has the same need. So how do we go about to declare a non limiting multi variable user friendly seeker into a generic model driven system like a WECPOF app?

This is what we need:

1. We need non rooted, persistent storage evaluated OCL expressions in order to execute a search

2. We need user input on how to limit the search

3. We need to allow for the user to use different limiting criteria's as he or she see fit ; after all the One-Field-Matches-Everything tactic that Google use does not really cut the mustard in enterprise applications (Touching a nerve did I? Users will want to limit the search on “Only this department”, “Only things from yesterday” etc, and even in google you need to use an extended syntax to get this done).

4. We want to allow for multiple sets of search definitions per seeker interface – if the user does not get a hit using the filters with the first set, it is natural for the user to “try again” and then we want to use another set of search criteria's; this has been tested on real users and they find it intuitive and obvious that it should work this way.

This article use this model:

image

And this is how we define the seeker:

image

#1 We declare variables that holds the user input – one variable is a string, and the other is an Object reference of type ReferenceClass (from our model).

#2 We add Columns that use these variables so that we get some UI for the user to enter the criteria's into. The Reference value we set up as a PickList.

#3 We create a SearchExpression with two criterias – the result of the two criteria's will be intersected (on the server side). And here is an important fact: Since the result of the criteria’s will be intersected we need some way to say if a criteria is Active or not – after all it is up to the user to limit the search on either or both of the two criteria’s.

This is how the activation of a Criteria is done:

image

and

image

The Active Expression is optional, if you leave it blank it defaults to true – always on.

#4 The second batch of search expression is executed the second time the user clicks the search button BUT only if the search variables has not changed. You can have as many search batches as you need, and they are round-robin-used whenever the user clicks the search button with untouched variables. Whenever a variable is changed the Round-robin is reset and the first batch is used again.

Even if these rules may seem complex they are intuitive for the user – especially if you use the search batches to filter for the same data as the resulting columns show. For example; the user enters someone’s first name, but your first batch filter on last name – the wrong people comes up for the first search, the user hits search again – now we use the second batch where you filter on first name – voila. This was just an example – you can just as well create a filter expression that unions the first name and the last name results ; Person.allinstances->select(a|a.FirstName.SqlLike(vSeekString+’%’))->union(Person.allinstances->select(a|a.LastName.SqlLike(vSeekString+’%’)))

Another example might be that the users enter a number – first we try to match it with a product code, user hits search again, we try to match it with the order number. The user is still not happy so he hit search again – now we match it with the phone number of the customer – user happy. In this example we could have chosen to create a detailed search interface with 3 text boxes – one for product code, one for order number and one for customer phone number – just as valid. Or we could do a union expression as above – just as valid. Choose the strategy that sits best with your users.

Saturday, June 5, 2010

Styling WECPOF applications and ViewModels

This post is about how to get the cool things of WPF to merge with the cool things of ECO.

We need to get the XAML styles into our declarative application

We need to declare and use style names on specific places in our UI where it makes sense to us.

We need to inject FormatStrings into our declarative DataBindings.

We need all of this at a granular level so that we will not be limited by the tool; for example we need to be able to control a single column in a grid separately, and also control its header differently than its cells.

All of this is done with XAML ResourceDictionaries. You can do your own with blend or notepad or any tool you like.

If you add your dictionaries to the Config folder of ECO, we will pick them up in the style Menu:

image

image

When you click a Style in WECPOF-runtime we load that Dictionary and merge it with the Application.

What can you change?

image

   1: <!-- WECPOFWinBackgroundBrush The background of a single window -->
   2: <LinearGradientBrush x:Key="WECPOFWinBackgroundBrush" EndPoint="0,1" StartPoint="0,0">
   3:     <GradientStop Color="#eae" Offset="0.0" />
   4:     <GradientStop Color="#777" Offset="1.0" />
   5: </LinearGradientBrush>
   6:  
   7:  
   8: <!-- WECPOFWinContentBackgroundBrush The background of content plate of a single window -->
   9: <LinearGradientBrush x:Key="WECPOFWinContentBackgroundBrush" EndPoint="0,1" StartPoint="0,0">
  10:     <GradientStop Color="#aea" Offset="0.0" />
  11:     <GradientStop Color="#777" Offset="1.0" />
  12: </LinearGradientBrush>
  13:  
  14: <!-- WECPOFWindowEnvironmentBackgroundBrush The background of the containing window , the master window -->
  15: <SolidColorBrush x:Key="WECPOFWindowEnvironmentBackgroundBrush"  Color="Coral">
  16: </SolidColorBrush>
  17:  
  18: <!--   WECPOFWinFrameBrush  -->
  19:  
  20:   WECPOFWINHeaderStyle  The style used for the window header-->
  21: <Style x:Key="WECPOFWINHeaderStyle" TargetType="{x:Type TextBlock}">
  22:     <Setter Property="Foreground" Value="Snow"></Setter>
  23:     <Setter Property="Margin" Value="2,0,2,0"></Setter>
  24:     <Setter Property="FontSize" Value="20"/>
  25:     <Setter Property="FontWeight" Value="ExtraBold"></Setter>
  26: </Style>
  27:  
  28:  
  29: <!--  WECPOFMenuStyle  The style used for Main Menu-->
  30: <Style x:Key="WECPOFMenuStyle" TargetType="{x:Type Menu}">
  31:         <Setter Property="Background" Value="{StaticResource MenuBackground}"/>
  32: ...CUT FOR CLARITY
  33:         </Setter>
  34:     </Style>
  35:  
  36:  
  37:     <!--  WECPOFMenuItemStyle  The style used for MenuItems-->
  38: <Style x:Key="WECPOFMenuItemStyle" TargetType="{x:Type MenuItem}">
  39:     <Setter Property="Background" Value="Black"></Setter>
  40:     <Setter Property="Foreground" Value="White"></Setter>
  41: </Style>

What can you do more?

When you set the StyleRef on ViewModelColumns you can define a corresponding Style in XAML.

image

Since the ViewModelColumn often spawns two controls; the TextBlock and in this case a TextBox we have defined the naming convention of STYLENAME.COMPONENTTYPE.

This enables you to define Style1 for all control types you use:

   1: <!-- Style for the TextBlock part of a ViewModelCOlumn that has Style1 in the StyleRef-->
   2: <Style x:Key="Style1.TextBlock" >
   3:     <Setter Property="TextBlock.FontSize" Value="36"/>
   4: </Style>
   5:  
   6: <!-- Style for the TextBox part of a ViewModelCOlumn that has Style1 in the StyleRef-->
   7: <Style x:Key="Style1.TextBox"  TargetType="{x:Type TextBox}">
   8:     <Setter Property="FontSize" Value="36"/>
   9: </Style>
  10:  

There is more

In WPF you can set a StringFormat on a Binding to aid in common transformations like “show only 2 decimals”, “present the DateTime in text format”, “This value is really percent” etc.

This need too is addressed with a naming convention in the XAML resource dictionary:

   1:  
   2:     <!-- Format string can be defined per Style and is used in the Binding - use this to format dates and doubles etc -->
   3:     <sys:String x:Key="StyleNumber2Dec.StringFormat">{0:n}</sys:String>
   4:     <sys:String x:Key="FormatDate.StringFormat">ddd d MMM</sys:String>

You use a StyleRef in the ViewModelColumn, and create a string entry in the resource dictionary with a key of STYLENAME.StringFormat. We apply this when setting up the Binding for the components in your view.

And we need Grid precision too

A DataGrid has Styles that apply to the whole Grid, but also you can apply individual styles per GridColumn:

   1: <!-- DataGrid column offers two styles, HeaderStyle and CellStyle -->
   2: <!-- This is how you define the HeaderStyle-->
   3: <Style x:Key="StyleOnColumn.HeaderStyle.DataGridTextColumn" TargetType="{x:Type prim:DataGridColumnHeader}">
   4:     <Setter Property="Height" Value="30"/>
   5:     <Setter Property="Background" Value="Green"/>
   6:     <Setter Property="Foreground" Value="Black"/>
   7:     <Setter Property="FontSize" Value="28" />
   8: </Style>
   9:  
  10:  
  11: <!-- DataGrid column offers two styles, HeaderStyle and CellStyle -->
  12: <!-- This is how you define the CellStyle-->
  13: <Style x:Key="StyleOnColumn.CellStyle.DataGridTextColumn" TargetType="{x:Type tk:DataGridCell}">
  14:     <Setter Property="Height" Value="30"/>
  15:     <Setter Property="Background" Value="Yellow"/>
  16:     <Setter Property="Foreground" Value="Black"/>
  17:     <Setter Property="FontSize" Value="18" />
  18: </Style>
  19:  
  20:  
  21: <!-- To tell a grid to use a default style on all ColumnHeaders-->
  22: <Style x:Key="StyleOnColumn.ColumnHeaderStyle.DataGrid" TargetType="{x:Type tkPrimitives:DataGridColumnHeader}">
  23:     <Setter Property="Height" Value="20"/>
  24:     <Setter Property="Background" Value="Yellow"/>
  25:     <Setter Property="Foreground" Value="Green"/>
  26:     <Setter Property="FontSize" Value="16" />
  27: </Style>
  28:  
  29: <!-- To tell a grid to use a RowStyle -->
  30: <Style x:Key="StyleOnColumn.RowStyle.DataGrid" TargetType="{x:Type tk:DataGridRow}">
  31:     <Setter Property="Height" Value="20"/>
  32:     <Setter Property="Foreground" Value="Green"/>
  33:     <Setter Property="FontSize" Value="16" />
  34: </Style>
  35:  
  36: <!-- To tell a grid to use a Default CellStyle -->
  37: <Style x:Key="StyleOnColumn.CellStyle.DataGrid" TargetType="{x:Type tk:DataGridCell}">
  38:     <Setter Property="Height" Value="20"/>
  39:     <Setter Property="Background" Value="Transparent"/>
  40:     <Setter Property="Foreground" Value="Blue"/>
  41:     <Setter Property="FontSize" Value="12" />
  42: </Style>

And the result

The result is that you can be very flexible in how you want to convey information:

image

image

 

image

Help us out please!

Create your own styles and send them to us. We will use your name on the style so you will be a famous XAML designer before you know it!

Tuesday, June 1, 2010

Using WECPOF in runtime

You can use WECPOF in design time ECO projects and in Gaffr.net files.

This article explains how to use it in runtime to deliver finished applications.

It starts out as an ordinary ECO project (create one from the wizard, Create new project Eco project).

You then create your model, update the code, configure your Persistence mapper of choice, set up any remote persistence mapper and synchronization if you want to just as the PDF-tutorials explain, Create or evolve your database, or derive the model from an existing database with the Reverse derive options.

Then add a WPF-Window or a WPF-Page (for browser applications).

Make sure your project references WECPOFLogic, then add a reference to the xaml :

xmlns:wecpof="clr-namespace:WECPOFLogic;assembly=WECPOFLogic"

And add a MainMenu and a WECPOFWindowEnvironment (this is where the WECPOF windows will be hosted)

 
   1: <Window x:Class="WpfApplication1.Window1"
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:         xmlns:toolkit="http://schemas.microsoft.com/wpf/2008/toolkit"  
   5:     xmlns:wecpof="clr-namespace:WECPOFLogic;assembly=WECPOFLogic"
   6:     Title="Window1" Height="314" Width="354">
   7:     <Window.Resources>
   8:         <LinearGradientBrush x:Key="WECPOFWinBackgroundBrush" EndPoint="0,1" StartPoint="0,0">
   9:             <GradientStop Color="#CCC" Offset="0.0" />
  10:             <GradientStop Color="#EEE" Offset="1.0" />
  11:         </LinearGradientBrush>
  12: ... cut for clarity...
  13:     </Window.Resources>
  14:     <Grid>
  15:         <Grid.RowDefinitions>
  16:             <RowDefinition Height="22"></RowDefinition>
  17:             <RowDefinition></RowDefinition>
  18:         </Grid.RowDefinitions>
  19:         <Menu Grid.Row="0"  Name="MainMenu">
  20:         </Menu>
  21:         <wecpof:WECPOFWindowEnvironment x:Name="WecpofWinEnv">
  22:  
  23:         </wecpof:WECPOFWindowEnvironment>
  24:     </Grid>
  25: </Window>

In code behind we need to create our ecospace and hook everything up; this is boiler plate code:

   1: public partial class Window1 : Window
   2: {
   3:     EcoSpace _ecospace;
   4:     MenuHandling _MenuHandling;
   5:     public Window1()
   6:     {
   7:         InitializeComponent();
   8:  
   9:         // Create ecospace or use one that you have
  10:         _ecospace = new EcoProject1.EcoProject1EcoSpace();
  11:         _ecospace.Active = true;
  12:         // Start up the Eco UI dequeuer ; if not set nothing will show
  13:         WPFDequeuer.Active = true;
  14:         // Load viewmodel definitions from embedded resources
  15:         ViewModelDefinitionsInApplication.Init(_ecospace);
  16:  
  17:         // Create a WECPOF menu handling component
  18:         _MenuHandling = new MenuHandling();
  19:  
  20:         // Define where WECPOF should look for styles
  21:         WecpofWinEnv.PathToStyles = Directory.GetCurrentDirectory()+"\\Styles";
  22:         // And to where WECPOF should apply a choosen style
  23:         WecpofWinEnv.ResourceDictionaryTargetForStyles(this);
  24:         // Tell the MenuHandling component where to put the MenuItems. Hand over the list of Actions from ECO, hand over the EcoSpace. 
  25:         // Send in the WindowEnvironment defined in xaml, and say in what context should the speed keys be evaluated
  26:         _MenuHandling.InitMainMenu(MainMenu, ViewModelDefinitionsInApplication.GetActionsRuntime(), _ecospace, WecpofWinEnv, this);
  27:         // Let the WECPOF Window environment know about the MenuHandling component
  28:         WecpofWinEnv.InstallMenuHandling(_MenuHandling);
  29:         // Implement the event to implement Quit...
  30:         MenuHandling.OnExit += new EventHandler<EventArgs>(MenuHandling_OnExit);
  31:     }
  32:  
  33:     void MenuHandling_OnExit(object sender, EventArgs e)
  34:     {
  35:         Close();
  36:     }
  37: }

And you are done:

image

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