Click here to Skip to main content
15,879,348 members
Articles / Desktop Programming / WPF

WPF Grid Column and Row Hiding

Rate me:
Please Sign up or sign in to vote.
4.91/5 (52 votes)
16 Jul 2016CPOL5 min read 184.4K   3.5K   49   45
WPF Grid column and row hiding.

Introduction

How to easily hide columns and rows in a WPF Grid.

Background

The RowDefinition and ColumnDefinition are missing an obvious Visibility property. Why??? Who knows but it seems a rather silly oversight...

There have been various solutions mentioned (find these using Google etc...):

This method is by far the most heavy and quite frankly a waste of processing time... not to mention having to keep track of all the objects in a particular column/row etc...

  1. Set the ColumnDefinition/RowDefinitionWidth/Height to "Auto" and then set the Visibility of each element sitting in that column/row to Visibility.Collapsed.
  2. Manually setting the Width/Height of the respective ColumnDefinition/RowDefinition to 0...

This method is quick and easy but stumbles when it comes to unhiding - oh no - I forgot to remember what the width was before I set it zero... arghhhh........ now I have to keep track of all the column/row widths/heights or some other method...

Well, to be honest both methods, although they work, they suck...

I want to have my cake and to eat it too... This means I simply want to have a Visible property on my column and row definitions.

Solution

The solution is surprising easy...

For clarity I will now only refer to a ColumnDefinition for explanation. The solution is exactly the same for a RowDefinition. All you will need to do is swap the Width to Height and ColumnDefinition to RowDefinition, wave your magic wand - tap the screen and hey presto...

Recipe

This recipe will only use a true/false for whether the column is visible. The third state of Visibility.Hidden makes no sense in this context.

  1. We need a dependency property to add the 'Visible' to the ColumnDefinition.
  2. Use this new Visible property to coerce the Width dependency property.

What is coercion??? In this case think of it as a filter that sits on top of the Width property. I will have it filter the Width value to either report its original value or report zero depending on if the column is visible or not. It is important to note that coercion does not alter the underlying Width property. This means that we don't need code trying to remember what the width was before we hide the column.

So lets define a subclass which inherits ColumnDefinition as thus:

C#
public class ColumnDefinitionExtended : ColumnDefinition
{
}

Now we want to define our Visible dependency property using some boiler plate code inside our new class:

C#
public class ColumnDefinitionExtended : ColumnDefinition
{
    // Variables
    public static DependencyProperty VisibleProperty;

    // Properties
    public Boolean Visible
    {
        get { return (Boolean)GetValue(VisibleProperty); }
        set { SetValue(VisibleProperty, value); }
    }

    // Constructors
    static ColumnDefinitionExtended()
    {
        VisibleProperty = DependencyProperty.Register("Visible",
            typeof(Boolean),
            typeof(ColumnDefinitionExtended),
            new PropertyMetadata(true, new PropertyChangedCallback(OnVisibleChanged)));
    }

    // Get/Set
    public static void SetVisible(DependencyObject obj, Boolean nVisible)
    {
        obj.SetValue(VisibleProperty, nVisible);
    }
    public static Boolean GetVisible(DependencyObject obj)
    {
        return (Boolean)obj.GetValue(VisibleProperty);
    }
}

OK, so that's taken care of.

We've defined the VisibleProperty as a static DependencyProperty and provided some getter/setters... We have also provided a static constructor to register the property as required with a default value of true and providing a property changed callback.

The default value true is so that the column starts off by default as visible. The only thing missing now is the OnVisibleChanged callback code.

Now before we get there we need to put in some secret sauce and do a property override to be able to coerce the Width property.

This can be done by adding one more line to the static constructor as thus:

C#
static ColumnDefinitionExtended()
{
    VisibleProperty = DependencyProperty.Register("Visible",
        typeof(Boolean),
        typeof(ColumnDefinitionExtended),
        new PropertyMetadata(true, new PropertyChangedCallback(OnVisibleChanged)));
    
    ColumnDefinition.WidthProperty.OverrideMetadata(typeof(ColumnDefinitionExtended),
        new FrameworkPropertyMetadata(new GridLength(1, GridUnitType.Star), null, 
            new CoerceValueCallback(CoerceWidth)));
}

We have now overridden the normal ColumnDefinition.WidthProperty to provide a CoerceValueCallback function. This will enable the overriding of the Width property based on our requirements. In this case I will override the value if the visibility is set to false.

So we now have two routines to go... OnVisibleChanged and CoerceWidth.

