Click here to Skip to main content
15,892,927 members
Articles / Desktop Programming / XAML

WF4 Custom activities for message mediation

Rate me:
Please Sign up or sign in to vote.
4.98/5 (25 votes)
20 Dec 2012CPOL24 min read 108.7K   2K   72  
This article describes a design, implementation, and usage of custom message mediation activities for a XAML workflow.
//*****************************************************************************
//    Description.....WF4.5 Message Mediation Library
//                                
//    Author..........Roman Kiss, rkiss@pathcom.com
//    Copyright © 2009 ATZ Consulting Inc. (see included license.rtf file)         
//                        
//    Date Created:    07/07/09
//
//    Date        Modified By     Description
//-----------------------------------------------------------------------------
//    07/07/09    Roman Kiss     Initial Revision
//    10/10/12    Roman Kiss     Upadate 4.5
//*****************************************************************************
//
#region Namespaces
using System;
using System.Activities.Presentation;
using System.Activities.Presentation.Hosting;
using System.Activities.Presentation.Toolbox;
using System.Activities.Statements;
using System.Activities.Validation;
using System.Activities.XamlIntegration;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Linq;
using System.ServiceModel.Activities;
using System.ServiceModel.Activities.Presentation.Factories;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Xaml;
using System.Xml;
#endregion

namespace ManageableServicesConsole.UserControls
{
    public partial class WorkflowDesignerUserControl : System.Windows.Forms.UserControl
    {
        WorkflowDesigner wd;
        bool isdirty = false;

        #region Constructor
        public WorkflowDesignerUserControl()
        {
            InitializeComponent();
            this.pictureBoxWarning.Visible = false;

            DesignerReadOnly = false;
            RegisterMetadata();
            AddDesigner(null);

            this.splitContainer2.SplitterDistance = this.splitContainer2.ClientSize.Height -15;   
    
            AddToolBox();
            AddPropertyInspector();
        }
        #endregion

        #region Get/Set 
        public bool IsDirty 
        { 
            get { return isdirty; }
            set
            {
                isdirty = value;
                this.pictureBoxWarning.Visible = value;
            }
        }
        public TemplateFactoryItem FactoryItem { get; set; }
        public bool DesignerReadOnly { get; set; }
        public string GetCurrentXmlDocument
        {
            get { return this.xmlNotepadPanelControl1.XmlNotepadForm.GetCurrentXmlDocument.Replace("dw1DOlxFbXB0eS54YW1sAA==", ""); }
        }
        public string Xaml
        {
            get
            {
                this.xmlNotepadPanelControl1.XmlNotepadForm.LoadXmlDocument(this.wd.Text.Replace("dw1DOlxFbXB0eS54YW1sAA==", ""));
                this.buttonRefresh.Enabled = false;
                return this.xmlNotepadPanelControl1.XmlNotepadForm.GetCurrentXmlDocument;
            }
            set
            {
                this.xmlNotepadPanelControl1.XmlNotepadForm.LoadXmlDocument(value.Replace("dw1DOlxFbXB0eS54YW1sAA==", ""));
                this.LoadXaml(value.Replace("dw1DOlxFbXB0eS54YW1sAA==", ""));
            }
        }

        public List<string> ReferencedAssemblies { get; set; }
       
        #endregion

        #region Add/Remove ToolboxItem
        public void AddToolboxItem(string toolName, string assemblyName)
        {
            this.AddToolboxItem("Custom", toolName, assemblyName, null, null);
        }
        public void AddToolboxItem(string categoryName, string toolName, string assemblyName)
        {
            this.AddToolboxItem(categoryName, toolName, assemblyName, null, null);
        }
        public void AddToolboxItem(string categoryName, string toolName, string assemblyName, string bitmapName, string displayName)
        {
            ToolboxItemWrapper item = new ToolboxItemWrapper(toolName, assemblyName, bitmapName, displayName);
            
            ToolboxControl tc = this.elementHost2.Child as ToolboxControl;
            var category = tc.Categories.FirstOrDefault(e => e.CategoryName == categoryName);
            if (category == null)
               tc.Categories.Add(new ToolboxCategory(categoryName) { item });
            else 
                category.Add(item);
        }
        public void RemoveToolboxItem(string categoryName, string displayName)
        {
            ToolboxControl tc = this.elementHost2.Child as ToolboxControl;
            var category = tc.Categories.FirstOrDefault(e => e.CategoryName == categoryName);
            if (category != null)
            {
                var item = category.OfType<ToolboxItemWrapper>().FirstOrDefault(e => e.DisplayName == displayName);
                if (item != null)
                    category.Remove(item);
            }          
        }
        #endregion

        #region Grid Elements
        private void RegisterMetadata()
        {
            System.Activities.Core.Presentation.DesignerMetadata designers = new System.Activities.Core.Presentation.DesignerMetadata();
            designers.Register();          
        }

        private void AddToolBox()
        {
            ToolboxControl tc = GetToolboxControl();
            Grid.SetColumn(tc, 0);
            this.elementHost2.Child = tc;
        }

        private void AddPropertyInspector()
        {
            Grid.SetColumn(wd.PropertyInspectorView, 2);
            this.elementHost3.Child = wd.PropertyInspectorView;
        }

        private void AddDesigner()
        {
            this.wd = new WorkflowDesigner();
            UIElement element = this.wd.View;
            element.IsEnabled = true;
            element.IsHitTestVisible = true;
            this.elementHost1.Child = element;

            //4.5
            this.elementHostOutlineView.Child = this.wd.OutlineView;
        }

        private void AddDesigner(string xaml)
        {
            this.wd = new WorkflowDesigner();
            this.wd.Context.Items.GetValue<ReadOnlyState>().IsReadOnly = this.DesignerReadOnly;

            // 4.5
            this.wd.Context.Services.GetService<DesignerConfigurationService>().TargetFrameworkName = new System.Runtime.Versioning.FrameworkName(".NETFramework", new Version(4, 5));
            this.wd.Context.Services.GetService<DesignerConfigurationService>().AnnotationEnabled = true;
            this.wd.Context.Services.GetService<DesignerConfigurationService>().AutoSurroundWithSequenceEnabled = true;

            if (this.FactoryItem != null)
            {
                this.wd.Context.Items.SetValue(this.FactoryItem);
            }

            this.wd.ModelChanged += delegate(object source, EventArgs args)
            {
                Trace.WriteLine("=> WorfklowDesigner model has been changed");
                this.wd.Flush();
                this.buttonRefresh.Enabled = !string.IsNullOrEmpty(this.wd.Text);
                this.IsDirty = true;
                
                //this.xmlNotepadPanelControl1.XmlNotepadForm.LoadXmlDocument(this.wd.Text);
            };

            if (string.IsNullOrEmpty(xaml))
            {
                this.xmlNotepadPanelControl1.XmlNotepadForm.LoadXmlDocument(this.DefaultXamlActivity);
            }

            this.wd.Text = this.GetCurrentXmlDocument;
            this.wd.Load();

            Grid.SetColumn(this.wd.View, 1);
            UIElement element = this.wd.View;
            element.IsEnabled = true;
            element.IsHitTestVisible = true;
            this.elementHost1.Child = element;
            this.IsDirty = false;

            // 4.5
            UIElement elementOV = this.wd.OutlineView;
            elementOV.IsEnabled = true;
            elementOV.IsHitTestVisible = true;
            this.elementHostOutlineView.Child = elementOV;
        }
        #endregion

        #region Events
        private void splitContainer2_MouseDoubleClick(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            this.splitContainer2.SplitterDistance = this.splitContainer2.ClientSize.Height - 30;
        }
        #endregion 

        #region Clicks
        private void buttonRefresh_Click(object sender, EventArgs e)
        {
            this.buttonRefresh.Enabled = false;
            if (string.IsNullOrEmpty(this.wd.Text) == false)
            {
                this.xmlNotepadPanelControl1.XmlNotepadForm.LoadXmlDocument(this.wd.Text);
            }
        }
        private void buttonLoad_Click(object sender, EventArgs e)
        {
            if (MessageBox.Show("Are you shure to reloaded workflow?", "Load xaml to designer", MessageBoxButton.YesNoCancel, MessageBoxImage.Warning) == MessageBoxResult.Yes)
            {
                this.LoadXaml(this.GetCurrentXmlDocument);
            }
        }
        private void buttonRun_Click(object sender, EventArgs e)
        {
            this.worker(this.Xaml);
        }
        #endregion

