Creating a custom task with a default UI

Creating a custom task with a default UI

Rate This
  • Comments 7

The Developing a User Interface for a Custom Task entry in Books Online describes how to build your UI from scratch, but there is an easier way to create a UI that has the same look and feel as the stock tasks that ship with SSIS.

The Microsoft.DataTransformationServices.Controls assembly exposes a base class that most of the stock SSIS tasks inherit from – DTSBaseTaskUI. With minimal coding, you too can have a beautiful Task UI (that looks something like this:)

image

After creating your task, you can link it to a UI assembly using the UITypeName parameter of the DtsTask property. The recommended practice is to create a separate assembly for your UI to separate the UI code from the code that gets loaded at runtime, but this is optional. It can all work in the same assembly as well.

We’re going to create three classes for our “HelloWorldTask”

  1. HelloWorldTaskUI – Implements IDtsTaskUI
  2. HelloWorldTaskMainWnd – Extends DTSBaseTaskUI
  3. GeneralView – Implements IDTSTaskUIView

HelloWorldTaskUI is the entry point for the UI functionality, and is responsible for initializing and displaying your UI.

Extending DTSBaseTaskUI in HelloWorldTaskMainWnd gives you the form in the screen shot above. Each page of properties you want to display is a separate class that implements IDTSTaskUIView. You get the “Expressions” page for free.

Let’s walk through what you need to do.

First you’ll need to add some additional references:

  • Microsoft.DataTransformationServices.Controls
  • Microsoft.SqlServer.Dts.Design
  • System.Drawing
  • System.Windows.Forms

Note, that Microsoft.DataTransformationServices.Controls.dll is in the GAC, but doesn’t show up in the “.NET” tab of the “Add Reference…” dialog.

Add new User Control – GeneralView.cs. On the designer, add a property grid. To keep the same look and feel as other SSIS tasks, the InitializeComponents() should look something like this:

// 
// propertyGrid
// 
this.propertyGrid.Dock = System.Windows.Forms.DockStyle.Fill;
this.propertyGrid.Location = new System.Drawing.Point(0, 0);
this.propertyGrid.Name = "propertyGrid";
this.propertyGrid.PropertySort = System.Windows.Forms.PropertySort.Categorized;
this.propertyGrid.Size = new System.Drawing.Size(150, 150);
this.propertyGrid.TabIndex = 0;
this.propertyGrid.ToolbarVisible = false;

In your GeneralView class, define an internal class which contains all of the properties you’d like to display in the UI – ie. GeneralViewNode

internal class GeneralViewNode
{
    // Properties variables
    private string displayText = string.Empty;
    private string name = string.Empty;
    private string description = string.Empty;

    internal TaskHost taskHost = null;
    internal IDtsConnectionService connectionService = null;

    internal GeneralViewNode(TaskHost taskHost, IDtsConnectionService connectionService)
    {
        this.taskHost = taskHost;
        this.connectionService = connectionService;

        // Extract common values from the Task Host
        name = taskHost.Name;
        description = taskHost.Description;

        // Extract values from the task object
        HelloWorldTask.HelloWorldTask task = taskHost.InnerObject as HelloWorldTask.HelloWorldTask;
        if (task == null)
        {
            string msg = string.Format("Type mismatch for taskHost inner object.");
            throw new ArgumentException(msg);
        }

        displayText = task.DisplayText;
    }

    #region Properties

    [Category("General"), Description("Task name")]
    public string Name
    {
        get
        {
            return name;
        }
        set
        {
            string v = value.Trim();
            if (string.IsNullOrEmpty(v))
            {
                throw new ApplicationException("Task name cannot be empty");
            }
            name = v;
        }
    }

    [Category("General"), Description("Task description")]
    public string Description
    {
        get
        {
            return description;
        }
        set
        {
            description = value.Trim();
        }
    }

    [Category("General"), Description("Text to display")]
    public string DisplayText
    {
        get
        {
            return displayText;
        }
        set
        {
            displayText = value;
        }
    }

    #endregion
}

As you can see, we have the single property for the task (DisplayText), and two of the common Task properties – Name, and Description.

Create a member variable for this node class:

private GeneralViewNode generalNode;
internal GeneralViewNode GeneralNode
{
    get { return generalNode; }
}

Implement the IDTSTaskUIView interface. These methods are called when the UI is opened, and when the UI clicks OK.

#region IDTSTaskUIView Members

public void OnCommit(object taskHost)
{
    TaskHost host = taskHost as TaskHost;
    if (host == null)
        throw new ArgumentException("Arugment is not a TaskHost.", "taskHost");

    HelloWorldTask.HelloWorldTask task = host.InnerObject as HelloWorldTask.HelloWorldTask;
    if (task == null)
    {
        throw new ArgumentException("Arugment is not a HelloWorldTask.", "taskHost");
    }

    host.Name = generalNode.Name;
    host.Description = generalNode.Description;

    // Task properties
    task.DisplayText = generalNode.DisplayText;
}

public void OnInitialize(IDTSTaskUIHost treeHost, TreeNode viewNode, object taskHost, object connections)
{
    this.generalNode = new GeneralViewNode(taskHost as TaskHost, connections as IDtsConnectionService);
    this.propertyGrid.SelectedObject = generalNode;
}

public void OnLoseSelection(ref bool bCanLeaveView, ref string reason)
{
}

public void OnSelection()
{
}

public void OnValidate(ref bool bViewIsValid, ref string reason)
{
}

#endregion

Add new Windows Form – HelloWorldTaskMainWnd.cs. Change the inheritance from Form to DTSBaseTaskUI.

Note, after inheriting from DTSBaseTaskUI, you’ll get an error when trying to open the code in Design view in Visual Studio 2008. This is caused by DTSBaseTaskUI not having a default constructor. We’re aware of the issue, and will hopefully be able to resolve it in a cumulative update. It won’t cause issues at runtime.

Add some members for the UI properties we’ll use to initialize DTSBaseTaskUI.

private const string Title = "Hello World Task";
private const string Description = "Displays a message box";
private static Icon TaskIcon = new Icon(typeof(HelloWorldTask), "Task.ico");

Add a member for GeneralView class.

private GeneralView generalView;
public GeneralView GeneralView
{
    get { return generalView; }
}

Add a constructor.

public HelloWorldTaskUIMainWnd(TaskHost taskHost, object connections) :
    base(Title, TaskIcon, Description, taskHost, connections)
{
    InitializeComponent();
    
    // Setup our views
    generalView = new GeneralView();
    this.DTSTaskUIHost.FastLoad = false;
    this.DTSTaskUIHost.AddView("General", generalView, null);
    this.DTSTaskUIHost.FastLoad = true;
}

Note, the first parameter you use for AddView() is the text that gets displayed on the left of the main window.

Finally, add a new class that implements IDtsTaskUI to control the launching of your new window.

public class HelloWorldTaskUI : IDtsTaskUI
{
    private TaskHost taskHost = null;
    private IDtsConnectionService connectionService = null;

    #region IDtsTaskUI Members

    public void Delete(IWin32Window parentWindow)
    {
    }

    public ContainerControl GetView()
    {
        return new HelloWorldTaskUIMainWnd(taskHost, connectionService);
    }

    public void Initialize(TaskHost taskHost, IServiceProvider serviceProvider)
    {
        this.taskHost = taskHost;
        this.connectionService = serviceProvider.GetService(typeof(IDtsConnectionService)) as IDtsConnectionService;
    }

    public void New(IWin32Window parentWindow)
    {
    }

    #endregion
}

And there you go! Be sure to add the task UI assembly to the GAC before trying it out in Visual Studio.

I should mention that Kirk’s book has a chapter on creating custom tasks that gives a lot more details than I have here. If you’re interested in extending the SSIS platform, Kirk’s book (or the “SSIS White Book” as I like to call it) is an invaluable resource.

I’ve uploaded the source for the highly useful Hello World Task (and its UI) to my Sky Drive public folder. I developed this using SQL Server 2008, but it should also work the same way in 2005.

I’ll also be turning this into a community sample to be published on Codeplex (along with our other new development samples) sometime soon.

Enjoy!

Leave a Comment
  • Please add 4 and 6 and type the answer here:
  • Post
  • Other then just strings as properties, what about drop down lists with events?  Got any samples for that?

  • Do you mean like a drop down list of Connection Managers, with a "<New..>" option? Or a drop down list of SSIS event names?

    I have a sample ready for the first one - I just need to polish up a bit, and we'll publish it on Codeplex.

  • Some new samples have been added to the SSIS community samples project on codeplex . Delimited Flat File

  • If you are having trouble adding the reference

    "Microsoft.DataTransformationServices.Controls"

    You have to go to the following folder to add the reference,

    "C:\WINDOWS\assembly\GAC_MSIL\"

    And then navigate to said dll

    Hope this helps.

    Don

  • Matt, did you finally published the sample with "down list of Connection Managers etc." on Codeplex as mentioned above?

    I was not able to find them and I would really appropriate such samples even if they are not polished.

  • Hi Jan,

    You'll find it here: sqlsrvintegrationsrv.codeplex.com/.../17648

  • Hello Matt,

    I am creating a dynamic package in Script task to insert data in more than 1 table in single data flow task. However I am getting error inside forloop at ReinitializeMetadata() method right after where i am setting the Accessmode for destination table. This error comes after 1st iteration.

    I have provided detailed error information as well as code here

    social.msdn.microsoft.com/.../daa4dcc8-7c61-4137-94e0-fb725c45c0f7

    please help.

Page 1 of 1 (7 items)