开发者

Binding error in an activity designer using ActivityFunc<>

开发者 https://www.devze.com 2023-04-11 07:27 出处:网络
I\'m having a hard time getting a custom activity designer of mine to display in the workflow designer. My activity includes an activity func, and I\'ve already found a number of blo开发者_如何学JAVAg

I'm having a hard time getting a custom activity designer of mine to display in the workflow designer. My activity includes an activity func, and I've already found a number of blo开发者_如何学JAVAgs posts dealing with them here, here, here and here.

The custom activity has an ActivityFunc<> as an input argument, and I need to expose the func in a designer as drop zone in which the user can place an "inner" activity (à la TransactionScope).

The custom activity is authored in XAML, and the func's declaration looks like this:

<x:Property Name="CompletionTest" Type="ActivityFunc(sdscmt:DmeTask, sdsav:WfPatient, sdscmc:DmeClinicalElement, x:Boolean)" />

The XAML also contains an InvokeFunc<> activity matching the CompletionTest property.

The activity designer follows the recommendations outlined in the blog posts mentionned above. In particular, it overrides OnModelItemChanged to initialize the CompletionTest property:

if (this.ModelItem.Properties["CompletionTest"].Value == null)
{
    this.ModelItem.Properties["CompletionTest"].SetValue(
        new ActivityFunc<DmeTask, WfPatient, DmeClinicalElement, bool>()
        {
            Argument1 = new DelegateInArgument<DmeTask>
            {
                Name = "task"
            },
            Argument2 = new DelegateInArgument<WfPatient>
            {
                Name = "patient"
            },
            Argument3 = new DelegateInArgument<DmeClinicalElement>
            {
                Name = "element"
            },
            Result = new DelegateOutArgument<bool>
            {
                Name = "success"
            },
        });
}

The designer's XAML looks like this:

<sap:ActivityDesigner x:Class="SoftInfo.Dme.ServicesDme.Workflow.Design.PerformTaskDesigner" ... >
    <StackPanel>
        <sap:WorkflowItemPresenter AllowedItemType="{x:Type sa:Activity}" Background="Transparent" MinWidth="150" MinHeight="100" HintText="Drop the completion test here" Margin="5,5,5,5" Item="{Binding Path=ModelItem.CompletionTest.Handler, Mode=TwoWay}" />
    </StackPanel>
</sap:ActivityDesigner>

After all this, whenever I place an instance of my custom activity into a workflow, I get a red box labelled "Could not generate view for PerformTask" where my designer should appear. The box's tooltip indicates that an exception occurred from within the designer :

    System.Windows.Markup.XamlParseException: A 'Binding' cannot be set on the 'Item' property of type 'WorkflowItemPresenter'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.

I don't understand what I'm doing wrong. I've used WorkflowItemPresenter many times before, and this is the first time I've gotten this binding error.


That's kind of a messed up way to go about this. The error may be incorrect; the inner exception would tell the tale I'd wager (sorry, listening to Game of Thrones while I work today).

I implement IActivityTemplateFactory to configure my activity delegates:

public sealed class MyActivity: NativeActivity, IActivityTemplateFactory
{
    public const string ChildArgumentName = "theArgument";

    public ActivityFunc<object, bool> Child { get; set; }

    Activity IActivityTemplateFactory.Create(System.Windows.DependencyObject target)
    {
        return new MyActivity()
        {
            Child = new ActivityFunc<Capture, bool>
            {
                Argument = new DelegateInArgument<object>(ChildArgumentName )
            }
        };
    }
}

Notice a couple things here. Yes, the design surface instantiates an instance of your Activity only to use it to create another instance of the same Activity (configured properly, of course), but this method is bulletproof. You can, if requirements dictate, move your implementation of IATF elsewhere, but if it doesn't matter who cares?

Notice that I declare in my Activity's design what the name of the Func's argument will be. You can do this in a more generic fashion (custom attributes, etc), but if your Activities fit together in a predictable fashion, I have found this is the simplest manner to automatically wire up child Activities with their parents. YMMV.

In the designer, its similar:

<sap:WorkflowItemPresenter
    HintText="Add child here"
    Item="{Binding ModelItem.Child.Handler}" />

that's all you need. If the signature of the Activity doesn't match, it won't fit. The child also takes advantage of IATF to bind itself to its parent:

public sealed class ChildActivity : NativeActivity<bool>, IActivityTemplateFactory
{
    public InArgument<object> Target { get; set; }

    Activity IActivityTemplateFactory.Create(System.Windows.DependencyObject target)
    {
        return new ChildActivity
        {
            Target = new VisualBasicValue<object>(MyActivity.ChildArgumentName)
        };
    }
 }

If you expect your child Activity to be dropped on different targets, you might have to do some annoying inspection of the target DependencyObject, which will allow you to inspect the current workflow tree. (Please note, I'm not 100% familiar with the implicit conversion behavior of VisualBasicValue, so you might have some compiler errors with the above code!)

Another option if you have to be extra tricky (drop an Activity from the toolbox, then drag it somewhere else where you will have to re-do your inspection) is to inspect the current state of the workflow from within CacheMetadata. I haven't done this, but I believe you can and update your registrations with the design surface to reflect the current state of the workflow.

0

精彩评论

暂无评论...
验证码 换一张
取 消