2

I was looking at an answer on how to set the visibility of a data grid column, so i added

 <UserControl.Resources>
    <BooleanToVisibilityConverter x:Key="b2v" />
</UserControl.Resources>    

and then in my xaml I have

<DataGridTemplateColumn Visibility="{Binding Source={x:Reference ShowLookupCheckbox}, 
                                             Path=IsChecked
                                             Converter={StaticResource b2v}}" >
 ...
</DataGridTemplateColumn>

When my app starts and the checkbox is clear the column is correctly collapsed. When I check the checkbox the column correctly shows. When I click on the checkbox again the column agains is correctly collapsed. However, when I click to enable the checkbox a second time I get an InvalidOperationException "Specified element is already the logical child of another element. Disconnect it first."

I tried changing the Mode to one way but that didn't help. Anyone know what I'm doing wrong or how to interpret this exception?

I don't think the content of the column matters as it works correctly without the Visibility setting but for the sake of completion here's the full xaml I elided with ... above

 <DataGridTemplateColumn.Header>
 <TextBlock  Style="{StaticResource DataGridHeader}">MO Lookup</TextBlock>
 </DataGridTemplateColumn.Header>

 <DataGridTemplateColumn.CellTemplate >
  <DataTemplate >
     <Hots:AutoCompleteBoxEx ToolTip="Start typing a Master Order Id"
                             ItemsSource="{Binding AllMoLines,
                                                  UpdateSourceTrigger=PropertyChanged}"
                             Width="50"
                             HorizontalContentAlignment="Left"
                             FilterMode="StartsWith"
                             IsDropDownOpen="True"
                             IsTextCompletionEnabled="True"
                             LostFocus="MoLineBoxLostFocus"
                             SelectedItem="{Binding Path=SelectedMoLine, Mode=TwoWay}" >

         <Hots:AutoCompleteBoxEx.ItemTemplate>
             <DataTemplate>
                 <Grid Width="500">
                     <Grid.ColumnDefinitions>
                         <ColumnDefinition Width="50" />
                         <ColumnDefinition Width="400" />
                         <ColumnDefinition Width="50" />
                     </Grid.ColumnDefinitions>
                     <TextBlock Text="{Binding HssId, Mode=OneWay}"
                                Grid.Column="0" />
                     <TextBlock Text="{Binding Description, Mode=OneWay}"
                                Grid.Column="1" />
                     <TextBlock Text="{Binding Unit, Mode=OneWay}"
                                Grid.Column="2" />
                 </Grid>
             </DataTemplate>
         </Hots:AutoCompleteBoxEx.ItemTemplate>
     </Hots:AutoCompleteBoxEx>

 </DataTemplate>

[EDIT] Here are the first few lines from the Exception stack trace. The whole thing is 66 lines. It's the AddLogicalChild and ChangeLogicalParent that I find perplexing. I'm not sure why checking/unchecking a checkbox linked to a Visibility property is causing a child element to be added

System.InvalidOperationException was unhandled
  Message=Specified element is already the logical child of another element. Disconnect it first.
  Source=PresentationFramework
  StackTrace:
    at System.Windows.FrameworkElement.ChangeLogicalParent(DependencyObject newParent)
    at System.Windows.FrameworkElement.AddLogicalChild(Object child)
    at System.Windows.Controls.ContentControl.OnContentChanged(Object oldContent, Object newContent)
Community
  • 1
  • 1
Tod
  • 8,192
  • 5
  • 52
  • 93
  • 1
    How about changing the content to be sure? Do you still get the error if the CellTemplate is a simple TextBlock? – H.B. Feb 29 '12 at 07:45
  • I just put the same visibility code on a plain DataGridTextColumn (and removed it from the problematic area) but the same exception is thrown, good suggestion though. And just to be sure it's not the checkbox, I removed the Visiblity setting altogether, and I can toggle the checkbox to my heart's content. Doesn't do anything, but it doesn't throw. – Tod Feb 29 '12 at 07:52
  • Did you try replacing the `Source="{x:Reference ...}"` with `ElementName="..." `? – Dabblernl Feb 29 '12 at 08:11
  • @Dabblernl: Read the linked answer. – H.B. Feb 29 '12 at 08:29
  • @Tod: I for one can't reproduce any such exception for any column. – H.B. Feb 29 '12 at 08:30
  • I added the first couple of lines of the stack trace to my question in case it helps. I'm guessing it must have to do with how I construct the overall window. It's a data entry form with many nested user controls – Tod Feb 29 '12 at 22:12

3 Answers3

2

I had a similar problem and after a deep research i`ve found the following thread - Remove a column from datagrid and add it back again. The author of the post (Jeromeyers) share:

"If I comment out the code that creates the Column's Header as a TextBlock, then everything works fine. So, it is the Column's HeaderContent that is somehow wanting to switch parents, or something.

PS> The reason why I want the Column's Header to be a TextBlock is so that I can get text wrapping."

Vladimir Trifonov
  • 1,395
  • 11
  • 9
1

The following appears to work but it uses a call to UpdateLayout on the DataGrid in code behind.

After a column has changed from Collapsed/Hidden to Visible, call UpdateLayout on the DataGrid and catch the exception.

biegleux
  • 13,179
  • 11
  • 45
  • 52
Ger
  • 11
  • 1
1

As I understand your problem, you want to set the visibility of grid's columns at run time. You can create a Attached Property and bind it with grid columns.

(1) The Attached Property

public class GridColumnManager
{

    static GridColumnManager()
    {
        //Allows to set DataContextProperty on the columns. Must only be invoked once per application.
        FrameworkElement.DataContextProperty.AddOwner(typeof(DataGridColumn));
    }

    public static object GetDataContextForColumns(DependencyObject obj)
    { return obj.GetValue(DataContextForColumnsProperty); }

    public static void SetDataContextForColumns(DependencyObject obj, object value)
    { obj.SetValue(DataContextForColumnsProperty, value); }

    /// <summary>
    /// Allows to set DataContext property on columns of the DataGrid (DataGridColumn)
    /// </summary>
    /// <example><DataGridTextColumn Header="{Binding DataContext.ColumnHeader, RelativeSource={RelativeSource Self}}" /></example>
    public static readonly DependencyProperty DataContextForColumnsProperty =
        DependencyProperty.RegisterAttached(
        "DataContextForColumns", 
        typeof(object),
        typeof(GridColumnManager), 
        new UIPropertyMetadata(OnDataContextChanged));

    /// <summary>
    /// Propogates the context change to all the DataGrid's columns
    /// </summary>
    private static void OnDataContextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var grid = d as DataGrid;
        if (grid == null) return;
        var name = grid.Name;

        foreach (DataGridColumn col in grid.Columns)
            col.SetValue(FrameworkElement.DataContextProperty, e.NewValue);
    }



}

(2) How it works with Grid

<DataGrid CanUserAddRows="True" IsEnabled="True" AutoGenerateColumns="False" CanUserDeleteRows="True" IsSynchronizedWithCurrentItem="True" Height="200" 
              local:GridColumnManager.DataContextForColumns="{Binding Mode=OneWay}"  HorizontalAlignment="Left" Margin="80,90,0,0" 
              Name="dataGrid1" VerticalAlignment="Top" Width="361" ItemsSource="{Binding Employees,Mode=TwoWay,NotifyOnSourceUpdated=True}" >
        <DataGrid.Resources>
            <BooleanToVisibilityConverter x:Key="MyConvertor"/>
        </DataGrid.Resources>
        <DataGrid.Columns>
            <DataGridTemplateColumn Header="Stating Period" MinWidth="200" Width="*" Visibility="{/*Bind here with your element */,
                                                        RelativeSource={RelativeSource Self},
                                                        Converter={StaticResource MyConvertor}}">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding ID}"   />
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
                <DataGridTemplateColumn.CellEditingTemplate>
                    <DataTemplate>
                        <TextBox Text="{Binding ID, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}" />
                    </DataTemplate>                        
                </DataGridTemplateColumn.CellEditingTemplate>                    
            </DataGridTemplateColumn>  </DataGrid.Columns>

    </DataGrid>
  • I found that this blog entry on [Forwarding the DataGrid’s DataContext to its’ columns](http://blogs.msdn.com/b/jaimer/archive/2008/11/22/forwarding-the-datagrid-s-datacontext-to-its-columns.aspx) helped me understand this better. Plus I was originally just trying to bind the column's visibility to a view model visibility property so this should work much cleaner. – Tod Mar 01 '12 at 01:10
  • So now I'm binding to a Visibility property on my ViewModel but the same thing happens, it throws the _Specified Element is already the logical child of another element. Disconnect it first._ – Tod Mar 01 '12 at 05:29