The official Fatica Labs Blog! RSS 2.0
# Friday, 01 April 2011

Just a little post about this argument, since as the author of (t4) Hbm2net and Db2hbm I think I can say my opinion from a critic point of view. As an user I can say that, as an OR/M NHibernate is a great platform, almost any circumstances are treated in deep, and even the infamous “legacy db from hell” are gently handled. So this some of my thought:

ONE -  Mapping is not always monkey coding.

This comes from the experience from Db2hbm. The tool can be great if you ( as me, as everybody at the start/half part of the learning curve ) have to start with a big database, and you don’t want to do all the job by hand. Db2hbm has a strategy called mapping exception, you can use it to customize the mapping, but the product is actually monkey code, and you need to abandon the automatic lifecycle in favor of custom mapping. This is bad in term of code generation, since the difference between a code generator that works almost 90% the time and one that works 100% of the time is a lot more than 10% Smile.

TWO – Writing class is monkey coding. 

This comes from hbm2net. I worked personally on the old version in the NHContrib and produced a version with a T4 template. I never modified the template and always had a working class for my mapping, even with exotic associations. This is really pleasant, since we already did a boring job in writing the hbm, creating the class from it would be just painful. So, in my experience, there is nothing more in the class that is not already said in the hbm.

THREE – Xml is not the devil

There is nothing painful in writing XML if we enable the intellisense for it. It is easier to grab all the mapping details by writing your own mapping, and at soon you have a a good practice, you can leverage some nh mapping sugar, to deal with really powerful mapping constructions. With the meta tag hbm can collect even more information to feed more code generators.

FOUR – Fluent NHibernate

Fluent NHibernate is the ( probably most famous ) way to avoid writing XML with NHibernate. Even of there is an AutoMapping feature, that does avoid wrtite any code, you eventually ends writing such things:

public class CatMap : ClassMap<Cat>
{
  public CatMap()
  {
    Id(x => x.Id);
    Map(x => x.Name)
      .Length(16)
      .Not.Nullable();
    Map(x => x.Sex);
    References(x => x.Mate);
    HasMany(x => x.Kittens);
  }
}
So if it is monking code writing the class for HBM, here you monkey twice by writing two class for say the same thing. 

FIVE – ConfOrm

CodeConform is the newest, and maybe the most innovative no xml mapping strategy. Fabio Maulo inspired himself probably in this post. The main idea in Conform in order to me is the pattern strategy that can help write complex mapping without really having to touch any xml file. The xml layer is avoided even internally, so loading a session factory is sensibly faster.  Since Fabio is the lead developer of NHibernate, some of these concept will probably appear on the next NH version.

Conclusion – What I would like to see

I like code first OR/M. But in real life this happened to me 0 times. So having legacy DB ( well, DB first approach, that is the same ) is quite common for me, and I think for a lot of people. Write a mapping in code is not an option, or does not make sense in order to replace hbm, we eventually fall in duplicate the concepts. ConfOrm by design avoid composite key, that are really common in legacy schemas. We need something to easily grab the pattern the DB designer would mean and easily port it to our NH solution.

Friday, 01 April 2011 22:25:13 (GMT Daylight Time, UTC+01:00)  #    Comments [2] - Trackback
NHibernate

# Monday, 21 March 2011

This is an example on implementing a behavior for zooming a 2D representations in WPF in a CAD friendly fashion, in the sense we need some facilities to have the same line thickness at different zoom level as well as text size. This because in 2D cad application we usually need to zoom into details but line thickness are actually zero width lines so they must not appear thicker as we look near to them, and even text sometimes need the same strategy. The same as we zoom “far” from the drawing, line thickness should appear the same. In this sample we focalize just a first zoom mode, the one handling the mouse wheel, that is simple because it does not require much user interaction handling ( in comparison to a “zoom redct” function ), but as in a real 2D cad application, we want a real zoom at point.

We implement this function as a behavior, so zooming seems “a thing attached” in XAML. An attached behavior, in WPF term is, as a per John Gossman definition:

