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

WPF Grid Column and Row Hiding

, 28 Dec 2013
Rate this:
Please Sign up or sign in to vote.
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...):

  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.

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:

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

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
No Biography provided

Comments and Discussions

 
GeneralRe: Simplify more? Pinmemberimmortalus5-Oct-12 14:36 
QuestionMy vote is 5 Pinmemberglob3r1-Oct-12 3:24 
Questionnice PinmemberCIDev6-Sep-12 10:40 
QuestionGood solution, I like the use of the seldom used DP coerce PinmvpSacha Barber6-Sep-12 0:45 
When I was about 1/2 way through I thought ah this could be attached DP, but I see why you went for inheritance, so 5 from me
Sacha Barber
  • Microsoft Visual C# MVP 2008-2012
  • Codeproject MVP 2008-2012
Open Source Projects
Cinch SL/WPF MVVM

Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue
 
My Blog : sachabarber.net

QuestionRe: Good solution, I like the use of the seldom used DP coerce PinmemberMatt T Heffron6-Sep-12 7:05 
AnswerRe: Good solution, I like the use of the seldom used DP coerce Pinmemberimmortalus6-Sep-12 17:53 
SuggestionDefault width required for Column to behave as standard ColumnDefinition PinmemberMember 78879025-Sep-12 7:09 
GeneralRe: Default width required for Column to behave as standard ColumnDefinition Pinmemberimmortalus5-Sep-12 18:29 
GeneralMy vote of 5 PinmemberVllado216-Aug-12 3:04 
GeneralMy vote of 5 PinmentorWayne Gaylard9-Aug-12 23:39 

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

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

| Advertise | Privacy | Mobile
Web03 | 2.8.140721.1 | Last Updated 28 Dec 2013
Article Copyright 2012 by immortalus
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid