Using a Custom Expression Editor

This topic applies to Windows Workflow Foundation 4 (WF4).

A custom expression editor can be implemented to provide a richer or simpler expression editing experience. There are several scenarios in which you might want to use a custom expression editor:

  • To provide support for IntelliSense and other rich editing features in a rehosted workflow designer. This functionality must be provided because the default Visual Studio expression editor cannot be used in rehosted applications.

  • To simplify the expression editing experience for the business analyst users, so that they are not, for example, required to learn Visual Basic or deal with Visual Basic expressions.

Three basic steps are needed to implement a custom expression editor:

  1. Implement the IExpressionEditorService interface. This interface manages the creation and destruction of expression editors.

  2. Implement the IExpressionEditorInstance interface. This interface implements the UI for expression editing UI.

  3. Publish the IExpressionEditorService in your rehosted workflow application.

Implementing a Custom Expression Editor in a Class Library

Here is a sample of code for a (proof of concept) MyEditorService class that implements the IExpressionEditorService interface is contained in a MyExpressionEditorService library project.

using System;
using System.Collections.Generic;
using System.Activities.Presentation.View;
using System.Activities.Presentation.Hosting;
using System.Activities.Presentation.Model;

namespace MyExpressionEditorService
{
    public class MyEditorService : IExpressionEditorService
    {
        public void CloseExpressionEditors()
        {

        }
        public IExpressionEditorInstance CreateExpressionEditor(AssemblyContextControlItem assemblies, ImportedNamespaceContextItem importedNamespaces, List<ModelItem> variables, string text)
        {
            MyExpressionEditorInstance instance = new MyExpressionEditorInstance();
            return instance;
        }
        public IExpressionEditorInstance CreateExpressionEditor(AssemblyContextControlItem assemblies, ImportedNamespaceContextItem importedNamespaces, List<ModelItem> variables, string text, System.Windows.Size initialSize)
                {
            MyExpressionEditorInstance instance = new MyExpressionEditorInstance();
            return instance;
        }
        public IExpressionEditorInstance CreateExpressionEditor(AssemblyContextControlItem assemblies, ImportedNamespaceContextItem importedNamespaces, List<ModelItem> variables, string text, Type expressionType)
            {
            MyExpressionEditorInstance instance = new MyExpressionEditorInstance();
            return instance;
        }
        public IExpressionEditorInstance CreateExpressionEditor(AssemblyContextControlItem assemblies, ImportedNamespaceContextItem importedNamespaces, List<ModelItem> variables, string text, Type expressionType, System.Windows.Size initialSize)
        {
            MyExpressionEditorInstance instance = new MyExpressionEditorInstance();
            return instance;
        }
        public void UpdateContext(AssemblyContextControlItem assemblies, ImportedNamespaceContextItem importedNamespaces)
        {

        }

    }
}

Here is the code for a MyExpressionEditorInstance class that implements the IExpressionEditorInstance interface in a MyExpressionEditorService library project.

using System;
using System.Activities.Presentation.View;
using System.Windows;
using System.Reflection;
using System.Windows.Controls;

namespace MyExpressionEditorService
{
    public class MyExpressionEditorInstance : IExpressionEditorInstance
    {
        private TextBox textBox = new TextBox();

        public bool AcceptsReturn { get; set; }
        public bool AcceptsTab { get; set; }
        public bool HasAggregateFocus {
            get
            {
                return true;
            }
        }

        public System.Windows.Controls.ScrollBarVisibility HorizontalScrollBarVisibility { get; set; }
        public System.Windows.Controls.Control HostControl {
            get
            {
                return textBox;
            }
        }
        public int MaxLines { get; set; }
        public int MinLines { get; set; }
        public string Text { get; set; }
        public System.Windows.Controls.ScrollBarVisibility VerticalScrollBarVisibility { get; set; }

        public event EventHandler Closing;
        public event EventHandler GotAggregateFocus;
        public event EventHandler LostAggregateFocus;
        public event EventHandler TextChanged;

        public bool CanCompleteWord()
        {
            return (MessageBox.Show(MethodBase.GetCurrentMethod().Name, "TestEditorInstance", MessageBoxButton.YesNo) == MessageBoxResult.Yes);
        }
        public bool CanCopy()
        {
            return (MessageBox.Show(MethodBase.GetCurrentMethod().Name, "TestEditorInstance", MessageBoxButton.YesNo) == MessageBoxResult.Yes);
        }
        public bool CanCut()
        {
            return (MessageBox.Show(MethodBase.GetCurrentMethod().Name, "TestEditorInstance", MessageBoxButton.YesNo) == MessageBoxResult.Yes);
        }
        public bool CanDecreaseFilterLevel()
        {
            return (MessageBox.Show(MethodBase.GetCurrentMethod().Name, "TestEditorInstance", MessageBoxButton.YesNo) == MessageBoxResult.Yes);
        }
        public bool CanGlobalIntellisense()
        {
            return (MessageBox.Show(MethodBase.GetCurrentMethod().Name, "TestEditorInstance", MessageBoxButton.YesNo) == MessageBoxResult.Yes);
        }
        public bool CanIncreaseFilterLevel()
        {
            return (MessageBox.Show(MethodBase.GetCurrentMethod().Name, "TestEditorInstance", MessageBoxButton.YesNo) == MessageBoxResult.Yes);
        }
        public bool CanParameterInfo()
        {
            return (MessageBox.Show(MethodBase.GetCurrentMethod().Name, "TestEditorInstance", MessageBoxButton.YesNo) == MessageBoxResult.Yes);
        }
        public bool CanPaste()
        {
            return (MessageBox.Show(MethodBase.GetCurrentMethod().Name, "TestEditorInstance", MessageBoxButton.YesNo) == MessageBoxResult.Yes);
        }
        public bool CanQuickInfo()
        {
            return (MessageBox.Show(MethodBase.GetCurrentMethod().Name, "TestEditorInstance", MessageBoxButton.YesNo) == MessageBoxResult.Yes);
        }
        public bool CanRedo()
        {
            return (MessageBox.Show(MethodBase.GetCurrentMethod().Name, "TestEditorInstance", MessageBoxButton.YesNo) == MessageBoxResult.Yes);
        }
        public bool CanUndo()
        {
            return (MessageBox.Show(MethodBase.GetCurrentMethod().Name, "TestEditorInstance", MessageBoxButton.YesNo) == MessageBoxResult.Yes);
        }

        public void ClearSelection()
        {
            MessageBox.Show(MethodBase.GetCurrentMethod().Name);
        }
        public void Close()
        {
            MessageBox.Show(MethodBase.GetCurrentMethod().Name);
        }
        public bool CompleteWord()
        {
            return (MessageBox.Show(MethodBase.GetCurrentMethod().Name, "TestEditorInstance", MessageBoxButton.YesNo) == MessageBoxResult.Yes);
        }
        public bool Copy()
        {
            return (MessageBox.Show(MethodBase.GetCurrentMethod().Name, "TestEditorInstance", MessageBoxButton.YesNo) == MessageBoxResult.Yes);
        }
        public bool Cut()
        {
            return (MessageBox.Show(MethodBase.GetCurrentMethod().Name, "TestEditorInstance", MessageBoxButton.YesNo) == MessageBoxResult.Yes);
        }
        public bool DecreaseFilterLevel()
        {
            return (MessageBox.Show(MethodBase.GetCurrentMethod().Name, "TestEditorInstance", MessageBoxButton.YesNo) == MessageBoxResult.Yes);
        }
        public void Focus()
        {
            MessageBox.Show(MethodBase.GetCurrentMethod().Name);
        }
        public string GetCommittedText()
        {
            return "CommittedText";
        }
        public bool GlobalIntellisense()
        {
            return (MessageBox.Show(MethodBase.GetCurrentMethod().Name, "TestEditorInstance", MessageBoxButton.YesNo) == MessageBoxResult.Yes);
        }
        public bool IncreaseFilterLevel()
        {
            return (MessageBox.Show(MethodBase.GetCurrentMethod().Name, "TestEditorInstance", MessageBoxButton.YesNo) == MessageBoxResult.Yes);
        }
        public bool ParameterInfo()
        {
            return (MessageBox.Show(MethodBase.GetCurrentMethod().Name, "TestEditorInstance", MessageBoxButton.YesNo) == MessageBoxResult.Yes);
        }
        public bool Paste()
        {
            return (MessageBox.Show(MethodBase.GetCurrentMethod().Name, "TestEditorInstance", MessageBoxButton.YesNo) == MessageBoxResult.Yes);
        }
        public bool QuickInfo()
        {
            return (MessageBox.Show(MethodBase.GetCurrentMethod().Name, "TestEditorInstance", MessageBoxButton.YesNo) == MessageBoxResult.Yes);
        }
        public bool Redo()
        {
            return (MessageBox.Show(MethodBase.GetCurrentMethod().Name, "TestEditorInstance", MessageBoxButton.YesNo) == MessageBoxResult.Yes);
        }
        public bool Undo()
        {
            return (MessageBox.Show(MethodBase.GetCurrentMethod().Name, "TestEditorInstance", MessageBoxButton.YesNo) == MessageBoxResult.Yes);
        }
    }
}

Publishing a Custom Expression Editor in a WPF Project

Here is the code that shows how to rehost the designer in a WPF application and how to create and publish the MyEditorService service. Before using this code, add a reference to the MyExpressionEditorService library project from the project that contains the avalon2 application.

        
using System.Windows;
using System.Windows.Controls;
using System.Activities.Presentation;
using System.Activities.Statements;
using System.Activities.Core.Presentation;
using System.Activities.Presentation.View;
using MyExpressionEditorService;

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {

        private MyEditorService expressionEditorService;
        public MainWindow()
        {
            InitializeComponent();
            new DesignerMetadata().Register();
            createDesigner();
        }

        public void createDesigner()
        {
            WorkflowDesigner designer = new WorkflowDesigner();
            Sequence root = new Sequence()
            {
                Activities = {
                new Assign(),
                new WriteLine()}
            };

            designer.Load(root);

            Grid.SetColumn(designer.View, 0);

            // Create ExpressionEditorService 
            this.expressionEditorService = new MyEditorService();

            // Publish the instance of MyEditorService.
            designer.Context.Services.Publish<IExpressionEditorService>(this.expressionEditorService);

            MyGrid.Children.Add(designer.View);
        }
    }
}

Notes

If you are using an ExpressionTextBox control in a custom activity designer, it is not necessary to create and destroy expression editors using the CreateExpressionEditor and CloseExpressionEditors methods of the IExpressionEditorService interface. The ExpressionTextBox class manages this for you.

See Also

Tasks

Using the ExpressionTextBox in a Custom Activity Designer

Reference

IExpressionEditorService
IExpressionEditorInstance