The Attached Behavior pattern encapsulates "behavior" (usually user interactivity) into a class outside the visual heirarchy and allows it to be applied to a visual element by setting an attached property and hooking various events on the visual element.

Designing a behavior can start by the XAML, so we start by writing the XAML we expect to use in the way we like, as for example:

<Canvas z:Zoomable.Mode="Wheel" Background="White" SnapsToDevicePixels="True" x:Name="topCanvas">

 

The intent of this XAML is ( hopefully ) clear: we want to handle the zoom functionality on the Canvas in the mode “Wheel”. The underlined portion show where we attach the behavior. I did underline the Background assignment too: this is not exactly related to this behavior, but in general to get the canvas firing the mouse wheel events ( and any other mouse events ) it is necessary to specify a non transparent background, and these events are necessary to our behavior to work.

Implementing a behavior is done by using an attached property, and binding to the UIElement that property is applied to an event handler. In our implementation we use a class called ZoomHandler to handle the events:

public static readonly DependencyProperty ModeProperty =
           DependencyProperty.RegisterAttached("Mode", typeof(ZoomMode)
, typeof(Zoomable)
, new UIPropertyMetadata(ZoomMode.Disabled, new PropertyChangedCallback(OnModeChanged)));

       protected static void OnModeChanged(DependencyObject depo, DependencyPropertyChangedEventArgs depa)
       {
           Panel panel = depo as Panel;
           
           if (null == panel)
               throw new Exception("ZoomBehavior can only be attached to  objects derived from Panel");
           if (!attachedHandlers.ContainsKey(panel))
           {
               attachedHandlers[panel] = new ZoomHandler(panel);
           }
           attachedHandlers[panel].SetMode((ZoomMode)depa.NewValue);
       }

We attach a ZoomHandler class instance for each panel we want to zoom-enable, and we keep collected these object in an internal list. We define another attached property called UnityThickness. This is an important property in implementing CAD like behaviors: it is bound to the value of thickness of a line that appear to be one pixel thick at the selected zoom level. This property value is valuated from the ZoomHandler class, at the same time we apply the scaling factor, here below the function:

void canvas_MouseWheel(object sender, System.Windows.Input.MouseWheelEventArgs e)
       {
           if (currentMode == ZoomMode.Wheel || currentMode == ZoomMode.WheelAndMouse)
           {
               var reference = panel.Parent as IInputElement;
               if (null == reference)
                   reference = panel;
               Point pt = e.MouseDevice.GetPosition(reference);
               Transform mt = panel.RenderTransform;
               Matrix m = mt.Value;
               double amount = 1.0 + Math.Sign(e.Delta) * .1;
              
               m.ScaleAt(amount, amount, pt.X, pt.Y);
               currentAmount = m.M11;
               panel.RenderTransform= new MatrixTransform(m);
               Zoomable.SetUnityThickness(panel, 1.0 / currentAmount);
           }
       }

This function is the real one doing the zoom work. A scaling matrix is computed, to scale at an origin point defined by the current mouse coordinates. Then the attached UnityThickness property is set on the panel this ZoomHandler is handling. The scale amount is extracted form the first diagonal value of the RenderMatrix, by doing this we suppose we are using uniform scaling in zoom. Since we probably need to draw geometry with different thickness, we need some helper to bound the UnityThickness with different factors. The same can be done for font sizes. In order to achieve this, an helper class called ScaledThicknessSource is created: whith this class we can provide a scaled thickness by the current scale factor, here we can write:

  <Canvas z:Zoomable.Mode="Wheel" Background="White" SnapsToDevicePixels="True" x:Name="topCanvas">
            <z:ScaledThicknessSource  Thickness="1.0"  x:Name="Thickness1" UnityThickness="{Binding Path=(z:Zoomable.UnityThickness), RelativeSource={RelativeSource AncestorType=Canvas}}"  />
            <z:ScaledThicknessSource  Thickness="3.0"  x:Name="Thickness3" UnityThickness="{Binding Path=(z:Zoomable.UnityThickness), RelativeSource={RelativeSource AncestorType=Canvas}}"  />
            <z:ScaledThicknessSource   Thickness="22.0"  x:Name="FontSize12" UnityThickness="{Binding Path=(z:Zoomable.UnityThickness), RelativeSource={RelativeSource AncestorType=Canvas}}"  />
            <TextBlock Canvas.Left="20"  Canvas.Top="40" Foreground="Goldenrod" FontSize="{Binding Path=ActualThickness,ElementName=FontSize12}" >Non scaling text</TextBlock>
            <TextBlock Canvas.Left="240"  Canvas.Top="40" Foreground="Goldenrod" FontSize="22" >Scaling text</TextBlock>
            <Rectangle Fill="RosyBrown" StrokeThickness="2" Canvas.Left="60" Canvas.Top="80" Stroke="DeepSkyBlue" Width="100" Height="100"/>
            <Ellipse Fill="Brown" StrokeThickness="{Binding Path=ActualThickness,ElementName=Thickness1}" Canvas.Left="160" Canvas.Top="80" Stroke="Blue" Width="100" Height="100"/>
            <Path Canvas.Left="20" Canvas.Top="200" Stroke="BurlyWood" Data="M 45,0 L 35,30 L 0,30 L 30,55 L 20,85 L 45,60 L 70,85 L 60,55 L 90,30 L 55,30 L 45,0"/>
            <Path Canvas.Left="120" StrokeThickness="{Binding Path=ActualThickness,ElementName=Thickness3}" Canvas.Top="200" Stroke="DarkCyan" Data="M 45,0 L 35,30 L 0,30 L 30,55 L 20,85 L 45,60 L 70,85 L 60,55 L 90,30 L 55,30 L 45,0"/>
            <Image Canvas.Left="280" Canvas.Top="80" Source="/Assets/leo-mini.png" Width="128" Height="128"></Image>
        </Canvas>

In the example above, we create some instances of ScaledThickessSource, and we bound the property UnityThickess to the UnityThickness of the Zoomable behavior. Please note the syntax used:the () is necessary for an attached property. The thickness value is the desired one, at any zoom level. Then the examples geometry and text are bound tho the ActualThickness property of the helper instance, in order to have the proper scaled value.

Now we can look some examples of the behavior at work:

image The canvas with some example element. A test mantaining the dimension at any zoom level, a text affected by the zoom. The square has a standard thick border, the circle a correted thick border. The left star is with a standard border, and the rightr one has a fix tick border of two pixel. The image is added to show the zoom behavior working on raster images too.
image Here we show how the test is kept at each zoom level.
image Zooming in. The circle border thickness is kept, the square not.
image The same for the star. Notice as the left one appear to be bigger when we zoom in, this is because the left star border is not scaled.
image Zoom in works on Raster images…
image And zoom out too.

Another little thing, even if not necessary, the anti aliasing effect can blur a little the lines drawing, we can disable it in the following way:

 <Canvas z:Zoomable.Mode="Wheel" Background="White" SnapsToDevicePixels="True" x:Name="topCanvas">

Panning is not implemented yet. I would probably enable the ScrollViewer able to handle the pan, at present a standard ScrollViewer does simply not work since canvas does not measures the contained children total occupied size.

Monday, 21 March 2011 21:01:30 (GMT Standard Time, UTC+00:00)  #    Comments [3] - Trackback
Programmin | WPF

# Monday, 14 March 2011

This is the list of the posts related to Workflow4 Re-Hosting

Post 1 First drop, showing we can host the designer view in pure markup.
Post 2 Adding the activities bar into the game.
Post 3 Removing the blue gear, and adding the proper activity image.
Post 4 Showing the property editor grid.
Post 5 Default WorkflowView command routing.

 

Sample Project has a place on Codeplex.

Monday, 14 March 2011 20:16:50 (GMT Standard Time, UTC+00:00)  #    Comments [0] - Trackback
Programmin | Recipes | ReHosting | WF4

There is an entire suite of command defined by the designer view:

      • DesignerView.CopyAsImageCommand;
      • DesignerView.CopyCommand;
      • DesignerView.CreateArgumentCommand;
      • DesignerView.CreateVariableCommand;
      • DesignerView.CreateWorkflowElementCommand;
      • DesignerView.CutCommand;
      • DesignerView.CycleThroughDesignerCommand;
      • DesignerView.DeleteBreakpointCommand;
      • DesignerView.DisableBreakpointCommand;
      • DesignerView.EnableBreakpointCommand;
      • DesignerView.ExpandAllCommand;
      • DesignerView.ExpandCommand;
      • DesignerView.ExpandInPlaceCommand;
      • DesignerView.FitToScreenCommand;
      • DesignerView.GoToParentCommand;
      • DesignerView.InsertBreakpointCommand;
      • DesignerView.PasteCommand;
      • DesignerView.RedoCommand;
      • DesignerView.ResetZoomCommand;
      • DesignerView.SaveAsImageCommand;
      • DesignerView.SelectAllCommand;
      • DesignerView.ToggleArgumentDesignerCommand;
      • DesignerView.ToggleImportsDesignerCommand;
      • DesignerView.ToggleMiniMapCommand;
      • DesignerView.ToggleSelectionCommand;
      • DesignerView.ToggleVariableDesignerCommand;
      • DesignerView.UndoCommand;
      • DesignerView.ZoomInCommand;
      • DesignerView.ZoomOutCommand;

But since the view is internal in the designer it is not automatic binding commands form the UI to the underlying target. So our hosting solution propose an attached property, to route the command to the view in a way suitable in pure markup too. The property we create is called DesignerCommandTarget, and is used as below:

   1:  <Button Style="{StaticResource toolButton}" ToolTip="Zoom In"   Command="wfview:DesignerView.ZoomInCommand"
   2:   host:HostingHelper.DesignerCommandTarget="{StaticResource wfHost}" 
   3:  >
   4:                      <Image Source="Assets/zoom_in.png"></Image>
   5:  </Button>
   6:                 

 

So we basically bind the command to the button by using Command, as usual, but we set the DesignerCommandTarget to our hosting instance, that is defined in the resource. In order to have the commands exposed by the designer in XAML, we added the wfview namespace reference as underlined below:

   1:  <Window x:Class="WF4Host.MainWindow"
   2:          xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:          xmlns:toolbox="clr-namespace:System.Activities.Presentation.Toolbox;assembly=System.Activities.Presentation"
   5:          xmlns:host="clr-namespace:WF4Host"
   6:          xmlns:custom="clr-namespace:MyActivities;assembly=MyActivities"
   7:          xmlns:wf="clr-namespace:System.Activities.Presentation;assembly=System.Activities.Presentation"
   8:          xmlns:wfview="clr-namespace:System.Activities.Presentation.View;assembly=System.Activities.Presentation"
   9:          xmlns:activities="clr-namespace:System.Activities.Statements;assembly=System.Activities"
  10:          Title="MainWindow" Height="650" Width="825"
  11:          Icon="Assets/App.ico"
  12:          TextOptions.TextFormattingMode="Display"
  13:         
  14:          >

Command routing is very simple, and it is shown below:

 

   1:   protected static void OnCommandTargetChanged(DependencyObject depo, DependencyPropertyChangedEventArgs depa)
   2:          {
   3:              var src = depo as ICommandSource;
   4:              if (null != src && depa.NewValue != null )
   5:              {
   6:                  var ctargetdescr = DependencyPropertyDescriptor.FromName("CommandTarget"
   7:                                                , depo.GetType()
   8:                                                , depo.GetType()
   9:                                                ,true);
  10:                  ctargetdescr.SetValue(depo
  11:  , (depa.NewValue as HostingHelper).Designer.Context.Services.GetService<DesignerView>());
  12:              }
  13:          }

Every time we attach the property we check if the target is a CommandSource, so basically the strategy works for any object implementing this interface. If this pre condition is satisfied, we simply bind the WorkflowDesigner view as a CommandTarget for that source. In this way commands from our UI behave as they are defined inside the workflow, and we don’t need to write any code behind to custom wire them.

Our example application now looks like this:

 

image

