locked
Edit expression on FlowSwitch designer RRS feed

  • Question

  • Hello,

    I'm trying to follow the steps outlined by Ye in the previous post on adding an expression to the FlowSwitch designer.  I got it to work to the point where I can display the content of the expression in a read only label control.  When I try to add the ExpressionTextBox, I start to run into trouble. 

    The ExpressionTextBox displays fine, but after I try to exit the textbox, after editing the content, I either don't get a Leave event called (if using the approach described in the earlier forum post) or the binding fails, when I try to bind to the Expression property.  Below is my code.

    private void PutInEditMode()
        {
          WorkflowViewElement workflowViewElement = _flowSwitchMi.View as WorkflowViewElement;
          if (workflowViewElement != null && workflowViewElement.Content != null)
          {
            _normalFlowSwitchContent = workflowViewElement.Content;
            workflowViewElement.Content = new FlowSwitchEditModeUserControl();
    
            //Try binding expression to ExpressionTextBox. Something wrong, when leave focus of ExpressionTextBox
            //ExpressionTextBox expressionTextBox = new ExpressionTextBox();
            //Binding expressionBinding = new Binding("Expression");
            //expressionBinding.Source = _flowSwitchMi;
            //expressionBinding.Mode = BindingMode.TwoWay;
            //expressionBinding.Converter = new ArgumentToExpressionConverter();
            //expressionBinding.ConverterParameter = "In";
            //expressionTextBox.SetBinding(ExpressionTextBox.ExpressionProperty, expressionBinding);
            //expressionTextBox.ExpressionType = typeof(int);
            //expressionTextBox.OwnerActivity = _flowSwitchMi;
            //workflowViewElement.Content = expressionTextBox;
    
            //Try binding expression to Label content; that works fine.
            //Label expressionLabel = new Label();
            //Binding expressionBinding = new Binding("Expression");
            //expressionBinding.Source = _flowSwitchMi;
            //expressionBinding.Mode = BindingMode.OneWay;
            //expressionLabel.SetBinding(Label.ContentProperty, expressionBinding);
            //workflowViewElement.Content = expressionLabel;        
          }
        }

    The uncommented code sets the designer's content to be what I placed in a user control.  I'll get to that in a second.  Below that, in the first set of commented out code, I try to setup the binding using C#, rather than XAML. I abandoned this, in favour of doing the XAML approach with the user control, as normally I have better luck with that (but not in this case).  The second block of code binds the 'Expression' property to a label, and that works fine.

    Here's the XAML content of my user control (for FlowSwithcEditModeUserControl):

    <UserControl x:Class="ClientComponentDesignerLibrary.FlowSwitchEditModeUserControl"
           xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
           xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
           xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
           xmlns:s="clr-namespace:System;assembly=mscorlib"
           xmlns:sap="clr-namespace:System.Activities.Presentation;assembly=System.Activities.Presentation"
           xmlns:sapv="clr-namespace:System.Activities.Presentation.View;assembly=System.Activities.Presentation" 
           xmlns:sapc="clr-namespace:System.Activities.Presentation.Converters;assembly=System.Activities.Presentation"
           mc:Ignorable="d" 
           d:DesignHeight="70" d:DesignWidth="200">
      <Grid>
        <Grid.Resources>
          <ResourceDictionary>
            <sapc:ArgumentToExpressionConverter x:Key="ArgumentToExpressionConverter" />
          </ResourceDictionary>
        </Grid.Resources>
        <Grid.ColumnDefinitions>
          <ColumnDefinition Width="Auto"/>
          <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Label Content="Expression:"/>
        <sapv:ExpressionTextBox Grid.Column="1"
          Expression="{Binding Path=ModelItem.Expression, 
            Mode=TwoWay,             
            Converter={StaticResource ArgumentToExpressionConverter},             
            ConverterParameter=In }"
            ExpressionType="s:Int32"
          OwnerActivity="{Binding Path=ModelItem}"/>
      </Grid>
    </UserControl>
    

    As with the case where I use C# to create the binding, the user control displays fine, with both the label and ExpressionTextBox appearing ok.  When I finish typing in my expression, and leave focus, then I get a red rectangle around the ExpressionTextBox, implying an error of some sort.  (Note, I'm using a FlowSwitch<int>). 

    I would prefer to get this working with binding, rather than focusing on the Leave event, although I did try that briefly without any success.

    Thanks,

    Notre

    Tuesday, May 25, 2010 6:00 PM

