locked
ExpressionTextBox not working in a DataGrid? RRS feed

  • Question

  • I have developed a custom Activity that has a Dictionary<string, InArgument<string>> as one of its properties.
    Then I have created a specific PropertyEditor with a dialog box to edit the dictionary (a bit like the SendContentEditor of the Send activity).

    The dialog box displays a DataGrid with in one of the column, an ExpressionTextBox binded to the InArgument<string>.

    Everything is working except when I close the dialog box. The ExpressionTextBoxes do not always update the arguments with new expressions.
    It's not working 95% of the time and it seems related to focus but I am not sure.
    The same dialog but with a ListBox instead of a DataGrid is working perfectly.

    Any idea?

    Thanks,
    Nicolas

    Thursday, January 14, 2010 8:25 AM

Answers

  • Hello Zano,

    Apologies for the slow reply - This was a very interesting issue.

    I believe the root cause has something to do with the data grid commit model not being called at quite the right time.  We'll do a little more digging on why this is happening, but in the meantime, I do have a code snippet that will help get you unblocked.   The workaround is to explicitly call the expresison editor's commit on loss of focus, and then explicitly call the datagrid's commit afterwards.

    Xaml for the ETB in the data grid:

        <sap:ActivityDesigner.Resources>
            <sapc:ArgumentToExpressionConverter x:Key="ArgConv" />
        </sap:ActivityDesigner.Resources>
      <Grid>
            <DataGrid Name="DG1" ItemsSource="{Binding Path=ModelItem.Coll}" AutoGenerateColumns="False" CanUserReorderColumns="False" Margin="0,0,0,29" >
                <DataGrid.Columns>
                    <DataGridTemplateColumn ClipboardContentBinding="{x:Null}" Header="Binding">
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <sapv:ExpressionTextBox
                                            Expression="{Binding Path=Binding, Mode=TwoWay, Converter={StaticResource ArgConv}}"
                                            ExpressionType="{x:Type s:String}"
                                            OwnerActivity="{Binding Path=ModelItem}" LostFocus="ExpressionTextBox_LostFocus" AcceptsReturn="False" ExplicitCommit="True"
                                            />
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                    </DataGridTemplateColumn>
                </DataGrid.Columns>
            </DataGrid>

        </Grid>

    Interesting designer code:

            private void ExpressionTextBox_LostFocus(object sender, RoutedEventArgs e)
            {
                ExpressionTextBox etb = (ExpressionTextBox)sender;
                RoutedCommand cmd = DesignerView.CommitCommand as RoutedCommand;
                cmd.Execute(null, etb);
                DG1.CommitEdit(DataGridEditingUnit.Cell, false);
                DG1.CommitEdit(DataGridEditingUnit.Row, false);
            }

    Hope this helps,
    -Eric

    • Proposed as answer by Tim Lovell-Smith Thursday, January 21, 2010 6:03 PM
    • Marked as answer by zhenyu dai Monday, December 6, 2010 7:54 AM
    Thursday, January 21, 2010 2:34 AM

All replies

  • Can you post the XAML you are using for the DataGrid? Thanks
    Thursday, January 14, 2010 7:16 PM
  • Here is the XAML with the DataGrid

    <Window x:Class="Usercube.SecurityModel.Activities.Design.DelegationContentDialog"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:s="clr-namespace:System;assembly=mscorlib"
            xmlns:sap="clr-namespace:System.Activities.Presentation;assembly=System.Activities.Presentation"
            xmlns:sapp="clr-namespace:System.Activities.Presentation.PropertyEditing;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"
            Title="Bind dimensions to arguments/variables" Height="500" Width="500" xmlns:my="clr-namespace:Usercube.SecurityModel.Activities.Design" ShowInTaskbar="False" WindowStyle="SingleBorderWindow" WindowStartupLocation="CenterScreen">
        <Grid>
            <Button Content="OK" Margin="0,0,81,0" Name="okButton" IsCancel="False" IsDefault="True" Height="23" VerticalAlignment="Bottom" HorizontalAlignment="Right" Width="75" Click="okButton_Click" />
            <Button Content="Cancel" HorizontalAlignment="Right" Name="cancelButton" Width="75" IsCancel="True" Height="23" VerticalAlignment="Bottom" Click="cancelButton_Click" />
            <DataGrid ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=my:DelegationContentDialog, AncestorLevel=1}, Path=DimensionsBindings}" AutoGenerateColumns="False" CanUserReorderColumns="False" Margin="0,0,0,29">
                <DataGrid.Columns>
                    <DataGridTextColumn Binding="{Binding Path=PropertyName}" Header="Dimension" MinWidth="150" />
                    <DataGridTemplateColumn ClipboardContentBinding="{x:Null}" Header="Binding" Width="*">
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <sapv:ExpressionTextBox
                                            Expression="{Binding Path=Binding, Mode=TwoWay}"
                                            ExpressionType="{Binding Path=PropertyType, Mode=OneWay}"
                                            OwnerActivity="{Binding Path=ModelItem, Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType=my:DelegationContentDialog, AncestorLevel=1}}"
                                            />
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                    </DataGridTemplateColumn>
                </DataGrid.Columns>
            </DataGrid>
        </Grid>
    </Window>
    

    Another version with a ListBox instead of the DataGrid:
    <Window x:Class="Usercube.SecurityModel.Activities.Design.DelegationContentDialog"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:s="clr-namespace:System;assembly=mscorlib"
            xmlns:sap="clr-namespace:System.Activities.Presentation;assembly=System.Activities.Presentation"
            xmlns:sapp="clr-namespace:System.Activities.Presentation.PropertyEditing;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"
            Title="Bind dimensions to arguments/variables" Height="500" Width="500" xmlns:my="clr-namespace:Usercube.SecurityModel.Activities.Design" ShowInTaskbar="False" WindowStyle="SingleBorderWindow" WindowStartupLocation="CenterScreen">
        <Grid>
            <Button Content="OK" Margin="0,0,81,0" Name="okButton" IsCancel="False" IsDefault="True" Height="23" VerticalAlignment="Bottom" HorizontalAlignment="Right" Width="75" Click="okButton_Click" />
            <Button Content="Cancel" HorizontalAlignment="Right" Name="cancelButton" Width="75" IsCancel="True" Height="23" VerticalAlignment="Bottom" Click="cancelButton_Click" />
              <ListBox ScrollViewer.HorizontalScrollBarVisibility="Disabled" HorizontalContentAlignment="Stretch"  Margin="0,0,0,29"
                             ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=my:DelegationContentDialog, AncestorLevel=1}, Path=DimensionsBindings}" Name="listBox1">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <DockPanel>
                            <Label Content="{Binding Path=PropertyName}" DockPanel.Dock="Left" Width="150" />
                            <sapv:ExpressionTextBox DockPanel.Dock="Right"
                                            Expression="{Binding Path=Binding, Mode=TwoWay}"
                                            ExpressionType="{Binding Path=PropertyType, Mode=OneWay}"
                                            OwnerActivity="{Binding Path=ModelItem, Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType=my:DelegationContentDialog, AncestorLevel=1}}"
                                            />
                        </DockPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </Grid>
    </Window>
    The TwoWay bindings work exactly as expected with the ListBox.

    The ItemSource is a collection of:
    public sealed class GridItem
    {
        public string PropertyName { get; set; }
        public Type PropertyType { get; set; }
        public ModelItem Binding { get; set; }
    }
    Binding is the ModelItem containing the expression binded to the ExpressionTextBox.


    I must miss somthing as it's working with the messaging activities designers.


    Friday, January 15, 2010 7:49 AM
  • Hello Zano,

    Apologies for the slow reply - This was a very interesting issue.

    I believe the root cause has something to do with the data grid commit model not being called at quite the right time.  We'll do a little more digging on why this is happening, but in the meantime, I do have a code snippet that will help get you unblocked.   The workaround is to explicitly call the expresison editor's commit on loss of focus, and then explicitly call the datagrid's commit afterwards.

    Xaml for the ETB in the data grid:

        <sap:ActivityDesigner.Resources>
            <sapc:ArgumentToExpressionConverter x:Key="ArgConv" />
        </sap:ActivityDesigner.Resources>
      <Grid>
            <DataGrid Name="DG1" ItemsSource="{Binding Path=ModelItem.Coll}" AutoGenerateColumns="False" CanUserReorderColumns="False" Margin="0,0,0,29" >
                <DataGrid.Columns>
                    <DataGridTemplateColumn ClipboardContentBinding="{x:Null}" Header="Binding">
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <sapv:ExpressionTextBox
                                            Expression="{Binding Path=Binding, Mode=TwoWay, Converter={StaticResource ArgConv}}"
                                            ExpressionType="{x:Type s:String}"
                                            OwnerActivity="{Binding Path=ModelItem}" LostFocus="ExpressionTextBox_LostFocus" AcceptsReturn="False" ExplicitCommit="True"
                                            />
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                    </DataGridTemplateColumn>
                </DataGrid.Columns>
            </DataGrid>

        </Grid>

    Interesting designer code:

            private void ExpressionTextBox_LostFocus(object sender, RoutedEventArgs e)
            {
                ExpressionTextBox etb = (ExpressionTextBox)sender;
                RoutedCommand cmd = DesignerView.CommitCommand as RoutedCommand;
                cmd.Execute(null, etb);
                DG1.CommitEdit(DataGridEditingUnit.Cell, false);
                DG1.CommitEdit(DataGridEditingUnit.Row, false);
            }

    Hope this helps,
    -Eric

    • Proposed as answer by Tim Lovell-Smith Thursday, January 21, 2010 6:03 PM
    • Marked as answer by zhenyu dai Monday, December 6, 2010 7:54 AM
    Thursday, January 21, 2010 2:34 AM