Code for this post is hosted here.
Monday, 14 March 2011 11:13:09 (GMT Standard Time, UTC+00:00)  #    Comments [0] - Trackback
Programmin | Recipes | ReHosting | WF4

# Monday, 07 March 2011

Showing the property grid is achieved using  the same strategy we used to show the main editor, since it is a View internally exposed by the WorkflowDesigner. So we can decide where to put the property grid in XAML only in order to be blend friendly. Let’s have a look at the XAML:

   1:              <toolbox:ToolboxControl Grid.Column="0" Grid.Row="1" host:ToolboxItemSource.CategorySource="{StaticResource GeneralTools}"/>
   2:              <GridSplitter ResizeDirection="Columns" Grid.RowSpan="3" Grid.Column="1" Grid.Row="1" Height="Auto" Width="Auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
   3:              <host:MainViewPresenter HostingHelper="{StaticResource wfHost}" Grid.RowSpan="3" Grid.Column="2" Grid.Row="1"/>
   4:              <GridSplitter ResizeDirection="Rows" Grid.Column="0" Grid.Row="2" Height="Auto" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"/>
   5:              <host:PropertyViewPresenter HostingHelper="{StaticResource wfHost}" Grid.Column="0" Grid.Row="3"/>

Here we have all the graphical widget composing the designer as XAML tags. At line 5 we actually present the property editor. As a noise there is also some splitter we add in order to have a better usable interface. Code for the presenter is quite the same as the one we use to present the main view. So similar that could be refactor in some base class, but it is not done at the moment.

What we have right now is this:

image

A little problem appear to be the cursor over the splitter. For some weird reason the WF controls seems to stole the mouse cursor management and there is no way out to change it.

A poor man workaround is to make the splitter turn to bold when the mouse hover on it:

       <Style TargetType="GridSplitter">
            <Style.Triggers>
                <Trigger Property="IsMouseOver" Value="true">
                    <Setter Property="Background" Value="Black"/>
                 </Trigger>
 
            </Style.Triggers>
        </Style>

 

As usual the code so far can be found here.

Monday, 07 March 2011 20:41:30 (GMT Standard Time, UTC+00:00)  #    Comments [2] - Trackback
Programmin | Recipes | ReHosting | WF4

# Sunday, 06 March 2011

When we host the activities palette, we notice a blue gear without meaning on each of our activity. This should be easy to solve, but it is not so easy for the built in activities. So what we have till now is:

image

We want to have something better, even for the standard activities. In WF4 is not said that the ToolboxBitmapAttribute, the one used to bind the image to show in the palette is attached to all activities, and we can say it is not for sure for the built in activities. Fortunately the WF4 architecture provides a way to attach attributes to objects runtime, this is achieved via the AttributeTableBuilder class, that provide a way to register and apply attributes runtime to objects via another helper class: MetadataStore. So if we have somewhere these icons, we can attach the proper attribute runtime, and all is done, but where to find it ?  Do we have proper licensing to distribute it ? A reply to this question could be found here, and in particular in the Brannon King’s comment. Brannon show how to grab the icons from the redistributable System.Activities.Presentation dll, and more, it post the whole code to provide the attributes creation and binding, and graphics retrieval too. So we grab this great work and we add it in our helper class ToolboxItemSource, so we can ensure designer has the proper graphic information for both built in and custom activities we add as a source. Let’s have a look at the code, that is just a little modified:

   1:                  var builder = new AttributeTableBuilder();
   2:                  foreach (var item in query)
   3:                  {
   4:                      AddIconAttributes(item, builder);
   5:                  }
   6:                  MetadataStore.AddAttributeTable(builder.CreateTable());

Well this portion is in the member function AddTools, already present in our helper, we basically creates an AttributeTableBuilder, then we add the resources attributes, and then we realize, at line 6, the association. Let’s have a look at the Brannon’s code, that we embed in the AddIconAttributes:

   1:        protected static  bool AddIconAttributes(Type type, AttributeTableBuilder builder)
   2:          {
   3:              var secondary = false;
   4:              var tbaType = typeof(ToolboxBitmapAttribute);
   5:              var imageType = typeof(System.Drawing.Image);
   6:              var constructor = tbaType.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { imageType, imageType }, null);
   7:              string resourceKey = type.IsGenericType ? type.GetGenericTypeDefinition().Name : type.Name;
   8:              int index = resourceKey.IndexOf('`');
   9:              if (index > 0)
  10:              {
  11:                  resourceKey = resourceKey.Remove(index);
  12:              }
  13:              if (resourceKey == "Flowchart")
  14:              {
  15:                  resourceKey = "FlowChart"; // it appears that themes/icons.xaml has a typo here
  16:              }
  17:              resourceKey += "Icon";
  18:              Bitmap small, large;
  19:              object resource = resources[resourceKey];
  20:              if (!(resource is DrawingBrush))
  21:              {
  22:                  resource = resources["GenericLeafActivityIcon"];
  23:                  secondary = true;
  24:              }
  25:              var dv = new DrawingVisual();
  26:              using (var context = dv.RenderOpen())
  27:              {
  28:                  context.DrawRectangle(((DrawingBrush)resource), null, new Rect(0, 0, 32, 32));
  29:                  context.DrawRectangle(((DrawingBrush)resource), null, new Rect(32, 32, 16, 16));
  30:              }
  31:              var rtb = new RenderTargetBitmap(32, 32, 96, 96, PixelFormats.Pbgra32);
  32:              rtb.Render(dv);
  33:              using (var outStream = new MemoryStream())
  34:              {
  35:                  BitmapEncoder enc = new PngBitmapEncoder();
  36:                  enc.Frames.Add(BitmapFrame.Create(rtb));
  37:                  enc.Save(outStream);
  38:                  outStream.Position = 0;
  39:                  large = new Bitmap(outStream);
  40:              }
  41:              rtb = new RenderTargetBitmap(16, 16, 96, 96, PixelFormats.Pbgra32);
  42:              dv.Offset = new Vector(-32, -32);
  43:              rtb.Render(dv);
  44:              using (var outStream = new MemoryStream())
  45:              {
  46:                  BitmapEncoder enc = new PngBitmapEncoder();
  47:                  enc.Frames.Add(BitmapFrame.Create(rtb));
  48:                  enc.Save(outStream);
  49:                  outStream.Position = 0;
  50:                  small = new Bitmap(outStream);
  51:              }
  52:   
  53:              var tba = constructor.Invoke(new object[] { small, large }) as ToolboxBitmapAttribute;
  54:              builder.AddCustomAttributes(type, tba);
  55:              return secondary;
  56:          }

 

I’ve just stored the resources needed in a local static dictionary, by this call:

 

   1:  static ResourceDictionary resources = new ResourceDictionary
   2:   { Source = new Uri("pack://application:,,,/System.Activities.Presentation;component/themes/icons.xaml") };

 

Basically Brannon’s code use the assumption the icon is the name of the activity suffixed by “Icon”, except for a typo on FlowChart solved at lines 13-16. Then he create a bitmap and it save it on a memory stream. That stream is then use as an argument for ToolboxBitmapAttribute. So we put all together and we obtain this new presentation:

image

So we got rid of the blue gear. We will need the property editor next, always a XAML only strategy to present it has to be provided, probably with the same strategy we used for the main editor itself.

Have a look at the code here.

Sunday, 06 March 2011 22:10:10 (GMT Standard Time, UTC+00:00)  #    Comments [0] - Trackback
Programmin | Recipes | ReHosting | WF4

My Stack Overflow
Contacts

Send mail to the author(s) E-mail

Tags
profile for Felice Pollano at Stack Overflow, Q&A for professional and enthusiast programmers
About the author/Disclaimer

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2019
Felice Pollano
Sign In
Statistics
Total Posts: 157
This Year: 0
This Month: 0
This Week: 0
Comments: 127
This blog visits
All Content © 2019, Felice Pollano
DasBlog theme 'Business' created by Christoph De Baene (delarou) and modified by Felice Pollano
Nike Winkels Nederland Outlet Nike Nederland Store Outlet Nike Nederland 2015 Outlet Nike Outlet Online Nike Sneakers Outlet