25

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?

Yael
  • 1,566
  • 3
  • 18
  • 25
Hulda
  • 275
  • 1
  • 4
  • 7

5 Answers5

61

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.

H.B.
  • 166,899
  • 29
  • 327
  • 400
  • 6
    I know, that I'm a little late on party, but thanks, man! P.S. I hate `DataGrid` developer(s). – Dennis Jul 04 '13 at 06:39
  • I can't seem to get this to work ever since I upgraded from .NET 4 to 4.5.2. Does anyone know why? – CamHart Jan 15 '16 at 21:12
  • @CamHart: Do you know how to debug bindings? – H.B. Jan 17 '16 at 13:55
  • @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, http://stackoverflow.com/questions/34817417/update-wpf-from-net-4-to-4-5-2-datagridtextcolumn-visibility-datacontext-refer. – CamHart Jan 19 '16 at 18:12
  • @H.B. how can you solve it if the binding is being set in – virious Mar 16 '16 at 07:54
  • @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. – H.B. Mar 16 '16 at 16:15
  • It doesn't work for me, actually property is dinded properly, but column doesn't appear\disapperar – Pyrejkee Jun 16 '18 at 18:06
14

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}, ...}" />
AnjumSKhan
  • 9,647
  • 1
  • 26
  • 38
  • 1
    AnjumSKhan : Very nice indeed. Clean an elegant. Thx – JohnB May 31 '17 at 22:20
  • This also works in situations where a binding proxy doesn’t. Just tried it—thanks! – Informagic Apr 29 '19 at 08:59
  • 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: `` – Antonio Rodríguez Dec 02 '21 at 07:50
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?

Community
  • 1
  • 1
Scott Munro
  • 13,369
  • 3
  • 74
  • 80
  • Thank you for your answer. Can you tell me how I can recognize in Xaml if a property is a dependency property or not? – Hulda Jul 28 '11 at 11:37
  • 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. – Hulda Jul 28 '11 at 12:01
  • Actually it is a DP, otherwise you'd get a compilation error. – H.B. Jul 28 '11 at 12:07
  • @H.B. My mistake. This looks like a difference between Silverlight and WPF. – Scott Munro Jul 28 '11 at 12:15
  • Thanks for further explanation! – Hulda Jul 29 '11 at 08:56
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;
    }
}
Anton
  • 10,890
  • 8
  • 45
  • 54
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.

mly
  • 51
  • 5
  • 1
    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. – KevenDenen Aug 11 '21 at 20:44
  • 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. – Sisyphus Aug 29 '22 at 04:38