Dependency Property

I have a custom activity derived from HandleExternalEventActivity that I have generated from a interface using the wca.exe tool. On this activity there is a dependencyproperty Approved (type bool) generated. The value of the property is set in the eventhandler for the activity.

I have placed my activity inside a while activity that I have set up to have a Declarative Rule Condition that is set up like !this.pageApproved.Approved

When I debug my custom activity and more precisely the get/set of the dependencyproperty. I see that when I send an event to the workflow that sets the property to true everything seems nice

[ValidationOptionAttribute(ValidationOption.Required)]

publicbool Approved {

get {

return ((bool)(this.GetValue(PageApproved.ApprovedProperty)));

}

set {

this.SetValue(PageApproved.ApprovedProperty,value);

}

}

meaning the set method is called and value is set to true. But later when the get method is called to evaluate the while condition the get returns false causing the while to continue....

A strange thing is that the constuctor of my activity is called twice even though I have only one instance of the activity in the workflow. And furthermore the dependencyproperty is static declared which should mean that even if there is several instances they should all get same value.

So for some reason it seems that eihter is the Dependency property not saved properly or it is something from the outside that sets the value back (not through the set method though which I have a breakpoint in).

Anyone who has a clue on what this is caused by?

Thanks! /Johan

[2458 byte] By [jbearfoot] at [2007-12-21]
# 1

Johan,

You are running into a problem with activity execution contexts. Your constructor is being called twice because your activity is being cloned to be used in a new context.

See Nate's excellent blog article on this for more information and details on the solution.

http://blogs.msdn.com/advancedworkflow/archive/2006/03/21/557121.aspx

The short answer is that in your event handler, where you set or get the property, you want to use the sender or event arguments as the object to work from to get a handle on your activity. This ensures that you are working with the right instance.

Matt

MattMilner-Pluralsight at 2007-9-10 > top of Msdn Tech,Software Development for Windows Vista,Windows Workflow Foundation...
# 2

Thank you Matt!

It was a very interesting reading and I think I understand a little more about how activities gets executed. What I don't know though is how this should be applied to my particular case. Let me show you my case.

As i said I have a custom activity PageApproved that contains a property PageApproved as below:

public partial class PageApproved : HandleExternalEventActivity {

public static DependencyProperty ApprovedProperty = DependencyProperty.Register("Approved", typeof(bool), typeof(PageApproved));

[ValidationOptionAttribute(ValidationOption.Required)]

public bool Approved {

get {

return ((bool)(this.GetValue(PageApproved.ApprovedProperty)));

}

set {

this.SetValue(PageApproved.ApprovedProperty, value);

}

}

...

This activity listens for a event from the host and sets the property Approved from the args passed with the event as below:

protected override void OnInvoked(System.EventArgs e) {

EPiServer.WF.Attributes.WFPageApprovedEventArgs castedE = ((EPiServer.WF.Attributes.WFPageApprovedEventArgs)(e));

this.Approved = ((bool)(castedE.Approved));

//PageApproved thisActivity = this.GetActivityByName("pageApproved", false) as PageApproved;

//thisActivity.Approved = ((bool)(castedE.Approved));

this.PageLink = ((EPiServer.Core.PageReference)(castedE.PageLink));

}

And in my workflow this activity is placed in a while activity which has a Declarative rule condition that is "!this.pageApproved.Approved".

The problem as I have said earlier is that the value I set in OnInvoked in the activity is not the same as the while activity reads in its evaluation.

After reading the article I guess I should replace this.Approved (which I assume executes in another context and the one that the while activity evaluates against) in OnInvoked with something that points to activity in the same context as the while activity. i just don't know how to get a handle to that activity.... I tried the outcommented code but that will not help since this.GetActivityByName returns the same instance as "this". I guess I need something to replace "this" with.

I cant really use anything in args or sender since the activity is an event called from the app that hosts the workflow.

Another option might be to change the declarative rule condition to something else than "!this.pageApproved.Approved", the question is what?

Any suggestions?

Thanks /Johan

jbearfoot at 2007-9-10 > top of Msdn Tech,Software Development for Windows Vista,Windows Workflow Foundation...
# 3

Hi again!

I managed to solve my problem (in my opinon though a ugly way) by replacing

thisActivity.Approved = ((bool)(castedE.Approved));

with

((PageApproved)this.Parent.Parent.GetActivityByName("pageApproved")).Approved = ((bool)(castedE.Approved));

So what I do is that I traverse my workflow upwards to while activity and then down again to my activity and then I get right instance....

This is obviously not good because then I have a dependency in my activity to the workflow, that is I have to know from the activity how the workflow is.

What I would like is some method so you from a copied activity can get a handle to the "original" activity (I mean the activity future copies will be copied from) like GetTemplateActivity or something similar. Perhaps it already exists but I have missed it....

/Johan

jbearfoot at 2007-9-10 > top of Msdn Tech,Software Development for Windows Vista,Windows Workflow Foundation...
# 4

Actually, the OnInvoked event gets raised by the HandleExternalEvent activity. The sender is the instance that raised it. So, you can use something like this:

PageApprovedActivity thisActivity = sender as PageApprovedActivity;

thisActivity.Approved = true;

This guarantees that the instance you are working on is the actual instance of your activity that you want.

Matt

MattMilner-Pluralsight at 2007-9-10 > top of Msdn Tech,Software Development for Windows Vista,Windows Workflow Foundation...
# 5

Thanks for your replies, I really appreciate that you take time to answer.

Unfortuantely I cant make that work.

My activity is generated by use of tool wca.exe with option /i meaning the generated code includes the sender object. The generated code will then contain a DependencyProperty Sender of type object. The OnInvoked method will still just take a simple parameter of type EventArgs. Inside the OnInvoked method the following line is generated from wca tool

this.Sender = this.ParameterBindings["sender"].Value;

When I debug the code sender is always null. Should I in my workflow that contains the Activity bind the sender property to something? I cant really figure out to what that should be, the activity is a custom event activity meaning its triggered from a call from host. As I understand from Matts last reply the sender should be the activity itself (the template instance that is, not the current spawned activity) but I cant bind a property on an activity to itself.

It feels like I am missing something.

Thanks

Johan

jbearfoot at 2007-9-10 > top of Msdn Tech,Software Development for Windows Vista,Windows Workflow Foundation...
# 6

Johan,

the first question I would have is "why are you using the /i switch on the wca tool?" Do you have some reason for needing/wanting that configuration?

My comments were related to the event handler that you would define in the workflow itself. I see that you are working within the custom activity itself.

To answer your question in the last post regarding the sender; I believe the way you have the setup, it would be the sender you passed with the original event from the host. This would have to be a serializable object and generally isn't passed.

To fix your problem with the condition, here is what I would do.

In the workflow, bind the ApprovedProperty of your custom activty to a field in the workflow. Then in your condition, test the field in the workflow rather than trying to get a handle to the the current activity. I tried this out and it worked like a charm. You can also try to work with the DynamicActivity property of the while activity, but I found that it is not usually set when the condition is evaluated so it doesn't work well in this situation.

The custom activity code that was generated for you should work fine in the solution I have mentioned above. You just need a way to store the result locally in the workflow so it is easy to reference from the rule.

Matt

MattMilner-Pluralsight at 2007-9-10 > top of Msdn Tech,Software Development for Windows Vista,Windows Workflow Foundation...
# 7

Hi and thanks for the response again. The reason I tried to use option /i was a previous suggestion that sender would be the template activity. I think I might have misunderstand that suggestion.

Your suggested solution about a field works fine.

Before that I solved it by using a construction like

public PageApproved TemplateActivity

{

get

{

if (this.IsDynamicActivity)

{

Activity activity = this.Parent;

while (activity.Parent != null && activity.IsDynamicActivity)

activity = activity.Parent;

return activity.GetActivityByName(this.Name) as PageApproved;

}

else

return this;

}

}

and then I set my property in the eventhandler like

TemplateActivity.Approved = castedE.Approved;

which works well. In that case if I reuse my activity in another workflow where its not placed in a activity that will spawn "copies", (i.e. while activity) it will return itself while it in the latter case returns the "template" activity.

jbearfoot at 2007-9-10 > top of Msdn Tech,Software Development for Windows Vista,Windows Workflow Foundation...

Software Development for Windows Vista

Site Classified