        #region Tester
        private void worker(string xaml)
        {
            ThreadPool.QueueUserWorkItem(delegate
            {
                string filename = Guid.NewGuid().ToString();
                byte[] buffer = Encoding.UTF8.GetBytes(xaml);
                using(var file = MemoryMappedFile.CreateNew(filename, 1000000))
                using (var writer = file.CreateViewAccessor())
                {
                    writer.WriteArray<byte>(0, buffer, 0, buffer.Length);

                    ProcessStartInfo psi = new ProcessStartInfo();
                    psi.Arguments = string.Format("{0} {1}", filename, buffer.Length);
                    psi.WorkingDirectory = AppDomain.CurrentDomain.BaseDirectory;                
                    psi.FileName = typeof(WorkflowConsoleXamlTester.Program).Assembly.Location;
                    Process exeProcess = Process.Start(psi);
                    exeProcess.WaitForExit();
                    exeProcess.Close();
                }
            });

        }
        #endregion

        #region Helpers
        private void LoadXaml(string xaml)
        {
            Dictionary<string, string> namespaces = null;
            try
            {
                if (this.wd != null && !string.IsNullOrEmpty(xaml))
                {
                    // validate new xaml
                    namespaces = this.GetNamespaces(xaml);

                    if (IsWorkflowService(xaml))
                    {
                        // tbd
                    }
                    else
                    {
                        var a = ActivityXamlServices.Load(new StringReader(xaml));
                        ActivityValidationServices.Validate(a);
                    }                  
                }

                // show up
                this.AddDesigner(xaml);
                this.AddPropertyInspector();

                this.buttonRefresh.Enabled = false;
            }
            catch (Exception ex)
            {
                StringBuilder sb = new StringBuilder(ex.Message);
                
                // show assemblies:
                if (namespaces != null)
                {
                    sb.AppendFormat("\r\n\r\nAssemblies:\r\n");
                    foreach (KeyValuePair<string, string> item in namespaces)
                    {
                        if (item.Value.StartsWith("clr-namespace:") && item.Value.IndexOf("assembly=") > 1)
                            sb.AppendFormat(" {0}\r\n", item.Value);
                    }
                }
                System.Windows.Forms.MessageBox.Show(sb.ToString());
            }
        }

