How to hide wpf datagrid columns depending on a property
Asked Answered
P

5

26

I have the following WPF sample program:

Xaml:

<Window x:Class="AncestorArie.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <BooleanToVisibilityConverter x:Key="BoolToVis" />
    </Window.Resources>
    <Grid>
        <DataGrid AutoGenerateColumns="False" Name="Blumen" 
                  ItemsSource="{Binding Leaves}">
            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding Color}" 
                                    Header="Farbe" Width="160" />
                <DataGridTextColumn Binding="{Binding Size}" 
                                    Header="Größe" Width="60"
                                    Visibility="{Binding Path=DataContext.Flag, 
                                                RelativeSource={RelativeSource Findancestor, 
                                                AncestorType={x:Type Window}}, 
                                                Converter={StaticResource BoolToVis}}" />
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>

Code behind:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        Flowers rose = new Flowers();
        rose.Leaves = new ObservableCollection<Leaf>();

        rose.Flag = false;

        Leaf L1 = new Leaf();
        L1.Color = "rot";
        L1.Size = 3;
        rose.Leaves.Add(L1);

        Leaf L2 = new Leaf();
        L2.Color = "gelb";
        L2.Size = 2;
        rose.Leaves.Add(L2);

        this.DataContext = rose;            
    }
}

And the model classes are:

public class Leaf
{
    public string Color { get; set; }
    public int Size { get; set; }
}

public class Flowers
{
    public bool Flag { get; set; }
    public ObservableCollection<Leaf> Leaves { get; set; }
}

As you can see, I want to hide the 2nd datagrid column, if the Flag property is set to false. But it doesn't work. I get the following binding error in the Visual Studio Output window:

System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Window', AncestorLevel='1''. BindingExpression:Path=DataContext.Flag; DataItem=null; target element is 'DataGridTextColumn' (HashCode=44856655); target property is 'Visibility' (type 'Visibility')

What is wrong in my code concerning the Visibility attribute?

Paraphrast answered 28/7, 2011 at 10:52 Comment(0)
M
62

A column in a datagrid is an abstract object which does not appear in the visual tree, thus you cannot use RelativeSource-binding, ElementName will not work either since it will not find a governing FrameworkContentElement so you are in kind of a bind.

One way that works is via Source and x:Reference, for that you will need to name your window and move the column to its resources to avoid a cyclical dependency error:

<Window Name="_window" ...>
    <Window.Resources>
        <DataGridTextColumn x:Key="ThatPeskyColumn"
                            Binding="{Binding Size}"
                            Visibility="{Binding DataContext.Flag, Source={x:Reference _window}, Converter={StaticResource BoolToVis}}"/>
    </Window.Resources>
    <!-- ... -->
        <DataGrid AutoGenerateColumns="False" Name="Blumen" 
                  ItemsSource="{Binding Leaves}">
            <DataGrid.Columns>
                <StaticResource ResourceKey="ThatPeskyColumn"/>
                <!-- ... -->

Great fun.

Maraca answered 28/7, 2011 at 12:24 Comment(7)
I know, that I'm a little late on party, but thanks, man! P.S. I hate DataGrid developer(s).Shallot
I can't seem to get this to work ever since I upgraded from .NET 4 to 4.5.2. Does anyone know why?Accountancy
@CamHart: Do you know how to debug bindings?Maraca
@H.B. No I don't. I attempted to start up a second VS, and attach to the process running on the first VS. But I couldn't ever get any errors to trigger. I did find a solution though, #34817917.Accountancy
@H.B. how can you solve it if the binding is being set in <style> in separate ResourceDictionary in separate xaml file?Disaccredit
@virious: Can't think of a proper solution off the top of my head. How exactly would you do that in the first place? I don't think the columns themselves are stylable in any way.Maraca
It doesn't work for me, actually property is dinded properly, but column doesn't appear\disapperarFebruary
P
16

I would prefer a more elegant approach which involves using a Freezable.

<Window.Resources>

    <DiscreteObjectKeyFrame x:Key="FlagKey" Value="{Binding Flag}"/>

</Window.Resources>


<DataGridTextColumn ... Visibility="{Binding Value, Source={StaticResource FlagKey}, ...}" />
Philipp answered 7/11, 2016 at 5:39 Comment(4)
AnjumSKhan : Very nice indeed. Clean an elegant. ThxCymric
This also works in situations where a binding proxy doesn’t. Just tried it—thanks!Spoliate
Tested in .Net 5 and works like a charm. Plus, it allow me to hide several columns at once based on a common property. Easy an smart solution. EXTRA TIP: use a converter for DiscreteObjectKeyFrame.Value if you want to convert boolean to visibility: <DiscreteObjectKeyFrame x:Key="FlagKey" Value="{Binding Flag, Converter={StaticResource BooleanToVisibilityConverter}}" />Orchitis
This is explained in detail here: https://mcmap.net/q/277123/-wpf-databinding-error-in-tag-propertyTheocentric
S
4

Visibility on DataGridTextColumn is not a DependencyProperty and can't be databound. Use a DataGridTemplateColumn and bind the visibility of the controls within the template.

Edit: Actually, this statement only applies to silverlight. See this other SO question for further details.

How to bind DataGridColumn.Visibility?

I asked about the easiest way to tell whether a property is a dependency here.

How can I most easily determine whether a property is a dependency property?

Sharpeyed answered 28/7, 2011 at 11:24 Comment(5)
Thank you for your answer. Can you tell me how I can recognize in Xaml if a property is a dependency property or not?Paraphrast
It's confusing that I get a binding error (see my Edit). It looks as if WPF would try to bind the Visibility to a property. If a property can't be bound at all I had expected a different error - something like "Binding not possible" or so.Paraphrast
Actually it is a DP, otherwise you'd get a compilation error.Maraca
@H.B. My mistake. This looks like a difference between Silverlight and WPF.Sharpeyed
Thanks for further explanation!Paraphrast
P
2

Solution proposed by H.B. is really good and has true WPF MVVM spirit. Use it where possible.

In my particular case something went wrong so I came out with different way, as my project is not strict MVVM, so I can use coded solution.

In CustomView.xaml name assigned to column:

<DataGrid>
    <DataGrid.Columns>
        <DataGridTemplateColumn x:Name="MachinesColumn" ... />
        ...

In CustomView.xaml.cs we have a simple property which directly changes visibility of column:

public Visibility MachinesColumnVisible
{
    get { return MachinesColumn.Visibility; }
    set
    {
        if (value == MachinesColumn.Visibility)
            return;
        MachinesColumn.Visibility = value;
    }
}
Panacea answered 20/3, 2014 at 9:22 Comment(0)
R
1

If you are AutoGenerating the columns, there is an event you can use:

https://learn.microsoft.com/en-us/dotnet/api/system.windows.controls.datagrid.autogeneratingcolumn?view=net-5.0

I am changing to autogenerated columns and this event to solve several issues! Can also be used to change the Header on the column.

Regrate answered 11/8, 2021 at 20:11 Comment(2)
Links to external resources are encouraged, but please add context around the link so your fellow users will have some idea what it is and why it’s there. Always quote the most relevant part of an important link, in case the external resource is unreachable or goes permanently offline.Bula
Even though the detail is external, this link was exactly what I needed :) I wanted the properties for other parts of the table like grouping and extra row details but didn't want those columns showing up.Homosexuality

© 2022 - 2024 — McMap. All rights reserved.