• mwinkle.blog

    Deep Dive into the WF4 Designer Data Model: ModelItem and ModelProperty


    In this post I published an overview of the designer architecture.  I’ll copy the picture and the description of what I want to talk about today.


    There are a few key components here

    • Source
      • In VS, this is xaml, but this represents the durable storage of the “thing” we are editing
    • Instance
      • This is the in memory representation of the item being edited.  In vs2010 for the WF designer, this is a hierarchy of System.Activities instances (an object tree)
    • Model Item Tree
      • This serves as an intermediary between the view and the instance, and is responsible for change notification and tracking things like undo state
    • Design View
      • This is the visual editing view that is surfaced to the user, the designers are written using WPF, which uses plain old data binding in order to wire up to the underlying model items (which represent the data being edited).
    • Metadata Store
      • This is a mapping between type and designers, an attribute table for lack of a better term.  This is how we know what designer to use for what type

    Motivating the ModelItem tree

    One observation that you could make when looking at the diagram above is “I should just be able to bind the view to the instance.”  This approach could work, but has a couple of implementation problems:

    • It is unrealistic to expect that instances of runtime types will all be built to fit perfectly into a design oriented world.  The most obvious problem is that our activity types aren’t going to inherit from DependencyObject, or implement INotifyPropertyChanged.  We can’t specify that all collections are ObservableCollection  [interesting trivia, ObservableCollection has moved from WindowsBase.dll into System.dll].  If we could, that would make life easy, but that’s not the case.  
      • Additionally, there are design time services that we need to think about supporting (such as Undo/Redo), and we need to make sure we can consistently handle this across many object types, including those that have not been written by us.
    • There may be a case for not actually operating on a live instance of the object graph.  Note, in VS2010, we do, but if we want to do work that would enable a design time XAML experience, we would need our instance to actually contain a lot of information about the source document. 
    • If we go directly from the view to the instance, we tightly couple the two together, and that makes doing more interesting things in the future tricky.  For instance, if we want to add refactoring support to update instances of objects, we need more than just the object graph to do that (the model item tree also keeps track of things like back pointers, so I know everybody that references the object). 

    These reasons cause us to think about an abstraction we can use to intermediate the implementation details of the instance and the view with a common layer.  If you have programmed at the WPF designer extensibility level, you will likely be familiar with the idea (and some of the types) here.

    The ModelItem tree

    The way that I think about the ModelItem/ModelProperty tree is that it forms a very thing proxy layer on top of the shape of the instance being edited. 

    Let’s start with a very simple type:

    public class Animal
        // simple property
        public string Name { get; set; }
        // complex property 
        public Location Residence { get; set; } 
        // list 
        public List<Animal> CloseRelatives { get; set; }
        // dictionary
        public Dictionary<string, object> Features { get; set; } 
    public class Location
        public string StreetAddress { get; set; }
        public string City { get; set; }
        public string State { get; set; } 

    Ignore for a moment that I just gave an animal Features, I’m a PM, it’s how we think :-)

    Now, let’s create some instances of that, and then actually create a ModelItem.

       1:  EditingContext ec = new EditingContext();
       2:  var companion1 = new Animal { Name = "Houdini the parakeet" };
       3:  var companion2 = new Animal { Name = "Groucho the fish" };
       4:  var animal = new Animal 
       5:                   {
       6:                       Name = "Sasha the pug",
       7:                       Residence = new Location 
       8:                       {
       9:                           StreetAddress = "123 Main Street",
      10:                           City = "AnyTown",
      11:                           State = "Washington"
      12:                       },
      13:                       Features = { 
      14:                          {"noise", "snort" },
      15:                          {"MeanTimeUntilNaps", TimeSpan.FromMinutes(15) }
      16:                       },
      17:                       CloseRelatives = { companion1, companion2 } 
      18:                   };
      19:  ModelTreeManager mtm = new ModelTreeManager(ec);  mtm.Load(animal);
      20:  ModelItem mi = mtm.Root;

    One thing to note here is that I am using ModelTreeManager and EditingContext outside the context (no pun intended) of the designer (see lines 1, 19, and 20 in the above snippet).  This isn’t the usual way we interact with these, but it’s for this sample so that we can focus just on the data structure itself. [as an aside, my brother did have a parakeet named Houdini]. 

    Let’s take a quick look at a visualization of what the data structure will look like.  Remember to think about the ModelItem tree as a thin proxy to the shape of the instance.

    Rather than spend an hour in powerpoint,  I’ll just include a sketch :-)


    On the left, you see the object itself.  For that object, there will be one ModelItem which “points” to that object.  You can call ModelItem.GetCurrentValue() and that will return the actual object. If you look at the ModelItem type, you will see some interesting properties which describe the object.

    •  ItemType is the type of the object pointed to (in this case, Animal)
    • Parent is the item in the tree which “owns” this model item (in the case of the root, this is null)
    • Source is the property that provided the value (in the case of the root, this is null)
    • Sources is a collection of all the backpointers to all of the properties which hold this value
      • Note, the distinction between Source and Sources and Parent and Parents is a topic worthy of another post
    • View is the DependencyObject that is the visual (in teh case above, this is null as there is no view service hooked into the editing context)
    • Properties is the collection of properties of this object

    Properties is the part where things get interesting.  There is a collection of ModelProperty objects which correspond to the shape of the underlying objects.  For the example above, let’s break in the debugger and see what there is to see.


    As we might expect, there are 4 properties, and you will see all sorts of properties that describe the properties.  A few interesting ones:

    • ComputedValue  is a short circuit to the return the underlying object that is pointed to by the property.  This is equivalent to ModelProperty.Value.GetCurrentValue(), but has an interesting side effect that setting it is equivalent to SetValue().  
    • Name, not surprisingly, this is name of the property
    • PropertyType is the type of the property
    • Collection and Dictionary are interesting little shortcuts that we’ll learn about in a future blog post.
    • Value points to a model item that is in turn the pointer to the value
    • Parent points to the ModelItem which “owns” this property

    As Value points to another model item, you can see how this begins to wrap the object, and how this can be used to program against the data model.  Let’s look at a little bit of code. 


    You might say “hey, that’s a little ugly”  and I have two bits of good news for you.

    1. ModelItem has a custom type descriptor, which means that in WPF XAML we can bind in the way we expect (that is, I can bind to ModelItem.Location.StreetAddress, and the WPF binding mechanism will route that to mi.Properties[“Location”].Value.Properties[“StreetAddress”].  So, if you don’t use C# (and just use XAML), you don’t worry about this
    2. In RTM, we will likely add support for the dynamic keyword in C# that will let you have a dynamic object and then program against it just like you would from WPF XAML.  It’s pretty cool and I hope we get to it, if we do I will blog about it.

    Here’s a set of tests which show the different things we’ve talked about:


       1:  ModelItem root = mtm.Root;
       2:  Assert.IsTrue(root.GetCurrentValue() == animal, "GetCurrentValue() returns same object");
       3:  Assert.IsTrue(root.ItemType == typeof(Animal),"ItemType describes the item");
       4:  Assert.IsTrue(root.Parent == null,"root parent is null");
       5:  Assert.IsTrue(root.Source == null, "root source is null");
       6:  Assert.IsTrue(((List<Animal>)root.Properties["CloseRelatives"].ComputedValue)[0] == companion1, 
       7:             "ComputedValue of prop == actual object");
       8:  Assert.IsFalse(((List<Animal>)root.Properties["CloseRelatives"].ComputedValue)[0] == companion2, 
       9:             "ComputedValue of prop == actual object");
      10:  Assert.AreEqual(root.Properties["Residence"].
      11:      Value.
      12:      Properties["StreetAddress"].
      13:      Value.GetCurrentValue(), "123 Main Street", "get actual value back out");
      14:  Assert.AreEqual(root, root.Properties["Residence"].Parent, "property points to owner");
      15:  ModelItem location = root.Properties["Residence"].Value;
      16:  Assert.AreEqual(root.Properties["Residence"], location.Source, "sources point to the right place");

    Oh my, won’t this get explosively large?

    Good question, and the truth is that yes, this could get large as you were to spelunk the object graph.  The good news is that we’re incredibly lazy about loading this, we will only flush out the properties collection on demand, and we won’t generate a ModelItem until it is requested.  When we combine this with the View virtualization work we have done, we will only ever load as much in the WF designer as you need.   This keeps the overhead minimal, and in does not represent a substantial memory overhead.

    Why should I be careful about ModelItem.GetCurrentValue()

    One might be tempted to just say “Hey, I’m in C#, I’ll just call GetCurrentValue() and party on that.  If you do that, you are entering dangerous waters where you can mess up the data model.  Since the underlying instance doesn’t likely support any change notification mechanism, the model item tree will get out of sync with the underlying instance description.  This will manifest itself in problems at the designer because our serialization is based off the instance, not the ModelItem tree (note, that’s a vs2010 implementation detail that could change in a subsequent release).  The net result though is that you will get your view out of sync with your instance and serialization and that’s generally considered problematic. 


    Wow, that’s a longer post than I intended.  What have we covered:

    • The Modelitem tree and why we need it
    • The relationship between the underlying instance and ModelItem/ModelProperty
    • The shape and use of ModelItem / ModelProperty
    • Imperative programming against the tree

    What haven’t we covered yet

    • ModelItemCollection and ModelItemDictionary
    • How to use Sources and Parents to understand how the object sits in the graph and manipulation we can do for fun and profit

    I’ll get there.  In the meantime, if you have questions, let me know.


    **** Minor update on 10/29 to fix a bug in the code ****

    This is the way to use ModelTreeManager to generate ModelItems (Line 3 is the critical piece that was missing):

       1:  EditingContext ec = new EditingContext();
       2:  ModelTreeManager mtm = new ModelTreeManager(ec);
       3:  mtm.Load(new Sequence());
       4:  mtm.Root.Properties["Activities"].Collection.Add(new WriteLine());
  • mwinkle.blog

    WF4 Designer Bloggers


    I wanted to put a quick note up as the team is starting to blog a little more frequently and putting some interesting content out there.  A few places to check out to hear from the folks on the team


    What blogs are you reading for WF4 content?  What kind of stuff would you like to hear more about?  Let us know!

  • mwinkle.blog

    Types, Metatypes and Bears, Redux


    I made a quick post a few months back where I tried to talk about the way the designer works and lets us design types, as well as simply configure instances of types.

    There were a couple of key points that I wanted to make there in that post:

    • The workflow designer can configure instances of activity graphs, and create entire types as well
    • Types are designed by editing an instance of a type that represents the type being designed, the metatype
    • There is some XAML trickery required to serialize and deserialize this
    • This same type of work is done to enable the DynamicActivity capability

    A few folks have noticed (and sent me mail), that things look a little different in Beta2.  While we are going to have a more thorough, “here’s everything that changed” doc, I want to go ahead and update at least some of the things that I’ve been talking about here.

    What’s New

    In reality, very little is new, we’ve primarily moved stuff around now.  One thing that you may remember is that the DesignTimeXamlReader was not public in beta1, and if you are looking around, you may not find it.  We have made this functionality public however.  Thus, see the “what’s changed" bit.

    What’s Changed

    We took a long look at things and realized we had a bunch of XAML stuff all over the place.  We felt it would be a good idea to try to consolidate that into one place in WF, so System.Activities.XamlIntegration.ActivityXamlServices becomes your one stop shop for most things Activity and XAML related.  Let’s take a quick look and see what’s in there:

    ActivityXamlServices Members

    [This documentation is for preview only, and is subject to change in later releases. Blank topics are included as placeholders.]

    Creates an instance of an activity tree described in XAML.

    The ActivityXamlServices type exposes the following members.

    clip_image001[4] Methods





    Overloaded. Maps an x:Class activity tree to an ActivityBuilder or ActivityBuilder<(Of <(TResult>)>).



    Maps an ActivityBuilder or ActivityBuilder<(Of <(TResult>)>) from the specified writer to an x:Class activity tree.



    Overloaded. Maps an x:Class activity tree to an DynamicActivity or DynamicActivity<(Of <(TResult>)>).



    Overloaded. Creates an instance of a declarative workflow.



    Load is used to generally take some XAML and return an Activity which you can then use to execute.  If Load encounters a XAML stream for <Activity x:Class, it will subsequently generate a DynamicActivity.  This functions basically the same way WorkflowXamlServices.Load() did in beta1. 

    You also see CreateBuilderReader, and CreateBuilderWriter, which are used to surface the DesignTimeXaml capabilities that we used in beta1.  These will return an instance of a XamlReader/Writer that handles the transformation between the metatype and the <Activity x:Class XAML.   The metatype has changed names from ActivitySchemaType to ActivityBuilder.

    The table below should help summarize the uses and changes between beta1 and beta2.  In this area, I don’t expect any changes between what you see now, and what you will see in RTM.




    metatype (type to build types) ActivitySchemaType ActivityBuilder
    Mechanism to load DynamicActivity WorkflowXamlServices.Load() ActivityXamlServices.Load()
    Mechanism to load ActivityBuilder use WorkflowDesigner.Load() to get an ActivitySchemaType Use the reader from CreateBuilderReader() to pass into XamlServices.Load()
    Mechanism to save ActivityBuilder to XAML Create a new DesignTimeXamlWriter, pass that to XamlServices.Save() Use the writer returned from CreateBuilderWriter() to pass into XamlServices.Save()

    To explore this, use CreateBuilderReader() and XamlServices.Load() on a workflow that you’ve built in the designer and poke around a bit to see what’s going on.

    Here is some sample code that walks through this:

       1:  ActivityBuilder ab1 = new ActivityBuilder();
       2:  ab1.Name = "helloWorld.Foo";
       3:  ab1.Properties.Add(new DynamicActivityProperty { Name = "input1", Type = typeof(InArgument<string>) });
       4:  ab1.Properties.Add(new DynamicActivityProperty { Name = "input2", Type = typeof(InArgument<string>) });
       5:  ab1.Properties.Add(new DynamicActivityProperty { Name = "output", Type = typeof(OutArgument<string>) });
       6:  ab1.Implementation = new Sequence
       7:  {
       8:      Activities =
       9:      {
      10:          new WriteLine { Text = "Getting Started " },
      11:          new Delay { Duration = TimeSpan.FromSeconds(4) },
      12:          new WriteLine { Text = new VisualBasicValue<string> { ExpressionText= "input1 + input2" }},
      13:          new Assign<string> { To = new VisualBasicReference<string> { ExpressionText = "output" },
      14:                       Value = new VisualBasicValue<string> {ExpressionText= "input1 + input2 + \"that's it folks\"" } }
      15:      }
      17:  };
      18:  StringBuilder sb = new StringBuilder();
      19:  StringWriter tw = new StringWriter(sb);
      20:  XamlWriter xw = ActivityXamlServices.CreateBuilderWriter(
      21:      new XamlXmlWriter(tw, new XamlSchemaContext()));
      22:  XamlServices.Save(xw , ab1);
      23:  string serializedAB = sb.ToString();
      25:  DynamicActivity da2 = ActivityXamlServices.Load(new StringReader(serializedAB)) as DynamicActivity;
      26:  var result = WorkflowInvoker.Invoke(da2, new Dictionary<string,object> { {"input1","hello"}, {"input2", "world" }});
      27:  Console.WriteLine("result text is {0}", result["output"]);
      30:  ActivityBuilder ab = XamlServices.Load(
      31:      ActivityXamlServices.CreateBuilderReader(
      32:          new XamlXmlReader(new StringReader(serializedAB)))) as ActivityBuilder;
      34:  Console.WriteLine("there are {0} arguments in the activity builder", ab.Properties.Count);
      35:  Console.WriteLine("Press enter to exit");
      36:  Console.ReadLine();


    Good luck, and happy metatyping!

  • mwinkle.blog

    Navigating the WF4 Beta 2 Samples


    Hot off the presses (and the download center) come the WF4 Beta 2 samples here.  The team has invested a lot of time into these samples and they provide a good way to get up to speed on the way a particular feature or group of features work together.

    Note, there are 2300 files to be unzipped, so hopefully there is a sample in here for everyone.

    At a high level, we work down the directory structure from technology, sample type, and then some functional grouping of samples. 



    Within the “Sample Type” we have a few different categories we use.

    • Basic
      • These are “one feature” samples that are used to illustrate how to use a given sample.  Often times they are hosted in the most basic wrapper necessary in order to get the feature to a point where it can be shown. 
      • These are grouped within feature level areas, a few  examples from the samples are:
        • \WF\Basic
          • \BuiltInActivities – how to configure and use the activities that are in the box
          • \CustomActivities
            • \CodeBodied\ – writing activities in code, including async, composite, and leveraging ActivityAction’s
            • \Composite – writing composite activities
            • \CustomActivityDesigners – writing activity designers
          • \Designer – programming against the infrastructure, a tree view designer, and a XamlWriter which will remove view state.
          • \Persistance
          • \Tracking
    • Scenario
      • These are higher level samples that require pulling together a number of features in order to highlight how the features can be combined to enable an application of the technology that might be of interest.  In the WF bucket, you will see things like Compensation and Services, which pull together a number of individual features
      • The ActivityLibrary folder is chock full of interesting sample activities that are useful for seeing how to write activities, as well as code that might be useful in your application.  Some of these are items which we aren’t shipping in the framework but may in the future.   Many of these also include interesting designers as well.  Some of the interesting sample activities in here:
    • Application
      • These samples are used to show how to pull everything together within the context of an application.  For instance, the WorkflowSimulator is described this way:
        • This sample demonstrates how to write a workflow simulator that displays the running workflow graphically. The application executes a simple flowchart workflow (defined in Workflow.xaml) and re-hosts the workflow designer to display the currently executing workflow. As the workflow is executed, the currently executing activity is shown with a yellow outline and debug arrow. In addition tracking records generated by the workflow are also displayed in the application window. For more information about workflow tracking, see Workflow Tracking and Tracing. For more information about re-hosting the workflow designer, see Rehosting the Workflow Designer.

          The workflow simulator works by keeping two dictionaries. One contains a mapping between the currently executing activity object and the XAML line number in which the activity is instantiated. The other contains a mapping between the activity instance ID and the activity object. When tracking records are emitted using a custom tracking profile, the application determines the instance ID of the currently executing activity and maps it back to the XAML file that instantiated it. The re-hosted workflow designer is then instructed to highlight the activity on the designer surface and use the same method as the workflow debugger, specifically drawing a yellow border around the activity and displaying a yellow arrow along the left side of the designer

    • Extensibility
      • This is a section inside the WCF samples that focuses on the various mechanisms and levels of extensibility


    I will be blogging more on some of the interesting (to me) individual samples.  What do you think?  Are there samples you’d like to see?  How are you using these, is there anything we can do to make these more useful?

  • mwinkle.blog

    WF4 Designer Enhancements in VS 2010 Beta 2


    So, you might have heard that Beta2 has shipped.  I wanted to give a brief overview of the changes that have been made to the designer since beta1

    So, without further ado, here are my top 10 new designer features in Beta 2

    1. New UI
    2. Expand in place
    3. Choose Toolbox Items
    4. Imports designer
    5. Text on Flowchart labels
    6. IActivityTemplateFactory is public
    7. Namespace change
    8. WriteLine designer
    9. UI Virtualization
    10. Information about arguments in the property grid

    New UI

    We made a decision in Beta2 to do a bit of an overhaul on the designers general theme and feel in order to feel more like part of VS.  The UI we had in Beta1 was still the first pass at the designer surface that we showed last year in the first CTP at PDC.  Here are some screenshots of that new UI.  Our UX PM, Cathy, has started a blog, and I expect she will have some really interesting discussions about the design decisions that we made.


    We’ve rounded out the set of icons as well




    We’ve also created focus around the header, and surface information within that header (like validation errors)


    Expand in Place

    One of the biggest pieces of feedback that we got from the Beta1 release was the need to see what’s going on in my workflow.  The model we had in Beta1 was optimized around a few levels of composition, and required the user to double-click to drill into the activity.  This model works well to focus on an activity, but comes at the expense of the broader view.  We’ve gone ahead and changed that in beta2 that allows the workflow items to expand in place.

    I am jazzed that we did this, and there is no good way to put it in a picture.  Open up the designer and start playing around, and I think you’ll like what you’ll see.  The other nice part of this is that I can still choose to drill into if I want to have that focus.  This should also support fairly deep nesting of activities, meaning you can visualize large, complex workflows on the canvas. 

    Additionally, you can choose to expand or collapse individual nodes, or go to the “Expand All” command in order to expand out the tree.  One thing to note is that we don’t allow expansion inside of a flowchart because we don’t yet have a mechanism that lets me rearrange all of the items around that newly expanded activity. 

    Choose Toolbox Items

    If you have an assembly on disk somewhere that contains some activities, you can now add it into the toolbox through the Choose Items dialog.



    When you drop these new items on the canvas, the appropriate reference should be wired up for you.

    Imports Designer

    One thing that we heard from folks is that they like the vb expressions (well, they like the expressions, they aren’t super excited that we only have one language choice at this point), but they really didn’t like fully qualifying the type names.  To help out with this, we’ve introduced the Imports designer which lets you pick out the namespaces you want to import that will be used to make it easier to resolve types.



    Go to the Imports designer down at the bottom (next to Arguments and Variables) and add System.Xml


    This will now bring in System.Xml into the expression text box:



    Text on Flowchart Labels

    People seem to like the flowchart model, but one request we heard was that it was tough to see what all the lines meant, especially coming out of the branching constructs. I’ll show a few other interesting flowchart things here.

    First, hovering over a switch/decision will show the expression


    Clicking on the small triangle will “pin” this expression so that I can see it while working on the flowchart.  Now, hook up some of the lines:



    We also heard that the labels True and False are descriptive for the decision, they may not capture the intent.  So, you can click on the decision, open the property grid and set these to some custom value.


    Switch will display the switch values as well.

    IActivityTemplateFactory is Public

    Towards the end of Beta1, we slipped a little feature in called IActivityTemplateFactory as an internal API to generate a configured set of activities (like what happens when I drop the messaging activity pairs).  We found a number of folks really liked this idea, so we have made this public.  This lets you add these factories to the toolbox to drop a templatized, configured set of activities onto the canvas.  A real simple one would look like [this drops a Pick with two branches, like what happens today when you drop a Pick]:


       1:  public sealed class PickWithTwoBranchesFactory : IActivityTemplateFactory
       2:  {
       3:      public Activity Create(DependencyObject target)
       4:      {
       5:          return new Pick
       6:          {
       7:              Branches =
       8:              {
       9:                  new PickBranch
      10:                  {
      11:                      DisplayName = "Branch1"
      12:                  },
      13:                  new PickBranch
      14:                  {
      15:                      DisplayName = "Branch2"
      16:                  }
      17:              }
      18:          };
      19:      }
      20:  }


    Namespace change

    We’ve made some OM changes (there have been some I’ll blog about later). One important one is that we needed to move out of the *.Design namespaces, primarily because *.Design is meant to specifically imply VS designer specific stuff, and our designer stuff is not VS specific.  So, we’ve moved from .Design to .Presentation as our namespace and assembly names of choice. There has also been some reduction in the number of namespaces, and moving some types out of the root namespace (System.Activities.Presentation) into more specialized namespaces (System.Activities.Presentation.Converters). 

    We’ve also reduced the number of assemblies from 3 to 2, and here is the way to think about them.

    • System.Activities.Presentation.dll => this is the core extensibility mechanism and the bulk of the “guts” of the designer
    • System.Activities.Core.Presentation.dll => this is primarily the assembly that contains the designers for the out of box activity designers.  There are some, but not too many extensibility / programmability points.
      • One thing to note is that the out of box designers have been made internal as there isn’t any extensibility built into those.

    Within System.Activities.Presentation.dll, here are the public namespaces:

    • System.Activities.Presentation
    • .Converters
    • .Debug
    • .Hosting
    • .Metadata
    • .Model
    • .PropertyEditing
    • .Services
    • .Toolbox
    • .Validation
    • .View


    Writeline Designer

    We had not originally planned for a designer for WriteLine, we had felt that the default designer was good enough for this.  We heard some feedback and one of the testers decided to put one together.  This makes it easy to configure the WriteLine on the canvas:


    UI Virtualization

    This is a feature you may not see, or ever need to think about, but it’s one that I think is pretty neat, and helps us increase performance for large workflows.  We’ve built up some infrastructure inside the designer that supports virtualization of the UI surface.  The way to think about this is lazy loading the visuals of the designer until they are actually needed.  While this isn’t that useful for a small workflow, as a workflow starts to span multiple screens, it makes sense to only load the visual elements that are required for the workflow that are actually in view.  You may see artifacts of this if you open a large workflow as you see some of the workflow “draw on the fly.”  The advantages of doing this are that bigger workflows will open faster with fewer visual elements on the screen.  The information used by virtualization is coupled with the viewstate of the workflow in order to cache the size of the elements in order to reduce the need to do complex computations about size, position and overlap.

    Information About Arguments in the Property Grid

    Now, on hover we will display the information about the arguments and properties of the selected item, and additionally, we will pick up the System.ComponentModel.DesscriptionAttribute data as well.  The following code:

       1:  public sealed class MySimpleActivity : CodeActivity
       2:  {
       3:      [Description("This is a basic argument which will be used to compute the end value")]
       4:      public InArgument<string> Text { get; set; }
       6:      protected override void Execute(CodeActivityContext context)
       7:      { ...

    will show up like this in the property grid:


    Bonus Feature: Additional Toolbox Organization

    Rather than go with the “big flat list” approach, we’ve made the organization of items in the toolbox a little more granular.  This also shows some of the updated icons for the activities :-)


    I’m really excited that beta2 is out the door, and I can’t wait to start hearing about folks using it.  I’ll start blogging a little more, and there are a few folks on my team that will start talking about some other interesting designer topics.

Page 1 of 1 (5 items)