Click here to Skip to main content
15,888,610 members
Articles / Desktop Programming / XAML

A dynamic Rehosted Workflow Designer for WF 4

Rate me:
Please Sign up or sign in to vote.
4.85/5 (26 votes)
29 Jul 2012CPOL10 min read 111.8K   11.8K   78  
This article presents a framework allowing you to integrate the workflow designer more easily in your own applications
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.IO;
using System.Reflection;
using System.Activities;
using System.Xaml;
using System.Activities.XamlIntegration;
using System.Diagnostics;
using System.Windows.Threading;
using Selen.WorkflowDesigner.Storage.Contracts;

namespace Selen.ActivityWorkflowType
{
    public class ActivityStorageAdapter : IStorageAdapter
    {
        private string compiledFileName = ActivityPaths.DestFileName;
        private string sourceFileName = ActivityPaths.FileName;
        private Dispatcher dispatcher = Dispatcher.CurrentDispatcher;

        public ActivityStorageAdapter()
        {
            AppDomain.CurrentDomain.AssemblyResolve += (sender, e) => (from item in Directory.EnumerateFiles(compiledFileName) where AssemblyName.GetAssemblyName(item).FullName == e.Name select Assembly.LoadFrom(item)).FirstOrDefault();
        }

        public IEnumerable<IWorkflowDescription> GetAllWorkflowDescriptions()
        {
            var lst = new List<WorkflowDescription>();

            foreach (var item in Directory.GetFiles(sourceFileName, "*.dll"))
            {
                var fileName = Path.GetTempFileName();
                File.Copy(item, fileName, true);
                var asm = Assembly.LoadFrom(fileName);
                var toAdd = asm.GetType("DynamicActivities.CompiledActivity", false);

                if (toAdd == null || !typeof (ICompiledActivity).IsAssignableFrom(toAdd)) continue;
                var instance = (ICompiledActivity)Activator.CreateInstance(toAdd);
                lst.Add(new WorkflowDescription(instance.ActivityName) { Description = instance.Description, WorkflowName = instance.ActivityName });
            }

            return lst;
        }

        public IDesignerModel CreateNewDesignerModel()
        {
            return new DesignerModel(new ActivityBuilder() {Name = "CustomActivities.Activity1"}, new ActivityEntity());
        }

        public bool CanCreateNewDesignerModel
        {
            get 
            { 
                return true;
            }
        }

        public IDesignerModel GetDesignerModel(object key)
        {
            var file = Path.Combine(sourceFileName, string.Format("{0}.dll", key));

            if (!File.Exists(file))
            {
                return null;
            }

            var fileName = Path.GetTempFileName();
            File.Copy(file, fileName, true);
            var asm = Assembly.LoadFrom(fileName);
            var type = asm.GetType("DynamicActivities.CompiledActivity", false);

            if (type != null && typeof(ICompiledActivity).IsAssignableFrom(type))
            {
                var instance = (ICompiledActivity)Activator.CreateInstance(type);
                object workflow = null;

                this.dispatcher.Invoke(() =>
                {
                    try
                    {
                        workflow = XamlServices.Load(ActivityXamlServices.CreateBuilderReader(new XamlXmlReader(new StringReader(instance.XAML))));
                    }
                    catch
                    {
                        workflow = null;
                    }
                });

                if (workflow == null)
                {
                    throw new LoadWorkflowException() { XAMLDefinition = instance.XAML, DesignerModel = new DesignerModel(null, new ActivityEntity() { Description = instance.Description }) };
                }

                return new DesignerModel(workflow, new ActivityEntity() { Description = instance.Description }).ApplyKey();
            }
            
            return null;
        }

        public bool SaveDesignerModel(IDesignerModel designerModel)
        {
            var model = (DesignerModel)designerModel;
            var activityName = ((ActivityBuilder)model.RootActivity).Name;

            if (string.IsNullOrEmpty(activityName))
            {
                throw new ApplicationException("activity name must not be empty");
            }
            else if(!activityName.Contains('.'))
            {
                throw new ApplicationException("activity name must contain dot seperator");
            }

            var sb = new StringBuilder();
            var xamlWriter = ActivityXamlServices.CreateBuilderWriter(new IgnorableXamlXmlWriter(new StringWriter(sb), new XamlSchemaContext()));
            XamlServices.Save(xamlWriter, model.RootActivity);

            var xaml = sb.ToString();
            var description = model.ActivityEntity.Description;
            var fileName = Path.Combine(Path.GetTempPath(), string.Format("{0}.dll", Guid.NewGuid().ToString()));

            var result = this.CompileAssembly(xaml, description, activityName, fileName);

            foreach (CompilerError item in result.Errors)
            {
                Trace.TraceError(item.ErrorText);
            }

            if (result.Errors.HasErrors)
            {
                throw new ApplicationException("Assembly could not be compiled");
            }

            File.Copy(fileName, Path.Combine(sourceFileName, string.Format("{0}.dll", activityName)), true);
            File.Delete(fileName);

            return true;
        }

        public bool DeleteDesignerModel(object key)
        {
            var fileName = Path.Combine(sourceFileName, string.Format("{0}.dll", key));

            if (File.Exists(fileName))
            {
                File.Delete(fileName);
                return true;
            }

            return false;
        }

        private CompilerResults CompileAssembly(string xaml, string description, string activityName, string fileName)
        {
            string source;
            using (var sr = new StreamReader(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "template.txt")))
            {
                xaml = xaml != null ? xaml.Replace('"', char.Parse("'")) : null;
                description = description != null ? description.Replace('"', char.Parse("'")) : null;
                activityName = activityName != null ? activityName.Replace('"', char.Parse("'")) : null;

                source = sr.ReadToEnd();
                source = source.Replace("{0}", xaml);
                source = source.Replace("{1}", description);
                source = source.Replace("{2}", activityName);
            }

            var codeDomProvider = new CSharpCodeProvider();
            var compilerParams = new CompilerParameters
                                     {
                                         OutputAssembly = fileName,
                                         GenerateExecutable = false,
                                         GenerateInMemory = false,
                                         IncludeDebugInformation = false
                                     };

            compilerParams.ReferencedAssemblies.Add(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Selen.ActivityWorkflowType.dll"));
            compilerParams.ReferencedAssemblies.Add("System.dll");
            compilerParams.ReferencedAssemblies.Add("System.Core.dll");
            compilerParams.ReferencedAssemblies.Add("System.Drawing.dll");
            compilerParams.ReferencedAssemblies.Add(@"WPF\WindowsBase.dll");
            compilerParams.ReferencedAssemblies.Add("System.Activities.dll");
            compilerParams.ReferencedAssemblies.Add("System.Activities.Core.Presentation.dll");
            compilerParams.ReferencedAssemblies.Add("System.Activities.Presentation.dll");
            compilerParams.ReferencedAssemblies.Add("System.Xml.dll");
            compilerParams.ReferencedAssemblies.Add("System.Xaml.dll");

            return codeDomProvider.CompileAssemblyFromSource(compilerParams, source);
        }
    }
}

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
Student
Germany Germany
I´m a student and hobby programmer from Germany.

Comments and Discussions