Answers

  • I remember once you mentioned you would recreate your own flowswitch designer. are you doing that?

    could you replace with following xaml part?

    <sapv:ExpressionTextBox  x:Uid="conditionBox" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" x:Name="conditionBox"
                  MinLines="1" MaxLines="1" MaxWidth="450" MinWidth="450"
                  Expression="{Binding Path=ModelItem.Expression, Mode=TwoWay}"
                  ExpressionType="{x:Type TypeName=sys:int}"
                  OwnerActivity="{Binding Path=ModelItem, Mode=OneWay}"
                  AutomationProperties.Name="Expression expression"/>

    • Marked as answer by Notre Thursday, May 27, 2010 6:09 PM
    Thursday, May 27, 2010 5:25 AM

All replies

  • I think I made one mistake in my bindings, when using the XAML user control.  I forgot to set the DataContext.  So, I modifed the calling C# code to look like this:

    workflowViewElement.Content = new FlowSwitchEditModeUserControl(_flowSwitchMi);

    And modified the user control's .xaml.cs file to include this:

            public FlowSwitchEditModeUserControl(object dataContext)
            {
                this.DataContext = dataContext;
                InitializeComponent();
            }

    And modified the XAML of my ExpressionTextBox, to look like this:

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

    Still no luck; the symptoms are the same.  Please advise.

    Thanks,

    Notre

    Tuesday, May 25, 2010 7:17 PM
  • this is some sample of adding ETB, not totally the same as what you did, but maybe you may get some reference

    public partial class Window1
        {
             ModelProperty condition;
             ModelItem root;
            public Window1(ModelItem item)
            {
                root = item;
                condition = item.Properties["Expression"];
                InitializeComponent();
                this.Loaded += OnWindowLoaded;
            }
           
            public ModelItem ConditionValue
            {
                get
                {
                    return condition.Value;
                }
                set
                {
                    condition.SetValue(value);
                }
            }

            void OnWindowLoaded(object sender, RoutedEventArgs args)
                {
                    etb.OwnerActivity = root;
                    etb.ExpressionType = typeof(string);
                    Binding binding = new Binding("condition");
                    binding.Source = this;
                    binding.Path = new PropertyPath("ConditionValue");
                    etb.SetBinding(ExpressionTextBox.ExpressionProperty, binding);
                    btn.Command = DesignerView.CommitCommand;
                    btn.CommandTarget = etb;
                    etb.BeginEdit();
                }

            private void btn_Click(object sender, RoutedEventArgs e)
            {

                DesignerView.CommitCommand.Execute(etb);
              
                this.Close();
            }
           
        }

    if this still doesn't work for you, please let me know.

    Tuesday, May 25, 2010 10:17 PM
  • Hi Ye,

    This sample does work for me.  Now I'm trying to convert it so that I can bind from my XAML based user control.  I changed my code to look like this:

    <sapv:ExpressionTextBox Margin="10" Grid.Column="1"
                    Expression="{Binding Path=ConditionValue,
                        Mode=TwoWay,                         
                        Converter={StaticResource ArgumentToExpressionConverter},                         
                        ConverterParameter=In }"
                        ExpressionType="s:Int32"
                    OwnerActivity="{Binding Path=SwitchMi}"/>

    Which binds to this code:

          ModelProperty condition;

            public ModelItem ConditionValue
            {
                get
                {
                    return condition.Value;
                }
                set
                {
                    condition.SetValue(value);
                }
            }

            public ModelItem SwitchMi
            {
                get
                {
                    return _flowSwitchMi;
                }
                set
                {
                    _flowSwitchMi = value;
                }
            }

     

             private void PutInEditMode()
            {
                WorkflowViewElement workflowViewElement = _flowSwitchMi.View as WorkflowViewElement;
                if (workflowViewElement != null && workflowViewElement.Content != null)
                {
                    _normalFlowSwitchContent = (FrameworkElement) workflowViewElement.Content;
                    //workflowViewElement.Content = new FlowSwitchEditModeUserControl(_flowSwitchMi);
                    condition = _flowSwitchMi.Properties["Expression"];
                    workflowViewElement.Content = new FlowSwitchEditModeUserControl(this);

               }

           }

     

    After entering some text in the expression text box, and exiting the control, I get an exception:

    ArgumentException:

    Object of type 'System.Activities.InArgument`1[System.Int32]` cannot be converted to System.Activities.Activity`1[System.32]'

    Thanks,

    Notre

    Wednesday, May 26, 2010 12:08 AM
  • Almost got it now.  Modified the code in my last post to look like this:

    <sapv:ExpressionTextBox Margin="10" Grid.Column="1"
                    Expression="{Binding Path=ConditionValue,
                        Mode=TwoWay}"
                        ExpressionType="s:Int32"
                    OwnerActivity="{Binding Path=SwitchMi}"/>

    That is, I got rid of the converter.  Now, the only remaining problem is that when I edit an expression twice in succession, the updated value appears in the property grid right away (and the designer tooltip), but my expression text box text (hint text?) shows the old the value still, i.e. the one I last entered!

    Notre

    Wednesday, May 26, 2010 12:27 AM
  • While I am close (I think) with the modifications I made in my last post, after thinking about this a bit more, I'm not sure why this approach is necessary and why my original XAML based expression text box doesn't work:

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

    While I am messing with the visual tree in the FlowSwitch<> designer, my user control is still associated with the FlowSwitch<> activity.  If I add a label and set a binding on the label like this:

    <Label Margin="10" Content="{Binding Path=ModelItem}"/>

    The output is "System.Activities.Statements.FlowSwitch'1[System.Int32]", when I don't explicitly pass in a data context to my user control, i.e.:

    workflowViewElement.Content = new FlowSwitchEditModeUserControl(_flowSwitchMi);

     

    This suggests to me that I shouldn't need to go through this indirect approach of introducing an extra property and specifying a data context.

    Can you explain why (if) this extra step is necessary, and if so, what I need to do to get the final part working (that is, have the expression text box 'hint text' immediately show the most recent value, after editing is complete rather than the last edit value)?

    Thanks,

    Notre

    Wednesday, May 26, 2010 4:13 PM
  • sorry for the late response.

    I will take a look and try to make it work today.

    Wednesday, May 26, 2010 5:25 PM
  • Thanks Ye, I appreciate it.

    Notre

    Wednesday, May 26, 2010 7:11 PM
  • I remember once you mentioned you would recreate your own flowswitch designer. are you doing that?

    could you replace with following xaml part?

    <sapv:ExpressionTextBox  x:Uid="conditionBox" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" x:Name="conditionBox"
                  MinLines="1" MaxLines="1" MaxWidth="450" MinWidth="450"
                  Expression="{Binding Path=ModelItem.Expression, Mode=TwoWay}"
                  ExpressionType="{x:Type TypeName=sys:int}"
                  OwnerActivity="{Binding Path=ModelItem, Mode=OneWay}"
                  AutomationProperties.Name="Expression expression"/>

    • Marked as answer by Notre Thursday, May 27, 2010 6:09 PM
    Thursday, May 27, 2010 5:25 AM
  • Hi Ye,

    I'm recreating my own FlowDecision designer, but not a FlowSwitch designer.  While I planned to do the same with the FlowSwitch, I discovered I couldn't without replacing the FlowChart designer as well, as there are dependencies of the FlowChart designer on the FlowDecision designer. 

    If I completely replace the FlowSwitch designer with something of my own, then when I try to make a connection between my custom FlowSwitch designer and another FlowStep in the FlowChart, an exception is thrown and error dialog is displayed.  Therefore, I've gone with the approach you suggest in the earlier post, where part of the existing FlowSwitch designer is dynamically replaced, but it is still (technically) the same out-of-the-box FlowSwitch designer.  In general, this seems to be working for me, except trying to get the ExpressionTextBox editor working.

    Thanks,

    Notre

    Thursday, May 27, 2010 5:02 PM
  • Hi Ye,

    I tried a slight variation of your last suggestion, and it works!

    Here's my new version:

                <sapv:ExpressionTextBox Grid.Column="1"
                    Expression="{Binding Path=ModelItem.Expression,
                    Mode=TwoWay}"
                    ExpressionType="s:Int32"
                    OwnerActivity="{Binding Path=ModelItem}"/>

    and here is my original (non-working) version:

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

    The only difference between the working and non-working one is the absence of the ArgumentToExpressionConverter.  Why do I not need the converter here? Is it because when I look at the FlowSwitch<> class, the Expression is a regular property rather than an in, in/out, or out argument?

    Thanks,

    Notre

    Thursday, May 27, 2010 5:23 PM
  • yes, you are right.

    if the expression is for In/outArgument, you need to put the converter, or you don't need.

    Thursday, May 27, 2010 5:57 PM