        private ToolboxControl GetToolboxControl()
        {
            ToolboxControl ctrl = new ToolboxControl();

            #region MS Built-in Activity
            ToolboxCategory categoryFlowchart = new ToolboxCategory("Flowchart")
            {
                new ToolboxItemWrapper(typeof(Flowchart)),
                new ToolboxItemWrapper(typeof(FlowSwitch<>)),
                new ToolboxItemWrapper(typeof(FlowDecision))
            };

            ToolboxCategory categoryCollection = new ToolboxCategory("Collection")
            {  
                new ToolboxItemWrapper(typeof(AddToCollection<>)),
                new ToolboxItemWrapper(typeof(ClearCollection<>)),
                new ToolboxItemWrapper(typeof(ExistsInCollection<>)),
                new ToolboxItemWrapper(typeof(RemoveFromCollection<>)),          
            };

            ToolboxCategory categoryPrimitives = new ToolboxCategory("Primitives")
            {             
                new ToolboxItemWrapper(typeof(Assign)),
                new ToolboxItemWrapper(typeof(Assign<>)),
                new ToolboxItemWrapper(typeof(Delay)),
                new ToolboxItemWrapper(typeof(InvokeMethod)),
                new ToolboxItemWrapper(typeof(InvokeDelegate)),  
                new ToolboxItemWrapper(typeof(WriteLine)), 
            };

            ToolboxCategory categoryControlFlow = new ToolboxCategory("ControlFlow")
            {
                new ToolboxItemWrapper(typeof(DoWhile)),               
                new ToolboxItemWrapper(typeof(System.Activities.Core.Presentation.Factories.ForEachWithBodyFactory<>)),
                new ToolboxItemWrapper(typeof(If)),
                new ToolboxItemWrapper(typeof(Parallel)),
                new ToolboxItemWrapper(typeof(System.Activities.Core.Presentation.Factories.ParallelForEachWithBodyFactory<>)),
                new ToolboxItemWrapper(typeof(Pick)),
                new ToolboxItemWrapper(typeof(PickBranch)),  
                new ToolboxItemWrapper(typeof(System.Activities.Core.Presentation.Factories.PickWithTwoBranchesFactory)),
                new ToolboxItemWrapper(typeof(Sequence)),
                new ToolboxItemWrapper(typeof(Switch<>)),
                new ToolboxItemWrapper(typeof(While)),                         
            };

            ToolboxCategory categoryMessaging = new ToolboxCategory("Messaging")
            {
                new ToolboxItemWrapper(typeof(CorrelationScope)),
                new ToolboxItemWrapper(typeof(InitializeCorrelation)),
                new ToolboxItemWrapper(typeof(Receive)),
                 new ToolboxItemWrapper(typeof(ReceiveAndSendReplyFactory)),
                new ToolboxItemWrapper(typeof(Send)),
                new ToolboxItemWrapper(typeof(SendAndReceiveReplyFactory)),
            };

            ToolboxCategory categoryRuntime = new ToolboxCategory("Runtime")
            {
                new ToolboxItemWrapper(typeof(Persist)),
                new ToolboxItemWrapper(typeof(TerminateWorkflow)),
                new ToolboxItemWrapper(typeof(NoPersistScope)),
            };

            ToolboxCategory categoryTransaction = new ToolboxCategory("Transaction")
            {
                new ToolboxItemWrapper(typeof(CancellationScope)),
                new ToolboxItemWrapper(typeof(CompensableActivity)),
                new ToolboxItemWrapper(typeof(Compensate)),
                new ToolboxItemWrapper(typeof(Confirm)),
                new ToolboxItemWrapper(typeof(TransactionScope)),
                new ToolboxItemWrapper(typeof(TransactedReceiveScope)),
            };

            ToolboxCategory categoryErrorHandling = new ToolboxCategory("Error Handling")
            {
                new ToolboxItemWrapper(typeof(Rethrow)),
                new ToolboxItemWrapper(typeof(Throw)),
                new ToolboxItemWrapper(typeof(TryCatch)),
            };

            ToolboxCategory categoryStateMachine = new ToolboxCategory("State Machine")
            {
                new ToolboxItemWrapper(typeof(StateMachine)),
                new ToolboxItemWrapper(typeof(System.Activities.Core.Presentation.Factories.StateMachineWithInitialStateFactory)),
                new ToolboxItemWrapper(typeof(State)),
                new ToolboxItemWrapper(typeof(System.Activities.Core.Presentation.FinalState)),
            };
            #endregion

            ctrl.Categories.Add(categoryPrimitives);
            ctrl.Categories.Add(categoryControlFlow);
            ctrl.Categories.Add(categoryMessaging);
            ctrl.Categories.Add(categoryErrorHandling);
            ctrl.Categories.Add(categoryFlowchart);
            ctrl.Categories.Add(categoryRuntime);
            ctrl.Categories.Add(categoryTransaction);
            ctrl.Categories.Add(categoryCollection);
            ctrl.Categories.Add(categoryStateMachine);

            return ctrl;
        }

        private Dictionary<string, string> GetNamespaces(string xaml)
        {
            Dictionary<string, string> namespaces = new Dictionary<string, string>();
            using (XmlReader xrd = XmlReader.Create(new StringReader(xaml)))
            {
                XamlXmlReader xxrd = new XamlXmlReader(xrd);
                while (xxrd.Read())
                {
                    if (xxrd.Namespace == null) continue;
                    namespaces.Add(xxrd.Namespace.Prefix, xxrd.Namespace.Namespace);
                }
            }
            return namespaces;
        }

        private string DefaultXamlActivity
        {
            get
            {
                return @"<Activity x:Class='EmptyActivity' mva:VisualBasic.Settings='Assembly references and imported namespaces serialized as XML namespaces' xmlns='http://schemas.microsoft.com/netfx/2009/xaml/activities' xmlns:mva='clr-namespace:Microsoft.VisualBasic.Activities;assembly=System.Activities' xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' />";
            }
        }
        #endregion  
    
        private bool IsWorkflowService(string xaml)
        {
            StringReader input = new StringReader(xaml);
            using (XmlReader xrd = XmlReader.Create(input))
            {
                while (xrd.Read())
                {
                    if (xrd.LocalName == "WorkflowService" || xrd.LocalName == "Receive")
                        return true;
                }
            }
            return false;
        }
    }

    public class TemplateFactoryItem : ContextItem
    {

        // Properties-
        public sealed override Type ItemType
        {
            get { return typeof(TemplateFactoryItem); }
        }

        public string ConnectionString { get; set; }
        public Type Editor { get; set; }
    }
}


By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions