2

I am trying to build a DataGrid with controls that will allow a user to Show/Hide the columns. My DataGrid will have something like 40 columns, and not all may be necessary all of the time. I have been able to do this exact thing with a ListView that uses a GridView. Here is the code:

<DataGrid Name="MyDataGrid" Grid.Row="2" Grid.Column="0" ItemsSource="{Binding ReportOrderingCustomersForSalesRepCollection}" Style="{DynamicResource styleDataGrid}" HeadersVisibility="All" AutoGenerateColumns="False" RowHeaderWidth="0" RowHeight="25">
    <DataGrid.Columns>
        <DataGridTextColumn Header="Customer #" Binding="{Binding CustomerNumber}" Width="90" Visibility="{Binding ElementName=Visibility_Txt,Path=Text,Mode=OneWay}"/>
        <DataGridTextColumn Header="Customer Name" Binding="{Binding CustomerName}" Width="125" />
        <DataGridTemplateColumn Header="Email" CellTemplate="{StaticResource Email}" Width="150" />
    </DataGrid.Columns>
</DataGrid>
<!-- text box -->
<TextBox Name="Visiblility_Txt">
    <TextBox.Style>
        <Style TargetType="TextBox">
            <Style.Triggers>
                <DataTrigger Binding="{Binding ElementName=ColumnVisibilityCheck,Path=IsChecked}" Value="False">
                    <Setter Property="Text" Value="Collapsed" />
                </DataTrigger>
                <DataTrigger Binding="{Binding ElementName=ColumnVisibilityCheck,Path=IsChecked}" Value="True">
                    <Setter Property="Text" Value="Visible" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </TextBox.Style>
</TextBox>
<!-- checkbox -->
<CheckBox Content="Show/Hide Customer Number" Name="ColumnVisibilityCheck" IsChecked="True" />

So I have a DataGrid setup. The Visibility Property on the first DataGridTextColumn is set as a binding to the text property of "Visibility_Txt". The text on that text box will be set to either Collapsed or Visible depending if the ColumnVisibilityCheck is checked or not.

Like i said, this works with a listview, Why won't this work with a DataGrid?

EMAW2008
  • 289
  • 2
  • 15
  • ViewModel/Code-Behind? And you have at least 3 grid rows but no row definitions – Matthew Frontino Jun 03 '15 at 13:57
  • The rows are populated from the ItemsSource. I am wanting to show/hide individual Columns. – EMAW2008 Jun 03 '15 at 14:09
  • grid rows are not the same as datagrid rows: – Matthew Frontino Jun 03 '15 at 14:12
  • Yes i understand that, i'm asking about how to show/hide Columns in a DataGrid, please read the post. nothing about rows. – EMAW2008 Jun 03 '15 at 14:19
  • How do you expect us to help you if we dont have the code we need to actually view the datagrid? Please provide everything needed to make the example work. – Matthew Frontino Jun 03 '15 at 14:20
  • There is no other code to show. It's a binding issue, I'm simply trying to understand why i cannot affect the visibility of a column in a DataGrid. – EMAW2008 Jun 03 '15 at 14:36
  • Yea, I'm not sure what code the dude wants pertaining to 'actually view the datagrid' so don't worry about that. In the meantime though just go put a Boolean To Visibility Converter on the binding base of your Visibility for the Column and bind directly to the checkbox instead of trying to grab the Text string from the TextBox. – Chris W. Jun 03 '15 at 14:48

3 Answers3

4

Was able to find some more information on this. This link has a good answer/explanation. datagridtextcolumn-visibility-binding

It turns out that the columns of a DataGrid do not appear in the visual tree of a DataGrid.

But the answer is to use x:reference in the visibility binding, and a BooleanToVisibilityConverter:

<DataGridTextColumn Header="Customer #" x:Name="CustNum_Col" Visibility="{Binding Source={x:Reference VisibilityCheck}, Path=IsChecked,Converter={StaticResource ObjectToVisibilityConverter}}" />

Visual Studio will show the squiggly line under the binding saying that "object not set to instance of an object" but it this still appears to work.

Jim Simson
  • 2,774
  • 3
  • 22
  • 30
EMAW2008
  • 289
  • 2
  • 15
1

Just set its MaxWidth property to Zero and it wont appear.

DataGrid.Columns[IndexOfColumn].MaxWidth = 0;
0

1: Apply the following behaviour your DataGrid

<i:Interaction.Behaviors>
    <b:DataGridColumnVisibilityBindingBehavior Binding="{Binding IsNameVisible}" 
ColumnIndex="3" />
    </i:Interaction.Behaviors>

3: Here is the Implementation of the Behavior

public class DataGridColumnVisibilityBindingBehavior : Behavior<DataGrid>
{
    private static readonly DependencyProperty ProxyProperty = DependencyProperty.RegisterAttached(
        "Proxy",
        typeof(object),
        typeof(DataGridColumnVisibilityBindingBehavior),
        new PropertyMetadata(OnGridProxyChanged));

    private static readonly DependencyProperty AssociatedBinderProperty = DependencyProperty.RegisterAttached(
        "AssociatedBinder",
        typeof(ColumnDataContextBinder),
        typeof(DataGridColumnVisibilityBindingBehavior),
        new PropertyMetadata(default(ColumnDataContextBinder)));

    public Binding Binding { get; set; }

    public int ColumnIndex { get; set; }

    protected override void OnAttached()
    {
        base.OnAttached();

        CheckProxyBound();

        var column = AssociatedObject.Columns[this.ColumnIndex];
        var columnBinder = new ColumnDataContextBinder(column, this.Binding)
            {
                DataContext = AssociatedObject.GetValue(ProxyProperty)
            };

        column.SetValue(AssociatedBinderProperty, columnBinder);
    }

    private static void OnGridProxyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var grid = (DataGrid)d;
        foreach (var column in grid.Columns)
        {
            var binder = (ColumnBinder)column.GetValue(AssociatedBinderProperty);
            if (binder != null)
            {
                binder.DataContext = e.NewValue;
            }
        }
    }

    private void CheckProxyBound()
    {
        if (AssociatedObject.GetBindingExpression(ProxyProperty) == null)
        {
            AssociatedObject.SetBinding(ProxyProperty, new Binding());
        }
    }

    private sealed class ColumnDataContextBinder : FrameworkElement
    {
        private static readonly DependencyProperty IsVisibleProperty = DependencyProperty.Register(
            "IsVisible",
            typeof(bool),
            typeof(ColumnDataContextBinder),
            new PropertyMetadata(true, (s, e) => ((ColumnDataContextBinder)s).OnVisibilityChanged()));

        private readonly DataGridColumn column;

        public ColumnDataContextBinder(DataGridColumn column, Binding binding)
        {
            column = column;

            SetBinding(IsVisibleProperty, binding);
        }

        private bool IsVisible
        {
            get { return (bool)GetValue(IsVisibleProperty); }
        }

        private void OnVisibilityChanged()
        {
            column.Visibility = IsVisible ? Visibility.Visible : Visibility.Collapsed;
        }
    }
}

3: I hope this answers your question.

Saraf Talukder
  • 376
  • 2
  • 12
  • 3
    For sake of completeness and clarification... Regular Bindings don't work on DataGridColumns because they're outside the visual tree and they don't have a DataContext. Saraf Behavior solves this by creating a proxy class that serves as DataContext. – almulo Jun 03 '15 at 16:09