The official Fatica Labs Blog! RSS 2.0
# 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

# Saturday, 05 March 2011

In the previous post we had a look on how to host WF4 designer in pure markup, we continue now to host, always in markup, the toolbar for dropping activities into the designer surface. The ToolboxControl is fortunately a standard WPF control, so it is not a problem to use it in markup, but it is a little difficult to add a list of categorized activities, because we have to add as a content of the control a list of category, each with a list of category. We want to use some sort of activity list source instead. So let’s have a look at the markup that add a ToolBoxControl to our designer:

   1:   <host:MainViewPresenter HostingHelper="{StaticResource wfHost}"  Grid.Column="1" Grid.Row="1"/>
   2:   <toolbox:ToolboxControl Grid.Column="0" Grid.Row="1" host:ToolboxItemSource.CategorySource="{StaticResource GeneralTools}" >

So the line 2 is the markup tag for adding the ToolboxControl, but the interesting part is the:

host:ToolboxItemSource.CategorySource="{StaticResource GeneralTools}"

CategorySource is an attached property of type ToolboxItemSource, an helper class we use to make easier binding to a list of activities. In this particular case we have defined an instance on the resources, called GeneralTools, as below:

   1:   <host:ToolboxItemSource x:Key="GeneralTools">
   2:              <host:ToolboxSource TargetCategory="General" AllSiblingsOf="{x:Type activities:Delay}"  />
   3:              <host:ToolboxSource TargetCategory="Custom" AllSiblingsOf="{x:Type custom:Activity1}"  />
   4:   </host:ToolboxItemSource>

As we can see, we can add to the ItemSource a list of source of type ToolboxSource, an helper class too. That class has the only pourpose of collecting the target category name, and a type of one activity to pick an assembly and contextually add all activities contained in. Most of the job is done by ToolboxItemSource, when the property is actually attached, so the code below:

   1:          protected static void OnCategorySourceChanged(DependencyObject depobj,DependencyPropertyChangedEventArgs dpcea)
   2:          {
   3:              if (null != dpcea.NewValue && dpcea.NewValue is ToolboxItemSource)
   4:              {
   5:                  var tbsrc = dpcea.NewValue as ToolboxItemSource;
   6:                  foreach (var source in tbsrc.Sources)
   7:                  {
   8:                      AddTools(depobj as ToolboxControl, source);
   9:                  }
  10:              }
  11:          }
  12:   
  13:          private static void AddTools(ToolboxControl toolboxControl, ToolboxSource source)
  14:          {
  15:              if (null != source.AllSiblingsOf)
  16:              {
  17:                  var cat = toolboxControl.Categories.Where(q => q.CategoryName.Equals(source.TargetCategory)).FirstOrDefault();
  18:                  if (null == cat)
  19:                  {
  20:                      cat = new ToolboxCategory(source.TargetCategory);
  21:                      toolboxControl.Categories.Add(cat);
  22:                  }
  23:                  var query = from type in source.AllSiblingsOf.Assembly.GetTypes()
  24:                              where type.IsPublic &&
  25:                              !type.IsNested &&
  26:                              !type.IsAbstract &&
  27:                              !type.ContainsGenericParameters &&
  28:                              (typeof(Activity).IsAssignableFrom(type) ||
  29:                              typeof(IActivityTemplateFactory).IsAssignableFrom(type))
  30:                              orderby type.Name
  31:                              select new ToolboxItemWrapper(type);
  32:                  foreach (var item in query)
  33:                      cat.Add(item);
  34:              }

35: }

 

Very easy, when we attach the item source, we pump on the control all the required activities, and it’s done. So this is another step on a XAML only re-hosting, let’s see the result:

image

Well you will probably note the very fancy gear icon that is the “mean nothing” icon that we usually have when we forgot something, and actually we forgot something, but differently from the old workflow is not so easy: a look here could help, and we probably work on that direction in further steps.

