| Chris's profileChris's SpacePhotosBlogLists | Help |
Chris's SpaceRandom information on software development |
|||||
Writing Better CodeI’ve seen many cases where developers working on projects with me have written code that technically works, but is very difficult to maintain or expand. In fact writing good code has become a bigger problem for us than writing code that works. Good coding practices are learned from experience and up until last week I thought they couldn’t be learned from a book. I picked up Martin Fowler’s book Refactoring and was amazed at how well he captured the concepts that I’ve learned over time. It makes me wish I would have read this book back when it was originally published. I think this is going to become a book I recommend to all the new developers we hire. Wss3 Workflow Tools ReleasedAfter many months of tinkering and playing with the code to make it easier to use and more efficient, we’re finally done. Well, at least until version 2.0. You can download the current release here. This project is an attempt by myself and another developer Wouter Van Vugt to create an easier way for developers to create WSS 3.0 Workflow forms in ASP.NET. The project currently includes all of the base classes you’ll need to allow creation of simple forms as ASCX controls as wel all…
We’re working on documentation, but in the mean time check out Wouter’s blog for some videos on how to use the system. WSS Workflow in Boston on October 20thI'll be teaching a WSS 3.0 Workflow course in Boston, MA on October 20th. Anyone needing a deep dive into the details of how to build custom workflows using VS.NET and create custom forms using ASP.NET or InfoPath might want to check it out. If you're interested, you can get the first module on the Ted Pattison Group's site. WSS 3.0 Workflow Bulk Task ProcessingOne of the most common questions I get when someone has worked with WSS 3.0 Workflow is, "How can I approve or reject all of my tasks on one screen?". They're tired of clicking one task, approving it, then clicking the next one and so on. All they want is a simple page that will list all of their tasks in one place and allow them to run down the list checking approve or reject and submitting when their done. There is a bulk approval option using InfoPath forms, but in my opinion it's not that useful. You can create a form that displays common controls. For example you can click an approve or reject button and approve or reject all of your tasks, but have no control over an individual task. Once you've made your decision the submission goes into a queue and is processed later. This isn't what they were looking for. In response to several requests by students in my past classes (you know who you are), we added support for bulk approvals in the WSS3 Workflow Tools CodePlex project. Using the same ASCX forms based approach I described in a previous post, we created a host page that will easy the process of creating bulk task processing forms. WSS3 Workflow Tools Bulk Task ProcessingThere were two key aspects of the built in support for bulk processing that needed to be remedied. First, access to the bulk processing should happen from the list containing the workflow. Second, the task processing form should allow different actions to be performed on each task. The solution to this was to build a special feature that added a new item to the actions menu of document libraries that directs the user to page of bulk task links. On this page the user will get a list of all the types of tasks that can be bulk processed. Once the user selects the type of task to bulk process they click the link and are directed to a final page that will display the all the tasks. This form is the only part that is defined specifically for the task being processed. The job of the form is to display the tasks using data from the framework and gather the results and pass them back to the framework. Building a Custom Bulk Task FormAdding a custom bulk form to a project that already has a task form in place is a simple process made up of 2 steps:
Creating a custom bulk task form is similar to creating a custom task form, with one difference; you're displaying multiple tasks instead of just one. This is handled by the TaskItemCollection class. When creating a custom task form, your .ASCX file derives from TaskControl which loads and saves using a TaskData object. When working with multiple tasks we need to return multiple TaskData objects. We also need to be able to identify a task, so a task id needs to be attached to each entry. This is handled by using a TaskItem class which derives from TaskData and then a TaskItemCollection that contains multiple TaskItem objects. The other difference between a task form and a bulk task forms is the base class. Bulk task forms derive from BulkTaskControl. Once the .ASCX file derives from BulkTaskControl, the TaskItemCollection is bound to a ASP.NET Repeater control and the task data is displayed on the screen. In this example I chose to use a drop down list to select the type of action to perform on the task. When the page loads, the LoadData method binds the TaskItemCollection returned by the GetData method to the Repeater control. The last step in creating the form is to extract the data from the form and submit the changes to the framework. Since the base class BulkTaskControl expects a TaskItemCollection, it will need to be created in the SaveData method and provided back to the framework. One detail to note in the code, make sure the AllowExtendedProperties property is set to true. If you don't set this value and try to use the example (which is based on task extended properties, not content types) it will fail. Now that the form is complete, all that's left is to register the bulk task forms in the workflow.xml file. Just like all of the forms in the WSS 3 Workflow Tools project, bulk task forms are registered using the workflow template's metadata. The first two entries required allow the framework to determine if bulk task forms exists so the feature button can be enabled. These metadata tags are Task_BulkIds and Task_nName. The Task_BulkIds is a comma delimited list of integer ids of tasks to be used that will map directly to the Task_nName metadata tags. For each number in the id list, one name entry exists. This lets the framework know whether to display the feature button and how to name the tasks in the following display page. Once the user has chosen the task to process in bulk, the framework will need to load the appropriate .ASCX file. This is where the BulkTask_nFormUrl is used. The number in the entry maps to the same id that the task form control receives. That's all it takes to add a custom bulk task for to an application that already has a task form. The goal of this framework was to make the process of building a bulk task form as simple as possible to allow you to focus on the business logic of your task and page and not the framework. For more information check out the WSS 3.0 Workflow Tools project website on CodePlex. You can download the sample code here. WSS 3.0 Workflow Task FormsUsing the WSS 3.0 Workflow Tools to add task forms to a WSS 3.0 workflow is a simple process. Just like Association and Initiation forms, the Task form is based on a hosted .ASCX control in a standard hosting page. The primary difference between association/initiation forms and task forms is how the data being transferred is stored. Association/Initiation forms use a string to transfer data between the WSS 3.0 framework and the forms. What is stored in this string is entirely up to the developer, although the data is commonly stored as XML. In Task forms, the data being transferred is stored as a set of named properties. How these properties are stored is based on which method of task creation is chosen. There are two ways to store data in workflow tasks: custom content types and extended properties. The primary difference between the two methods is how strongly typed the data is. Content types provide strong typing but requires that all fields in the task be defined at design time. Extended properties don't provide the same level of typing but allow items to be stored in a property bag. The WSS 3.0 Workflow Tools support both methods. Creating a Workflow TaskFor those of you that are already familiar with creating workflow tasks, you can skip this part. For those of you that aren't this will be a quick introduction to creating workflow tasks and waiting for them to change. The key steps in using workflow tasks are to create the task, wait for it to change, and complete the task. Each of these steps are performed with special SharePoint workflow activities:
In this example, the task is being created and then the workflow waits for it to change. As soon as it changes the workflow assumes it's done. In a real world scenario, the OnTaskChanged is often placed in a While look that will continue to look until some value in the task is set. At that point, the task is completed and the workflow continues, but for now this simple scenario will work. Creating a custom Task formCreating a custom task form is very similar to custom Association and Initiation forms. The only difference is the base class. Instead of deriving from ConfigurationControl, you derive from TaskControl. The only difference between these two classes is the object it returns from the GetData and SaveData methods. In the ConfigurationControl this class is ConfigurationData in TaskControl it's the TaskData. TaskData is the class the contains the logic that allows tasks to retrieve their values from a Content Type or extended properties. To access a value the overloaded[] operator is used to lookup a value from the content type or extended properties. TaskData also implements a custom TypeDescriptorProvider which allows the TaskData to be bound to using standard ASP.NET data binding constructors. The code below shows the markup for the page. Notice how the buttons utilize the CommandName of Commit to indicate to the framework the operation to perform and how they use the CommandArgument to indicate whether the button indicates approval or rejection. This command argument will be used later in the SaveData method. The code that supports the page overrides the LoadData and SaveData methods just like the Association or Initiation forms but this time the methods operate on a TaskData object. In the LoadData method the value MyFieldValue is used to populate the form. In the SaveData method the value is extracted from the form and placed back into the TaskData. There's also a Result value that's placed in the taskData object and it comes from the commandArgument parameter. This parameter is automatically provided to the form developer by the framework. When a button is clicked and it has a CommandName of commit, the CommandArgument value is provided to the SaveData method in the commandArgument parameter. This gives form developers a way to utilize the frameworks event handlers but still provide a value to the code handling the event. The last step is to register the form in the Workflow.xml file. This process is a little more complicated than the Association/Initiation forms. Since the form is attached to the task and not the workflow, a custom content type is required for task forms. The framework already provides a content type that maps the default task forms to the frameworks host forms. Setting the TaskListContentTypeId attribute in the Workflow.xml file tells the WSS 3.0 workflow framework which content type to use for new tasks. Now the custom task forms need to be mapped to the workflow using more metadata entries in the workflow template defined in the workflow.xml. This is done using the Task_nFormUrl tag, where n is the id of the task. The reason an ID is needed is to differentiate between multiple tasks in the same workflow. When a workflow is created using CreateTask a TaskType integer property is set that determines the type of task. When the custom form Url is retrieved, the TaskType value is used for n in the metadata tag. For example if the form for TaskType 0 is required, the metadata tag is b. Now that these changes are made, the custom task forms are ready to execute. I'll be adding another blog entry shortly to discuss how to process multiple tasks in the same UI, so instead of having to manually process several tasks in separate pages, all the tasks can be processed in one page. The code for this example can be downloaded here. |
|
||||
|
|