locked
Custom FlowSwitch Designer RRS feed

  • Question

  • I am trying to add my own FlowSwitchDesigner class and bind the FlowSwitch<string> activity to it, and am currently just trying to get it to work for FlowSwitch<string> (and will eventually want FlowSwitch<int> as well).  My goal is to both show the expression being switched on (so printouts will show it), and to let the user edit it in-place on the flowchart.

    I read the http://social.msdn.microsoft.com/Forums/en-US/wfprerelease/thread/6279d356-e5b7-47f2-b2d3-8d7cfe6b33e4 thread, and some others.  That thread seems to end with my current question.

    In my designer XAML I have:

    <sapv:ExpressionTextBox Grid.Row="0" Grid.Column="1" Expression="{Binding Path=ModelItem.Expression, Mode=TwoWay, Converter={StaticResource ArgumentToExpressionConverter}, ConverterParameter=In}" OwnerActivity="{Binding Path=ModelItem}" ExpressionType="s:String" />
    


    And this almost works. I see my custom designer when I drag a FlowSwitch<string> onto my designer's FlowChart. And it has the edit box. But when I enter text in the ExpressionTextBox, I don't see the change reflected in the Expression field in the Properties pane. And similarly, if I edit the field in the Properties pane, I don't see the change reflected in the ExpressionTextBox in the workflow itself (and, in fact, previous text will clear and it will go back to "Enter a VB expression" when I enter text from the Properties pane - so something is hooked up...). (And I expect these to work, as the designers for my own classes update in this fashon).

    I'm guessing the problem is with the ExpressionType (since Expression is type "Activity<T>" and not actually "string"). I tried assigning ExpressionType to typeof(Activity<string>) in the XAML's OnModelItemChanged in the code-behind, but that didn't seem to help.  I'm not sure how to do this in XAML (which I'm still relatively new to).

    P.S. Also, the border of the ExpressionTextBox appears red when I enter text into it, and I'm not sure why. I mention it here in case it's related.

    Also, I can post more code it it would help.

    Monday, August 1, 2011 9:24 PM

Answers

  • Unfortunatly, I did find one (big) problem with this.  I can drag a FlowSwitch to my FlowChart and set it's Expression.  But when i try to connect it to another activity (e.g., connect a case), I get the following exception (CustomFlowSwitchDesigner is my custom designer):

    -------------------------
    Workflow Designer
    -------------------------
    InvalidCastException:
    Unable to cast object of type 'WorkflowActivities.CustomFlowSwitchDesigner' to type 'System.Activities.Core.Presentation.FlowSwitchDesigner'.
    -------------------------
       at System.Activities.Core.Presentation.FlowSwitchLink`1.OnIsDefaultPropertyChanged(DependencyPropertyChangedEventArgs e)
       at System.Activities.Core.Presentation.FlowSwitchLink`1.OnIsDefaultCasePropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
       at System.Windows.DependencyObject.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
       at System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs args)
       at System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType)
       at System.Windows.DependencyObject.SetValueCommon(DependencyProperty dp, Object value, PropertyMetadata metadata, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType, Boolean isInternal)
       at System.Windows.DependencyObject.SetValue(DependencyProperty dp, Object value)
       at System.Activities.Core.Presentation.FlowSwitchLink`1..ctor(ModelItem flowSwitchMI, T caseValue, Boolean isDefault)
       at System.Activities.Core.Presentation.GenericFlowSwitchHelper.CreateGenericFlowSwitchLink[T](ModelItem currentMI, T caseValue, Boolean isDefault)
    -------------------------

    I can't seem to inherit from FlowSwitchDesigner either (is it a private class?).


    Yes, FlowSwitchDesigner is internal, which is why you can't inherit it.

    This is also probably the reason that Ye Yu posted code which works by modifying the visual tree in the original post you linked to.
     
    I feel sorry for helping you spend more time on this avenue only to get blocked on this particular issue, I didn't know or had forgotten that this particular dependency existed in the FlowchartDesigner code. :-(
     
    To be honest Flowchart designer hasn't had work done on it yet to make it extensible, quite the reverse - making the class internal leaves it less extensible now, but with more windows open to make it extensible in the future. I can forward this thread on to the designer team - they'll be interested in seeing the ways you wanted to be able to customize it for future reference.
    Tim

    • Marked as answer by Andrew_Zhu Thursday, August 11, 2011 8:30 AM
    Wednesday, August 3, 2011 1:24 AM

All replies

  • Hi Andy,

    In this case it may be surprising, but ArgumentToExpression isn't actually needed. The reason is that unlike many other places you see Expressions in the property grid in the Workflow Designer, FlowSwitch.Expression is not actually an InArgument<T>, it is an Activity<T>

    Converter={StaticResource ArgumentToExpressionConverter},

    The upshot of this is that you don't actually need an ArgumentToExpression converter at all.

    Other notes:
    - The border of the ETB being red is just a little clue to tell you that something is going wrong with the binding
    - ExpressionType="s:String" should be right

    Tim

    • Proposed as answer by Tim Lovell-Smith Tuesday, August 2, 2011 6:08 AM
    • Marked as answer by Andy Jacobs Tuesday, August 2, 2011 2:11 PM
    • Unmarked as answer by Andy Jacobs Wednesday, August 3, 2011 6:48 PM
    Tuesday, August 2, 2011 6:05 AM
  • Thanks - that worked!  And I love REMOVING code.

    I don't like having duplicate code, so as I alluded to above, I'd like to have one SwitchDesigner handle both strings and ints, if possible.  I found the following seems to work, but was wondering if there might be a more elegant solution (perhaps one involving just XAML)?

    In the code-behind for the switch designer, I added the following:

    protected override void OnModelItemChanged(object newItem)
    {
    	base.OnModelItemChanged(newItem);
    
    	if (null != ModelItem)
    	{
    		if (ModelItem.ItemType == typeof(FlowSwitch<int>))
    		{
    			ExpressionText.ExpressionType = typeof(int);
    		}
    		else if (ModelItem.ItemType == typeof(FlowSwitch<string>))
    		{
    			ExpressionText.ExpressionType = typeof(string);
    		}
    	}
    }
    
    

    And it seems to now work for both ints and strings.  Is there an easier way?

    Tuesday, August 2, 2011 2:19 PM
  • Hi again Andy,
    That code-behind approach is about as good as it gets. Although you could use reflection instead of switching (GetGenericArguments).
    Tim
    Tuesday, August 2, 2011 3:23 PM
  • Unfortunatly, I did find one (big) problem with this.  I can drag a FlowSwitch to my FlowChart and set it's Expression.  But when i try to connect it to another activity (e.g., connect a case), I get the following exception (CustomFlowSwitchDesigner is my custom designer):

    -------------------------
    Workflow Designer
    -------------------------
    InvalidCastException:
    Unable to cast object of type 'WorkflowActivities.CustomFlowSwitchDesigner' to type 'System.Activities.Core.Presentation.FlowSwitchDesigner'.
    -------------------------
       at System.Activities.Core.Presentation.FlowSwitchLink`1.OnIsDefaultPropertyChanged(DependencyPropertyChangedEventArgs e)
       at System.Activities.Core.Presentation.FlowSwitchLink`1.OnIsDefaultCasePropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
       at System.Windows.DependencyObject.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
       at System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs args)
       at System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType)
       at System.Windows.DependencyObject.SetValueCommon(DependencyProperty dp, Object value, PropertyMetadata metadata, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType, Boolean isInternal)
       at System.Windows.DependencyObject.SetValue(DependencyProperty dp, Object value)
       at System.Activities.Core.Presentation.FlowSwitchLink`1..ctor(ModelItem flowSwitchMI, T caseValue, Boolean isDefault)
       at System.Activities.Core.Presentation.GenericFlowSwitchHelper.CreateGenericFlowSwitchLink[T](ModelItem currentMI, T caseValue, Boolean isDefault)
    -------------------------

    I can't seem to inherit from FlowSwitchDesigner either (is it a private class?).

    Tuesday, August 2, 2011 8:21 PM
  • Unfortunatly, I did find one (big) problem with this.  I can drag a FlowSwitch to my FlowChart and set it's Expression.  But when i try to connect it to another activity (e.g., connect a case), I get the following exception (CustomFlowSwitchDesigner is my custom designer):

    -------------------------
    Workflow Designer
    -------------------------
    InvalidCastException:
    Unable to cast object of type 'WorkflowActivities.CustomFlowSwitchDesigner' to type 'System.Activities.Core.Presentation.FlowSwitchDesigner'.
    -------------------------
       at System.Activities.Core.Presentation.FlowSwitchLink`1.OnIsDefaultPropertyChanged(DependencyPropertyChangedEventArgs e)
       at System.Activities.Core.Presentation.FlowSwitchLink`1.OnIsDefaultCasePropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
       at System.Windows.DependencyObject.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
       at System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs args)
       at System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType)
       at System.Windows.DependencyObject.SetValueCommon(DependencyProperty dp, Object value, PropertyMetadata metadata, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType, Boolean isInternal)
       at System.Windows.DependencyObject.SetValue(DependencyProperty dp, Object value)
       at System.Activities.Core.Presentation.FlowSwitchLink`1..ctor(ModelItem flowSwitchMI, T caseValue, Boolean isDefault)
       at System.Activities.Core.Presentation.GenericFlowSwitchHelper.CreateGenericFlowSwitchLink[T](ModelItem currentMI, T caseValue, Boolean isDefault)
    -------------------------

    I can't seem to inherit from FlowSwitchDesigner either (is it a private class?).


    Yes, FlowSwitchDesigner is internal, which is why you can't inherit it.

    This is also probably the reason that Ye Yu posted code which works by modifying the visual tree in the original post you linked to.
     
    I feel sorry for helping you spend more time on this avenue only to get blocked on this particular issue, I didn't know or had forgotten that this particular dependency existed in the FlowchartDesigner code. :-(
     
    To be honest Flowchart designer hasn't had work done on it yet to make it extensible, quite the reverse - making the class internal leaves it less extensible now, but with more windows open to make it extensible in the future. I can forward this thread on to the designer team - they'll be interested in seeing the ways you wanted to be able to customize it for future reference.
    Tim

    • Marked as answer by Andrew_Zhu Thursday, August 11, 2011 8:30 AM
    Wednesday, August 3, 2011 1:24 AM