views:

59

answers:

1

I am trying to control the visibility of a column using a checkbox (this is in WPF 4.0).

Here is a snippet of my XAML:

<Window.Resources>
    <BooleanToVisibilityConverter x:Key="BoolToVisConverter" />
</Window.Resources>

<CheckBox x:Name="GeneralDetailsVisible" Content="General Details"  Margin="5"/>

<DataGridTextColumn Header="Crew" 
  Binding="{Binding Path=Crew}" 
  Visibility="{Binding ElementName=GeneralDetailsVisible, 
                       Converter={StaticResource BoolToVisConverter}, 
                       Path=IsChecked}">
</DataGridTextColumn>

Now I know the BooleanToVisibilityConverter converter is working as I bound it to a text block and I can see the values I am expecting. If I enter the values by hand into the columns visibility property it works. But not when I bind it. What am I doing wrong?

Answer:

Quartermeister pointed me to the answer. The page he pointed to is a bit misleading as the code listed on the post does not work and you must look at the sample code.

Here is my final, working code, for anyone else who runs into this problem:

Converter to turn our ViewModels bool property into the correct Visibility value for our column attribute.

<Window.Resources>
    <BooleanToVisibilityConverter x:Key="BoolToVisConverter" />
</Window.Resources>

Bind the checkbox to the ViewModels property that will control the columns visibiity.

<CheckBox
  x:Name="DetailsVisible" 
  Content="Show Details"
  IsChecked="{Binding Path=DisplayDetails}" />

Then bind Visibility to the ViewModels DisplayDetails property. Notice that it is the columns own DataContext that is bound to.

<DataGridTextColumn
  Header="Reliability" 
  Binding="{Binding Path=Reliability}" 
  Visibility="{Binding (FrameworkElement.DataContext).DisplayDetails, 
                       RelativeSource={x:Static RelativeSource.Self}, 
                       Converter={StaticResource BoolToVisConverter}}">
</DataGridTextColumn>

Add the following code to your project, this allows the catching of the change in a DataGrids DataContext.

FrameworkElement.DataContextProperty.AddOwner(typeof(DataGridColumn));

FrameworkElement.DataContextProperty.OverrideMetadata(typeof(DataGrid),
        new FrameworkPropertyMetadata
           (null, FrameworkPropertyMetadataOptions.Inherits,
           new PropertyChangedCallback(OnDataContextChanged))); 

Then whenever your DataGrids DataContext is changed we update all the attached DataGridColumsn with the new DataContext.

public static void OnDataContextChanged(DependencyObject d,
                                        DependencyPropertyChangedEventArgs e)
{
    DataGrid grid = d as DataGrid;
    if (grid != null)
    {
        foreach (DataGridColumn col in grid.Columns)
        {
            col.SetValue(FrameworkElement.DataContextProperty, e.NewValue);
        }
    }
}

One Gotcha to look out for. If you add your DataContext to your page like so:

<Window.DataContext>
    <vm:WeaponListViewModel />
</Window.DataContext>

Then the above function will be called before your DataGrid has any columns!

I got around this by manually attaching my DataConext in code behind after the window was created.

A: 

The problem is that the DataGrid columns are not part of the visual tree, so they cannot use bindings. (The "Binding" property is actually a normal property of type Binding, not a dependency property. It applies that binding object to the cells, which are part of the visual tree.)

Here is a link to a blog that has a workaround and demonstrates binding the visibility of a column to a check box: http://blogs.msdn.com/b/jaimer/archive/2008/11/22/forwarding-the-datagrid-s-datacontext-to-its-columns.aspx

Quartermeister