All are welcome to grab or cooperate on the source code for this sample here.

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

This post is based on this great post from The Problem Solver, but here we try to use less code behind and more markup language, to let the “host” decide where to put the designer components in his application. Basically there is three main chunk involved in hosting the WF:

  1. The main designer surface
  2. The property view
  3. The toolbox.

Components 1) and 2) are exposed as property of the WorkflowDesigner class, that itself is not a UIElement, but a simple class derived from object. The two property of interest are View and PropertyInspectorView, and both are UIElements. The problem is: how to let’s the host application writer to decide where to place these in XAML ? Lets start creating a class that act as an helper, and provide inside it some initialization code:

namespace WF4Host
{
    public class HostingHelper:DependencyObject
    {
        public WorkflowDesigner Designer { get { return workflowDesigner;} }
        WorkflowDesigner workflowDesigner;
        public HostingHelper()
        {
            workflowDesigner = new WorkflowDesigner();
            new DesignerMetadata().Register();
            //Creates an empty workflow
            workflowDesigner.Load(new Sequence());
        }
    }
    
}

basically we creates an instance of the WorkflowDesigner itself, load a first Sequence activity, and start the designer runtime. In xaml we can declare such an object as a StaticResource:

   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:host="clr-namespace:WF4Host"
   5:          xmlns:wf="clr-namespace:System.Activities.Presentation;assembly=System.Activities.Presentation"
   6:          Title="MainWindow" Height="650" Width="825"
   7:          >
   8:      <Window.Resources>
   9:          <host:HostingHelper x:Key="wfHost"/>
  10:          <LinearGradientBrush x:Key="backBrush" StartPoint="0,0" EndPoint="0,1" >
  11:              <GradientStop Offset="0.1" Color="DarkSlateBlue"></GradientStop>
  12:              <GradientStop Offset="0.2" Color="DarkSlateGray"/>
  13:              <GradientStop Offset="0.9" Color="BlanchedAlmond"/>
  14:          </LinearGradientBrush>
  15:      </Window.Resources>

 

Easy: at line 4 we add the namespace for our project. At line 9 we declare an instance of our HostingHelper and we name it wfHost. Remaining resources are just some beautiful ( ehm ) arts.

Then we need to present the graphical components. Lets declare in our code a control deriving from grid:

   1:  namespace WF4Host
   2:  {
   3:      public class MainViewPresenter : Grid
   4:      {
   5:          public HostingHelper HostingHelper
   6:          {
   7:              get { return (HostingHelper)GetValue(HostingHelperProperty); }
   8:              set { SetValue(HostingHelperProperty, value); }
   9:          }
  10:   
  11:          public static readonly DependencyProperty HostingHelperProperty =
  12:              DependencyProperty.Register("HostingHelper", typeof(HostingHelper)
  13:              , typeof(MainViewPresenter)
  14:              , new UIPropertyMetadata(null, new PropertyChangedCallback(OnHostingHelperChanged)));
  15:   
  16:          protected static void OnHostingHelperChanged(DependencyObject dobj
  17:              , DependencyPropertyChangedEventArgs dpcea)
  18:          {
  19:              if (dobj is MainViewPresenter)
  20:              {
  21:                  var mvp = dobj as MainViewPresenter;
  22:                  mvp.Children.Clear();
  23:                  if (dpcea.NewValue != null)
  24:                  {
  25:                      var host = dpcea.NewValue as HostingHelper;
  26:                      host.Designer.View.SetValue(Panel.HorizontalAlignmentProperty, HorizontalAlignment.Stretch);
  27:                      host.Designer.View.SetValue(Panel.VerticalAlignmentProperty, VerticalAlignment.Stretch);
  28:                      mvp.Children.Add(host.Designer.View);
  29:                  }
  30:              }
  31:          }
  32:      }
  33:  }

 

So this class is the MainView presenter. We call the Main View the one in which the WF is actually drawn. This control has a dependency property called HostingHelper, so we can bind it to the current HostingHelper that orchestrates the Workflow design. Let see how to do it in xaml:

   1:  <Grid Name="grid">
   2:              <Grid.RowDefinitions>
   3:                  <RowDefinition Height="32"/>
   4:                  <RowDefinition Height="*"/>
   5:              </Grid.RowDefinitions>
   6:                  <Grid.ColumnDefinitions>
   7:              <ColumnDefinition Width="100"></ColumnDefinition>
   8:              <ColumnDefinition></ColumnDefinition>
   9:              <ColumnDefinition Width="100"></ColumnDefinition>
  10:          </Grid.ColumnDefinitions>
  11:               
  12:              <host:MainViewPresenter
  13:               HostingHelper="{StaticResource wfHost}"  Grid.Column="1" Grid.Row="1"/>
  14:              
  15:          </Grid>

So at line 12 we declare our presenter, and we bind his hosting helper property to the hosting helper instance wfHost.

This is the current result:

image

So we achieved less code behind and more “blendability” by creating a little tool library. We should improve it with others components to complete the designer environment. I decided to publish this example as a project on codeplex, so you can join to use and improve it.

Saturday, 05 March 2011 10:49:00 (GMT Standard Time, UTC+00:00)  #    Comments [2] - Trackback
Programmin | Recipes | ReHosting | WF4

# Tuesday, 15 February 2011

Giammai :)

Giacchè il famoso Reflector di Lutz Roeder è diventato un tool a pagamento , e del resto non è mai stato Open Source, quali alternative ci sono  ? A me è piaciuta l’alternativa proposta dal team di SharpDevelop: ILSpy. Usa l’editor interno di SharpDevelop per visualizzare il disassemblato, che può essere C# o IL, il che è più che sufficiente per la stragrande maggioranza dei programmatori .NET. In più è open source, e si può scaricare e compilare da qui.

clip_image001

Tuesday, 15 February 2011 14:04:44 (GMT Standard Time, UTC+00:00)  #    Comments [2] - Trackback
Reflector | Deblector

# Sunday, 13 February 2011

Volevo sperimentare un po’ con silverlight, e la prima cosa che mi è venuta in mente è stata fare un bel blog gatget che mostrasse le ultime attività “meritorie” su StackOverflow, utilizzando le Stack Overflow API.

Con un po’ di lavoro il risultato è stato questo:

image

Ovviamente, anche se il progetto è semplicissimo, non ho saputo rinunciare a Caliburn Micro sia per l’aggancio MVVM che per le action. Ancora un paio di cose, e poi proverò a distribuirlo sul mio blog.  Come visibile nella barra di destra il componentino c’è: Provato sia su firefox che su IE sembra comportarsi bene. Il deploy su Aruba è stato facilissimo, anche perchè non c’è niente da fare a livello permissionWinking smile

Sunday, 13 February 2011 16:40:07 (GMT Standard Time, UTC+00:00)  #    Comments [0] - Trackback
Programmin | Silberlight

# Friday, 11 February 2011

Questo può accadere se, e generare dei potenziali super bachi quando…

1195438304820774480valessiobrito_Bug_Buddy_Vector.svg.med

…in un handler custom hostato in IIS quando si chiama Response.End();. Nel mio caso l’handler faceva, dopo aver scritto I dati nello stream di uscita, la doppia chiamata Response.Flush() e Response.End() che va evitata se si vuole evitare l’eccezione suddetta. Il problema non si verificava sul server di debug integrato di VS.

Friday, 11 February 2011 19:58:02 (GMT Standard Time, UTC+00:00)  #    Comments [0] - Trackback


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 2017
Felice Pollano
Sign In
Statistics
Total Posts: 157
This Year: 0
This Month: 0
This Week: 0
Comments: 124
This blog visits
All Content © 2017, 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