The OnVisibleChanged is really a one-liner telling the ColumnDefinition.WidthProperty to perform its coercion.

C#
static void OnVisibleChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
   obj.CoerceValue(ColumnDefinition.WidthProperty);
}

So when we change the Visible property to either true or false this one liner forces the WidthProperty to be coerced. I.e., the WidthProperty will be filtered against our requirements.

So here is the last piece of our puzzle...

C#
static Object CoerceWidth(DependencyObject obj, Object nValue)
{
    return (((ColumnDefinitionExtended)obj).Visible) ? nValue : new GridLength(0);
}

This is a simple test. If our Visible property is set to true then return the width as is... If the visible is set to false then set the width to zero (thus hiding the column).  

You might ask that if I set the width to zero when I 'hide' then when I set the visible back to true 'unhide' how does the width get set back to its original value. As mentioned above the coercion does not overwrite the width value. The width value remains unchanged. The coercion simple alters the value that you 'see' from the WidthProperty. Remember it acts like a filter.

As an update to this article I have also included code in the below segment to override the MinWidth property due to a comment made about this being a problem. It is the same sort of thing as overriding the main Width property.  Couple of extra lines of code...

So there we have it - The complete solution for column hiding is as follows:

C#
using System;
using System.Windows;
using System.Windows.Controls;

namespace MyProject.MyNamespace.Extended
{
    public class ColumnDefinitionExtended : ColumnDefinition
    {
        // Variables
        public static DependencyProperty VisibleProperty;

        // Properties
        public Boolean Visible
        {
            get { return (Boolean)GetValue(VisibleProperty); }
            set { SetValue(VisibleProperty, value); }
        }

        // Constructors
        static ColumnDefinitionExtended()
        {
            VisibleProperty = DependencyProperty.Register("Visible",
                typeof(Boolean),
                typeof(ColumnDefinitionExtended),
                new PropertyMetadata(true, new PropertyChangedCallback(OnVisibleChanged)));
            
            ColumnDefinition.WidthProperty.OverrideMetadata(typeof(ColumnDefinitionExtended),
                new FrameworkPropertyMetadata(new GridLength(1, GridUnitType.Star), null,
                    new CoerceValueCallback(CoerceWidth)));

            ColumnDefinition.MinWidthProperty.OverrideMetadata(typeof(ColumnDefinitionExtended),
                new FrameworkPropertyMetadata((Double)0, null,
                    new CoerceValueCallback(CoerceMinWidth)));
        }

        // Get/Set
        public static void SetVisible(DependencyObject obj, Boolean nVisible)
        {
            obj.SetValue(VisibleProperty, nVisible);
        }
        public static Boolean GetVisible(DependencyObject obj)
        {
            return (Boolean)obj.GetValue(VisibleProperty);
        }

        static void OnVisibleChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            obj.CoerceValue(ColumnDefinition.WidthProperty);
            obj.CoerceValue(ColumnDefinition.MinWidthProperty);
        }
        static Object CoerceWidth(DependencyObject obj, Object nValue)
        {
            return (((ColumnDefinitionExtended)obj).Visible) ? nValue : new GridLength(0);
        }
        static Object CoerceMinWidth(DependencyObject obj, Object nValue)
        {
            return (((ColumnDefinitionExtended)obj).Visible) ? nValue : (Double)0;
        }
    }
}

I will leave it as an exercise for you to produce the corresponding RowDefinitionExtended class... (should take all of about 10-30 seconds depending on how fast you can cut/paste and find/replace...)

Examples

So let's look at an example of how to use the code...

At the top of your XAML file, stick in a namespace definition of where you have the classes defined. E.g...

XML
xmlns:extended="clr-namespace:MyProject.MyNamespace.Extended"

Here is an ordinary Grid with three columns and three rows... Nothing really to note here... This is what it looks like before you upgrade it to using the new definitions.

XML
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition />
        <ColumnDefinition />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition />
        <RowDefinition />
    </Grid.RowDefinitions>
</Grid>

So here is the same Grid using the new extended column and row definitions.

XML
<Grid>
    <Grid.ColumnDefinitions>
        <extended:ColumnDefinitionExtended />
        <extended:ColumnDefinitionExtended />
        <extended:ColumnDefinitionExtended />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <extended:RowDefinitionExtended />
        <extended:RowDefinitionExtended />
        <extended:RowDefinitionExtended />
    </Grid.RowDefinitions>
</Grid>

Not much really happening - but we can do things like:

XML
<Grid>
 <Grid.ColumnDefinitions>
    <extended:ColumnDefinitionExtended Visible="False" />

Or we could databind the Visible property to a datacontext/viewmodel...

XML
<Grid DataContext="Something">
  <Grid.ColumnDefinitions>
    <extended:ColumnDefinitionExtended Visible="{Binding ColumnVisible}"/>

Or programmatically:

C#
((ColumnDefinitionExtended)m_MyGrid.ColumnDefinitions[SomeColumnNumber]).Visible = false;

So there we have it - Our cake and we get to eat it too.....

Addendum

As an addendum to this article, in the comments section I proposed a recipe to convert to using the full Visibility tristate (ie Visible, Hidden, Collapsed) vs this solutions Visible true/false.

I've post the solution to this recipe as an additional download. Please read the comments section under the subject line 'Width of Grid still large through hiding a row' to follow the discussion.

People may find this solution better fits their specific requirements in their grid behaviour/usage.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Unknown
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionThanks Pin
Bernhard Hiller30-Mar-20 2:55
Bernhard Hiller30-Mar-20 2:55 
QuestionWhy did you include GetVisible and SetVisible? Pin
Tomáš Maleček15-Nov-16 1:33
Tomáš Maleček15-Nov-16 1:33 
QuestionWidth of Grid still large though hiding a row Pin
LumoWIT6-Jul-16 4:59
LumoWIT6-Jul-16 4:59 
AnswerRe: Width of Grid still large though hiding a row Pin
immortalus11-Jul-16 21:57
immortalus11-Jul-16 21:57 
GeneralRe: Width of Grid still large though hiding a row Pin
immortalus14-Jul-16 20:25
immortalus14-Jul-16 20:25 
GeneralMy vote of 5 Pin
andres_fprado24-Oct-15 19:01
professionalandres_fprado24-Oct-15 19:01 
QuestionWhy so difficult? Pin
Graf Kaliostro9-Jun-15 1:07
Graf Kaliostro9-Jun-15 1:07 
AnswerRe: Why so difficult? Pin
immortalus11-Jul-16 21:09
immortalus11-Jul-16 21:09 
GeneralRe: Why so difficult? Pin
Graf Kaliostro12-Jul-16 3:42
Graf Kaliostro12-Jul-16 3:42 
QuestionAwesome, helped a lot! Pin
Bartosz Jarmuż19-Apr-15 3:25
Bartosz Jarmuż19-Apr-15 3:25 
Questionthanks works a treat Pin
wazzawozza28-Aug-14 23:33
wazzawozza28-Aug-14 23:33 
QuestionProblems in * sizing Pin
sharda.bhagwatkar19-Aug-14 19:32
sharda.bhagwatkar19-Aug-14 19:32 
Questionwhen OverrideMetadata being called? Pin
jones&jones19-Aug-14 9:38
jones&jones19-Aug-14 9:38 
AnswerRe: when OverrideMetadata being called? Pin
immortalus19-Aug-14 18:51
immortalus19-Aug-14 18:51 
Generalit's not simply Pin
killedman24-Jul-14 10:57
killedman24-Jul-14 10:57 
GeneralRe: it's not simply Pin
immortalus24-Jul-14 17:40
immortalus24-Jul-14 17:40 
GeneralRe: it's not simply Pin
killedman25-Jul-14 3:12
killedman25-Jul-14 3:12 
GeneralBrilliant Solution Pin
Tom Pester24-Jul-14 1:56
Tom Pester24-Jul-14 1:56 
QuestionThis solution does not binding for me! Pelase help! Pin
Nickythita14-May-14 2:00
Nickythita14-May-14 2:00 
AnswerRe: This solution does not binding for me! Pelase help! Pin
immortalus24-Jul-14 18:02
immortalus24-Jul-14 18:02 
QuestionSorry, but it does not solve the problem Pin
dietmar paul schoder26-Nov-13 6:06
professionaldietmar paul schoder26-Nov-13 6:06 
AnswerRe: Sorry, but it does not solve the problem Pin
immortalus26-Dec-13 22:17
immortalus26-Dec-13 22:17 
GeneralRe: Sorry, but it does not solve the problem Pin
dietmar paul schoder26-Dec-13 22:29
professionaldietmar paul schoder26-Dec-13 22:29 
SuggestionMinwidth needs to be coersed to completely hide column or row Pin
Colin Maclean1-Oct-13 2:23
Colin Maclean1-Oct-13 2:23 
GeneralRe: Minwidth needs to be coersed to completely hide column or row Pin
immortalus26-Dec-13 22:07
immortalus26-Dec-13 22:07 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.