Welcome to Windows Workflow Foundation (WF)
Top Tasks :

WF Community Bloggers

Browse by Tags

All Tags » VB » NetFx3   (RSS)

  • Red Gate to continue development of .NET Reflector

    .NET Reflector, by Lutz Roeder, must be one of the most useful tools I have when developing .NET code. Usually it is the first thing I install right after Visual Studio not even waiting until I need it because I know I will.

    So the big news is that Red Gate, makers of the Ants profiler and lots of other tools, are taking over from Lutz Roeder and will continue developing .NET Reflector. Interesting move and I hope this means a bright future for the .NET Reflector.

    Read more about this here.

     

    Enjoy!

     

    www.TheProblemSolver.nl
    Wiki.WindowsWorkflowFoundation.eu

  • Book review: Pro WF by Bruce Bukovics

    Windows Workflow Foundation, WF for short, is one of the technologies I work a lot with and as a consequence I own several books about WF. Comparing this book with the others I can only say it is one of the best books you can get on the subject. It has a good coverage of almost all the subject you are going to need to know. Not only is there a good coverage but the explanation and examples are very clear. Now there are a few thing missing from the book. As it was written with the .NET framework 3.0 there is no coverage of the WCF integration that comes in the form of the SendActivity and the ReceiveActivity. While this is unfortunate it is also understandable and I can only hope that the author has the time to update the book with one more chapter.

    For a book this size, about 700 pages, I was amazed at how little I could find that I didn't like. In fact one of few errors I could find is the statement that only a single instance of the WorkflowRuntime can be created per AppDomain. This is a myth that stems back to the first beta's where it was briefly the case but the restriction has been lifted long ago and has never been part of the released product. But given the size of the book and how few people ever need to create multiple WorkflowRuntime objects it is hardly a big objection.

    So my advise: If you are getting into Windows Workflow Foundation make sure you buy this book!

     

    Enjoy.

  • Visual Studio 2008 Service Pack 1 available

    It is available from the subscriptions download at http://msdn.microsoft.com/en-us/subscriptions/default.aspx

     

    Get it while it is hot Smile

     

    Enjoy!

  • More on using a TransactionScopeActivity within a ReceiveActivity

    In a previous blog post I write about what happens when you place a TransactionScopeActivity within a ReceiveActivity and an exception occurs that is supposed to roll back the transaction. In short the story was very bad and we could come up with only a partial workaround, not a pretty sight.

     

    But there is more to it than just that little horror story. Suppose you do the obvious and place the a TransactionScopeActivity within a ReceiveActivity and no exception occurs. Say like the workflow below, please note that the codeActivity1 only sets the return value and causes no error.

     

    image

     

    Now the transaction is committed and the WCF call returns perfectly normally so everything is good right. Well not quite Sad

    The obvious first point is that the TransactionScopeActivity serves no real purpose. After all if it isn't allowed to fail under any circumstances why bother with it in the first place. Well ok there is the point of doing several updates as a single transaction so other users cannot see a partially committed order but that is about it.

    But that is actually the least of my worries as there is a far bigger issue to worry about and that is called workflow persistence.

    Yes that is right. After all when we are using a TransactionScopeActivity workflow persistence is mandatory. The TransactionScopeActivity is decorated with the PersistOnClose attribute so the state of the workflow will be persisted as soon as the transaction is complete. And normally that is a good thing but in this case it is the cause of the second problem because this is still inside the ReceiveActivity. So basically we are storing the workflow state as it is just before returning an answer to the client. Now if everything is running fine that won't matter because the workflow will continue until it is finished and everyone is happy. But suppose the workflow host terminates before the workflow is finished? In this test I added the DelayActivity, and set UnloadOnIdle to false so it doesn't persist the state, giving me the opportunity to kill the workflow runtime. Now if I restart the runtime It is going to reload the last state of the workflow and continue from there. And guess what it's going to do first? You guessed it: it is going to send the response to the client for a second time. Of course the client is no longer around and that action fails with an InvalidOperationException with message "Workflow unloaded between request & response."

    I guess the message is not entirely correct as it should say "Workflow reloaded between request & response." but it's close enough.

    The bottom line is the workflow terminates unless you specifically allow for this to happen and catch the error.

     

    So basically we have a choice between putting the TransactionScopeActivity inside a ReceiveActivity, not being able to throw an error and having a restore problem, or putting the TransactionScopeActivity around the ReceiveActivity, something that only works with a sequential workflow and an initiating ReceiveActivity as I described in the previous post.

    I guess these options make me pretty unhappy Sad.

     

    Enjoy your workflow transactions!

  • Using a TransactionScopeActivity with a WCF ReceiveActivity

    I got an email from a friend last week asking about using a ReceiveActivity and, while receiving, using a TransactionScopeActivity to transitionally save some data in a database. Seems like a common enough scenario right? Well he was having some problems. If everything worked and the transaction succeeded everything was fine and the answer came back. But if an exception occurred and the transaction was aborted be was receiving a real weird error:

    System.ServiceModel.FaultException`1[System.ServiceModel.ExceptionDetail]: Workflow service unexpectedly unloaded from memory while executing a ReceiveActivity. Make sure that the the workflow does not contain any blocking activities within a ReceiveActivity.  (Fault Detail is equal to An ExceptionDetail, likely created by IncludeExceptionDetailInFaults=true, whose value is:
    System.InvalidOperationException: Workflow service unexpectedly unloaded from memory while executing a ReceiveActivity. Make sure that the the workflow does not contain any blocking activities within a ReceiveActivity.).

    If we where to believe the message there was a blocking call, something there most certainly was not!

    And to make things more confusing, if we removed the TransactionScopeActivity and just let the exception occur it would bubble back to the client just as it was supposed to. So what gives?

    Well a lot of people looked at this and in the end we declared this a pretty bad bug. Mind you our words not those from Microsoft. But we did find a workaround. So lets take a look at a repro and how to fix this. My original test workflow looked like this:

    image

    I receive a WCF request, start a transaction, determine the return value in a code activity, throw an exception and return. Seems reasonable right? Well I thought so but it produces the error claiming there is a blocking statement. While tracking this Marvin actually noticed that the workflow idle event was raised! Excuse me, a workflow is becoming idle to undo a transaction sounds kind of wrong. And of course when a TransactionScopeActivity is used in a regular workflow, ie non WCF call, there is no Idle event.

     

    A partial solution

    So the way to get this to work is create the following workflow:

    image

    So instead of creating a TransactionScopeActivity inside of my ReceiveActivity I am doing it the other way round. Sounds kind of weird right? Well I think so but it does do the right thing as it returns the correct fault information to the client and then undoes any transactional work done.

     

    So why does this workaround work in this case?

    The ReceiveActivity has its CanCreateInstance property set to true so this is the request that actually creates and starts the workflow. This means that the workflow is created and starts executing from the top. Yes that is right, it starts from the top, not from the ReceiveActivity so any activities before that are also executed. I suppose that potentially opens a can of worms but we will leave that be for the moment. In this case the WCF request is received, this starts the workflow, this in turn starts the transaction, the message is received and processed and the transaction either commits or, like in this case, rolls back.

    How about a non creating ReceiveActivity?

    Well I am afraid no luck there as this workaround isn't going to work then. The problem is that we start a transaction before we start waiting for the additional WCF calls and the TransactionScopeActivity has a a TimoutDuration. So in all likelihood the transaction timeout will occur before the message is received and this effectively cancels the ReceiveActivity meaning it cannot receive the message.

     

    I think this is a pretty major problem with the way WF and WCF work together. After all transactions a an essential piece of business applications and not being able to use them inside of a WCF request is a deadly sin.

     

    Enjoy with case Smile

  • CodeCamp 2008

    Afgelopen jaar hebben we het eerste CodeCamp in Nederland georganiseerd en dat was een groot succes. De meeste deelnemers vroegen om meer, sommige zelfs om een CodeCamp per kwartaal of een heel weekend lang. Nou hebben we dat laatste nog niet gedaan maar we zijn wel aan de slag gegaan om een nieuw CodeCamp te organiseren. Als datum hebben we zaterdag 6 september gekozen. Gelukkig waren Microsoft en Class-a behulpzaam en kunnen we, net als vorig jaar, weer in het Microsoft Innovation Center in Barneveld terecht. Een mooie datum en locatie om uitgerust van de vakantie een hoop kennis uit te wisselen.

    Het programma staat nog niet helemaal vast, hou daar de website voor in de gaten, maar je kan er ondermeer de bekende Nederlandse sprekers verwachten. Uiteraard kan je zelf ook nog een sessie voorstellen dus als je een idee hebt voor een leuke sessie laat het dan zo snel mogelijk weten.

     

    Dus zet de datum vast in je agenda en meld je zo snel mogelijk aan op de website www.code-camp.nl.

     

    Het CodeCamp is een gezamenlijk initiatief van de SDN, de stichting DotNed en VB Central.

  • SQL Compact 3.5 on a 64 bit Windows

    SQL Compact is a great little database and highly recommended. However it was designed for use on small compact, hence its name, devices and that has some drawbacks. One of these is that it doesn't run as a 64 bits application. And by default if you create a .NET application it is compile as "Any CPU" meaning it will run as a 64 bits application on a 64 bits version of Windows.

    I, or I should say Ron Jacobs, ran into that using my SqlCeWorkflowPersistenceService because the sample/test application  was set to the default of "Any CPU". The result is that the LINQ provider cannot load the runtime and a InvalidOperationException with message "Cannot open 'WorkflowPersistenceDatabase.sdf'. Provider 'System.Data.SqlServerCe.3.5' not installed." is the result. Now the fix is rather easy but it has to be done in the main program and that is to compile it for X86 instead of Any CPU.

    X86

    Do this and any application using  SQL Compact 3.5 will happily run on Windows 64bit as a WOW Application.

    Enjoy!

  • SQL Server Compact Workflow Persistence Service updated

    I have just updated the SQL Server Compact Workflow Persistence Service to better handle deserialization issues when loading a workflow. The service now makes sure the WorkflowRuntime ServicesExceptionNotHandled is raised, something it should have done in the first place.

    You can download the SQL Server Compact Workflow Persistence Service from here.

     

    Enjoy!

    www.TheProblemSolver.nl
    http://wiki.WindowsWorkflowFoundation.eu

  • Unit testing asynchronous workflow activities

    In a previous blog post I demonstrated how TypeMock allowed us to mock out the workflow runtime infrastructure and create true unit tests for a workflow activity. Now this may not seems like a big deal but because most of the classes in the workflow runtime are sealed mocking them with another mocking framework is pretty much impossible. Now you can test custom activities by wrapping them in a test workflow, creating a workflow runtime, running the workflow and inspecting the result I can hardly call this a unit test as we have to create lots of, complex, depended objects. So TypeMock really opens up some new possibilities here.

    But the previous examples where still pretty simple, although mocking the ActivityExecutionContext has always been considered impossible, so lets try something more complicated in the shape of an asynchronous activity. Basically an asynchronous activity is an activity that returns ActivityExecutionStatus.Executing from the Execute method indicating to the workflow runtime that it isn't done yet. The activity uses ActivityExecutionContext at a later point in time to indicate that it is done by calling the CloseActivity function.

    Lets take a quick look at our new activity.

    public partial class WriteLineActivity3 : Activity
    {
        public WriteLineActivity3()
        {
            InitializeComponent();
        }
    
        public string Message { get; set; }
    
        protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
        {
            ActivityExecutionStatus result = ActivityExecutionStatus.Closed;
    
            if (!string.IsNullOrEmpty(Message))
            {
                IComparable queueName = Guid.NewGuid();
                WorkflowQueuingService wqs = executionContext.GetService<WorkflowQueuingService>();
                WorkflowQueue queue = wqs.CreateWorkflowQueue(queueName, true);
                queue.QueueItemAvailable += new EventHandler<QueueEventArgs>(queue_QueueItemAvailable);
    
    
                WriteLineService3 service = executionContext.GetService<WriteLineService3>();
    
                service.WriteLine(Message, queueName);
                result = ActivityExecutionStatus.Executing;
            }
    
            return result;
        }
    
        void queue_QueueItemAvailable(object sender, QueueEventArgs e)
        {
            ActivityExecutionContext executionContext = sender as ActivityExecutionContext;
            executionContext.CloseActivity();
        }
    }
    

    In this case the return value from the Execute depends on the Message property being filled or not so we need to test this. First a simple test for an activity with an empty message:

     

    /// <summary>
    ///A test for Execute
    ///</summary>
    [TestMethod()]
    [DeploymentItem("WFUnitTest1.exe")]
    [VerifyMocks()]
    public void ExecuteTestEmpty()
    {
        WriteLineActivity3_Accessor target = new WriteLineActivity3_Accessor();
        ActivityExecutionStatus expected = ActivityExecutionStatus.Closed;
        ActivityExecutionStatus actual;
    
        ActivityExecutionContext executionContext = 
    RecorderManager.CreateMockedObject<ActivityExecutionContext>(); WorkflowQueuingService wqs = RecorderManager.CreateMockedObject<WorkflowQueuingService>(); WorkflowQueue queue = RecorderManager.CreateMockedObject<WorkflowQueue>(); WriteLineService3 service = RecorderManager.CreateMockedObject<WriteLineService3>(); using (RecordExpectations recorder = RecorderManager.StartRecording()) { executionContext.GetService<WriteLineService3>(); recorder.FailWhenCalled(); } target.Message = ""; actual = target.Execute(executionContext); Assert.AreEqual(expected, actual); }

    In this test I actually check that the runtime service, called WriteLineService3 in this case, is never retrieved. This test is still pretty simple and no more complicated than the test we did previously.

     

    Lets take a look at something more complicated. The following test has the Message property set so it is expected to call the runtime service. But being an asynchronous activity it is also required to create a WorkflowQueue and bind to its QueueItemAvailable event so we will check that has happened. Finally this time the Execute function should return a status of ActivityExecutionStatus.Executing as it isn't done yet. This test runs like this:

    /// <summary>
    ///A test for Execute
    ///</summary>
    [TestMethod()]
    [DeploymentItem("WFUnitTest1.exe")]
    [VerifyMocks()]
    public void ExecuteTestStart()
    {
        WriteLineActivity3_Accessor target = new WriteLineActivity3_Accessor();
        ActivityExecutionStatus expected = ActivityExecutionStatus.Executing;
        ActivityExecutionStatus actual;
    
    
        ActivityExecutionContext executionContext = 
            RecorderManager.CreateMockedObject<ActivityExecutionContext>();
        WorkflowQueuingService wqs = RecorderManager.CreateMockedObject<WorkflowQueuingService>();
        WorkflowQueue queue = RecorderManager.CreateMockedObject<WorkflowQueue>();
        WriteLineService3 service = RecorderManager.CreateMockedObject<WriteLineService3>();
    
        using (RecordExpectations recorder = RecorderManager.StartRecording())
        {
            // Record methods here
            executionContext.GetService<WorkflowQueuingService>();
            recorder.Return(wqs);
            wqs.CreateWorkflowQueue(null, true);
            recorder.Return(queue);
            queue.QueueItemAvailable += delegate(object sender, QueueEventArgs e) { };
    
            executionContext.GetService<WriteLineService3>();
            recorder.Return(service);
            service.WriteLine(null, null);
        }
    
        target.Message = "A message";
        actual = target.Execute(executionContext);
    
        Assert.AreEqual(expected, actual);
    
    A little more complicated than the previous check but not bad at all Smile

    In this case we are binding to the QueueItemAvailable event so we need to add another test for the event handler and make sure it informs the workflow runtime we are done.

    /// <summary>
    ///A test for queue_QueueItemAvailable
    ///</summary>
    [TestMethod()]
    [DeploymentItem("WFUnitTest1.exe")]
    [VerifyMocks()]
    public void queue_QueueItemAvailableTest()
    {
        WriteLineActivity3_Accessor target = new WriteLineActivity3_Accessor();
        QueueEventArgs e = null; // TODO: Initialize to an appropriate value
    
        ActivityExecutionContext executionContext = 
            RecorderManager.CreateMockedObject<ActivityExecutionContext>();
    
        using (RecordExpectations recorder = RecorderManager.StartRecording())
        {
            executionContext.CloseActivity();
        }
    
        target.queue_QueueItemAvailable(executionContext, e);
    }
    

    Not bad at all Smile

    That leaves us with writing a unit test for the runtime service itself. This runtime service actually looks like this:

    public class WriteLineService3: WorkflowRuntimeService
    {
        public void WriteLine(string message, IComparable queueName)
        {
            Guid instanceId = WorkflowEnvironment.WorkflowInstanceId;
    
            ThreadPool.QueueUserWorkItem(state => WriteLineAsync(message, instanceId, queueName));
        }
    
        private void WriteLineAsync(string message, Guid instanceId, IComparable queueName)
        {
            Thread.Sleep(TimeSpan.FromSeconds(5));
    
            Console.WriteLine(message);
    
            WorkflowInstance instance = GetWorkflow(instanceId);
            instance.EnqueueItem(queueName, null, null, null);
        }
    
        private WorkflowInstance GetWorkflow(Guid instanceId)
        {
            WorkflowInstance result = Runtime.GetWorkflow(instanceId);
            return result;
        }
    }
    

    Not very complicated or realistic for that matter but still lets write a quick unit test for it like this:

    /// <summary>
    ///A test for WriteLine
    ///</summary>
    [TestMethod()]
    [VerifyMocks(10000)]
    public void WriteLineTest()
    {
        WriteLineService3_Accessor target = new WriteLineService3_Accessor(); 
        
        string message = string.Empty; // TODO: Initialize to an appropriate value
        IComparable queueName = null; // TODO: Initialize to an appropriate value
    
        WorkflowInstance instance = RecorderManager.CreateMockedObject<WorkflowInstance>();
    
        using (RecordExpectations recorder = RecorderManager.StartRecording())
        {
            Guid instanceId = WorkflowEnvironment.WorkflowInstanceId;
            recorder.Return(Guid.NewGuid());
    
            target.GetWorkflow(instanceId);
            recorder.Return(instance);
            instance.EnqueueItem(null, null, null, null);
        }
    
        target.WriteLine(message, queueName);
    
    }
    

    The most important thing here is that we can mock the WorkflowInstance object just as easy as the ActivityExecutionContext in the previous tests. I am actually telling TypeMock to wait for 10 seconds with the [VerifyMocks(10000)] attribute. This allows the delay in the service to remain there and still check if the expected response was queued. One thing to note is that I had to create an extra GetWorkflow() function to wrap the real function on the WorkflowRuntime object. I was unable to mock the latter however I am unsure why this is the case and if mocking it is really impossible.

     

    Enjoy!

    www.TheProblemSolver.nl
    http://wiki.WindowsWorkflowFoundation.eu

  • Changing the ActivityDesignerTheme on an activity

    Using an ActivityDesignerTheme is a quick way to customize the appearance of an activity in the workflow designer. But there is a gotcha Sad Because once you applied the theme and have a workflow with the custom activity open in the designer no matter what you do changes will not be applied.

    I am not sure why this is but I suppose the designer caches the color scheme and doesn't see any changes made to it.

    Take the following activity:

    using System;
    using System.ComponentModel;
    using System.Drawing;
    using System.Workflow.ComponentModel;
    using System.Workflow.ComponentModel.Design;
    using System.Drawing.Drawing2D;
    
    namespace WorkflowConsoleApplication4
    {
        [Designer(typeof(MyActivity1ActivityDesigner))]
        public partial class MyActivity: Activity
        {
        }
    
    
        [ActivityDesignerTheme(typeof(MyActivity1ActivityDesignerTheme))]
        public class MyActivity1ActivityDesigner: ActivityDesigner
        {
        }
    
        public class MyActivity1ActivityDesignerTheme : ActivityDesignerTheme
        {
            /// <summary>
            /// Initializes a new instance of the MyActivity1ActivityDesignerTheme class.
            /// </summary>
            public MyActivity1ActivityDesignerTheme(WorkflowTheme theme)
                : base(theme)
            {
                BackColorStart = Color.LightBlue;
                BackColorEnd = Color.Blue;
                BackgroundStyle = LinearGradientMode.ForwardDiagonal;
            }
        }
    }
    

    Which look like this in the designer

    image

    Clearly the background has a blue gradient. Now if I change the theme code to the following:

    public class MyActivity1ActivityDesignerTheme : ActivityDesignerTheme
    {
        /// <summary>
        /// Initializes a new instance of the MyActivity1ActivityDesignerTheme class.
        /// </summary>
        public MyActivity1ActivityDesignerTheme(WorkflowTheme theme)
            : base(theme)
        {
            BackColorStart = Color.LightCyan;
            BackColorEnd = Color.Cyan;
            BackgroundStyle = LinearGradientMode.ForwardDiagonal;
        }
    }
    

    You might expect the activity to change its look to:

    image

    Unfortunately this doesn't happen Sad

    Now you can quit Visual Studio and restart it after which the new style will take affect but there is a quicker way. Right click the activity and select "Select Custom Theme..."

    image

    When the dialog shows just click OK and the designer reads the theme again Smile

     

    image

    Enjoy!

    www.TheProblemSolver.nl
    http://wiki.WindowsWorkflowFoundation.eu

  • Using multiple SetState activities in a StateMachineWorkflow

    When working with state machine workflow's the SetStateActivity is one of the more important activities to use. The main reason is that is the one used to transition from one state to another. You can actually transition state in a different way as well using the StateMachineWorkflowInstance.SetState() function but the SetStateActivity is by far the most often used.

    One of the cool features of the SetStateActivity that may not be immediately obvious is the fact that it doesn't do the state switch immediately. Instead it saved the new target start in an internal property, named NextStateName on the StateMachineExecutionState type to be exact, and only does the state switch when the current EventDrivenActivity or StateInitializationActivity is finished. And just to be clear those are the only two activities where you can use a SetStateActivity.

    image

    So the cool part is that you don't need to have the SetStateActivity right at the bottom of the EventDrivenActivity but it can be anywhere in the execution flow. Something that might make the execution logic quite a bit easier.

    image

    And the second cool behavior is that you can actually execute multiple SetStateActivity objects with different TargetStateName definitions and only the last one will be executed. Again something that might make the logic quite a bit simple as you can set a default TargetStateName at the beginning of your and make changes to the target as exceptional cases arise.

    Both of these behaviors are especially important as the TargetStateName is a meta property, meaning it cannot be changed at runtime but only at design time. So the option of dynamically changing the SetStateActivity doesn't really exist.

    Enjoy!

    www.TheProblemSolver.nl
    http://wiki.WindowsWorkflowFoundation.eu

  • Validating workflow activities at runtime

    As I mentioned in my previous blog post adding a custom ActivityValidator class to an activity validates it at runtime as well as at design time.

    So when is this runtime validation done and what happens when it fails?

    The when part is when the WorkflowInstance object is first created using one of the overloads of the WorkflowRuntime CreateWorkflow function. This means that the activity cannot really validate all properties as they are when it starts executing. After all when it depends on some data provided by another activity it needs to wait until this other activity has executed. And that will not happen at the CreateWorkflow moment but after it is started. So only use it to validate properties that need to be set at compile time. So what is the point of checking at runtime? Well suppose your existing activity changed and has extra requirements. The existing workflow's may not be recompiled so the first opportunity is to detect the error at runtime.

     

    image

     

    So what happens when the workflow is loaded and the runtime validation fails?

    In that case an exception of type WorkflowValidationFailedException is raised. The Errors collection contains all validation errors in the form of ValidationError objects. Check the ErrorText property so see what is wrong.

    Enjoy!

    www.TheProblemSolver.nl
    http://wiki.WindowsWorkflowFoundation.eu

  • How to Download all of Visual Studio 2008 SP1

     VS2008 SP1 Beta is quite a package. By default the installation downloads the packages as needed and when needed. Now that is just fine if you only need to install a single machine. But when you need to install multiple, possibly virtual, machines like I have to it just wastes a lot of bandwidth and time Sad. Fortunately there is a solution and it can be found here in the blog post by Heath Stewart.

    Enjoy! 

  • Microsoft Netherlands Community DevCast

    The Dutch Microsoft office has posted the first of the Community DevCast we recorded. The host is Alex Thissen who actually does most of the work as he also edits the screencasts so don't forget to thank him next week when you run into him at the Dutch DevDays.

    I am happy to say that amongst the first 6 screencasts released are no less that 4 I did with Alex about Workflow Foundation. And there are more to come so stay tuned.

    The other two are about C# and done by Dennis Vroegop again with Alex as the host.

    You can find the Community DevCast here. Keep in mind they are for a Dutch audience so they where recorded in Dutch!

    Enjoy!

    www.TheProblemSolver.nl
    http://wiki.WindowsWorkflowFoundation.eu

  • Changing the default icon of a custom workflow activity

    By default every custom workflow activity shows the same icon. And using an icon that help understand what the activity actually does makes life so much easier for the user of your activity. And remember he is a developer so some day he might just return the favor Smile

     

    The default looks like this.

    image

     

    So changing the icon isn't hard there are just a few steps that might catch you if you aren't careful.

    This is what my demo project looks like:

    image

     

    1. Add the new image to the project. In my case I added a folder named Images and dropped the PushpinHS.png in there. This PushpinHS.png can be found in the standard VS2008ImageLibrary.zip located in C:\Program Files\Microsoft Visual Studio 9.0\Common7\VS2008ImageLibrary\1033.
    2. Change the build action for PushpinHS.png to Embedded Resource.
    3. Next go to the Activity1.cs file and add the ToolboxBitmap attribute to the Activity1 class. We need to specify two parameters, the first is a type in which assembly the PushpinHS.png is located and the second is the name of the resource to use. These two are related because the resource name us relative to the namespace of the type used.
    4. [ToolboxBitmap(typeof(Activity1), "Images.PushpinHS.png")]
      public partial class Activity1: SequenceActivity
      {

      }

    And now we have a nice new icon in the activity like this:

    image

     

    One problem is that the resource name is actually relative to the class specified. Using relfector makes it easy to see all the actual names, including the namespace used.

    image

     

    So what to do if your activity is in another namespace and a direct reference to the resource isn't possible?

    The easiest way is to add another class outside of all namespaces and fully specify the resource name like this:

    using System.Drawing;
    using System.Workflow.Activities;

    internal class ResourceLoader
    { }

    namespace WorkflowConsoleApplication1.MyActivities
    {
        [ToolboxBitmap(typeof(ResourceLoader), "WorkflowConsoleApplication1.Images.PushpinHS.png")]
        public partial class Activity1: SequenceActivity
        {
            public Activity1()
            {
                InitializeComponent();
            }
        }
    }

     

    Note the ResourceLoader used in the ToolboxBitmap attribute is not included in any namespace to we need to specify the full resource name "WorkflowConsoleApplication1.Images.PushpinHS.png".

     

    Enjoy!

More Posts Next page »

<August 2008>
SuMoTuWeThFrSa
272829303112
3456789
10111213141516
17181920212223
24252627282930
31123456

Copyright © 2006 Microsoft Corporation. All Rights Reserved. | Terms of Use | Privacy Statement | Contact Us