Click here to Skip to main content
Click here to Skip to main content

WPF Grid Column and Row Hiding

By , 6 Sep 2012
 

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...):

  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. 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...

  3. 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:

public class ColumnDefinitionExtended : ColumnDefinition
{
}

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

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:

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.

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...

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.

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

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)));
        }

        // 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);
        }
        static Object CoerceWidth(DependencyObject obj, Object nValue)
        {
            return (((ColumnDefinitionExtended)obj).Visible) ? nValue : new GridLength(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...

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.

<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.

<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:

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

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

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

Or programmatically:

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

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

License

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

About the Author

immortalus
Unknown
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
Hint: For improved responsiveness ensure Javascript is enabled and choose 'Normal' from the Layout dropdown and hit 'Update'.
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionHide Column Definition Extendedmemberkidrockfl11 Apr '13 - 17:19 
AnswerRe: Hide Column Definition Extendedmemberimmortalus11 Apr '13 - 17:50 
GeneralMy vote of 5memberSchumiii7 Feb '13 - 4:36 
QuestionAuto sizing columns??memberSchumiii7 Feb '13 - 4:35 
AnswerRe: Auto sizing columns??memberimmortalus13 Feb '13 - 15:42 
QuestionSimplify more?memberThornik4 Oct '12 - 5:45 
AnswerRe: Simplify more? [modified]memberimmortalus4 Oct '12 - 11:38 
GeneralRe: Simplify more?memberThornik8 Oct '12 - 8:21 
AnswerRe: Simplify more?memberimmortalus4 Oct '12 - 16:15 
GeneralRe: Simplify more?memberThornik5 Oct '12 - 11:21 
GeneralRe: Simplify more?memberimmortalus5 Oct '12 - 14:36 
QuestionMy vote is 5memberglob3r1 Oct '12 - 3:24 
QuestionnicememberCIDev6 Sep '12 - 10:40 
QuestionGood solution, I like the use of the seldom used DP coercemvpSacha Barber6 Sep '12 - 0:45 
QuestionRe: Good solution, I like the use of the seldom used DP coercememberMatt T Heffron6 Sep '12 - 7:05 
AnswerRe: Good solution, I like the use of the seldom used DP coercememberimmortalus6 Sep '12 - 17:53 
SuggestionDefault width required for Column to behave as standard ColumnDefinitionmemberMember 78879025 Sep '12 - 7:09 
GeneralRe: Default width required for Column to behave as standard ColumnDefinitionmemberimmortalus5 Sep '12 - 18:29 
GeneralMy vote of 5memberVllado216 Aug '12 - 3:04 
GeneralMy vote of 5mentorWayne Gaylard9 Aug '12 - 23:39 

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

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130523.1 | Last Updated 6 Sep 2012
Article Copyright 2012 by immortalus
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid