One of my pet peeves with using WPF is that a Grid needs to have it’s Rows and Columns defined. One day I decided I was tired of messing with Row/Column definitions for simple Grids, so sat down to write some AttachedProperties.
Now, instead of writing something like this:
<Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> </Grid>
I can write this:
<Grid local:GridHelpers. </Grid>
I can also use this to draw a Grid with a dynamic number of rows or columns, or where I don’t know the number of rows/columns beforehand such as an ItemsPanelTemplate for an ItemsControl.
<Grid local:GridHelpers.
GridHelpers Code
Here is the AttachedProperty code:
public class GridHelpers { #region RowCount Property /// <summary> /// Adds the specified number of Rows to RowDefinitions. /// Default Height is Auto /// </summary> public static readonly DependencyProperty RowCountProperty = DependencyProperty.RegisterAttached( "RowCount", typeof(int), typeof(GridHelpers), new PropertyMetadata(-1, RowCountChanged)); // Get public static int GetRowCount(DependencyObject obj) { return (int)obj.GetValue(RowCountProperty); } // Set public static void SetRowCount(DependencyObject obj, int value) { obj.SetValue(RowCountProperty, value); } // Change Event - Adds the Rows public static void RowCountChanged( DependencyObject obj, DependencyPropertyChangedEventArgs e) { if (!(obj is Grid) || (int)e.NewValue < 0) return; Grid grid = (Grid)obj; grid.RowDefinitions.Clear(); for (int i = 0; i < (int)e.NewValue; i++) grid.RowDefinitions.Add( new RowDefinition() { Height = GridLength.Auto }); SetStarRows(grid); } #endregion #region ColumnCount Property /// <summary> /// Adds the specified number of Columns to ColumnDefinitions. /// Default Width is Auto /// </summary> public static readonly DependencyProperty ColumnCountProperty = DependencyProperty.RegisterAttached( "ColumnCount", typeof(int), typeof(GridHelpers), new PropertyMetadata(-1, ColumnCountChanged)); // Get public static int GetColumnCount(DependencyObject obj) { return (int)obj.GetValue(ColumnCountProperty); } // Set public static void SetColumnCount(DependencyObject obj, int value) { obj.SetValue(ColumnCountProperty, value); } // Change Event - Add the Columns public static void ColumnCountChanged( DependencyObject obj, DependencyPropertyChangedEventArgs e) { if (!(obj is Grid) || (int)e.NewValue < 0) return; Grid grid = (Grid)obj; grid.ColumnDefinitions.Clear(); for (int i = 0; i < (int)e.NewValue; i++) grid.ColumnDefinitions.Add( new ColumnDefinition() { Width = GridLength.Auto }); SetStarColumns(grid); } #endregion #region StarRows Property /// <summary> /// Makes the specified Row's Height equal to Star. /// Can set on multiple Rows /// </summary> public static readonly DependencyProperty StarRowsProperty = DependencyProperty.RegisterAttached( "StarRows", typeof(string), typeof(GridHelpers), new PropertyMetadata(string.Empty, StarRowsChanged)); // Get public static string GetStarRows(DependencyObject obj) { return (string)obj.GetValue(StarRowsProperty); } // Set public static void SetStarRows(DependencyObject obj, string value) { obj.SetValue(StarRowsProperty, value); } // Change Event - Makes specified Row's Height equal to Star public static void StarRowsChanged( DependencyObject obj, DependencyPropertyChangedEventArgs e) { if (!(obj is Grid) || string.IsNullOrEmpty(e.NewValue.ToString())) return; SetStarRows((Grid)obj); } #endregion #region StarColumns Property /// <summary> /// Makes the specified Column's Width equal to Star. /// Can set on multiple Columns /// </summary> public static readonly DependencyProperty StarColumnsProperty = DependencyProperty.RegisterAttached( "StarColumns", typeof(string), typeof(GridHelpers), new PropertyMetadata(string.Empty, StarColumnsChanged)); // Get public static string GetStarColumns(DependencyObject obj) { return (string)obj.GetValue(StarColumnsProperty); } // Set public static void SetStarColumns(DependencyObject obj, string value) { obj.SetValue(StarColumnsProperty, value); } // Change Event - Makes specified Column's Width equal to Star public static void StarColumnsChanged( DependencyObject obj, DependencyPropertyChangedEventArgs e) { if (!(obj is Grid) || string.IsNullOrEmpty(e.NewValue.ToString())) return; SetStarColumns((Grid)obj); } #endregion private static void SetStarColumns(Grid grid) { string[] starColumns = GetStarColumns(grid).Split(','); for (int i = 0; i < grid.ColumnDefinitions.Count; i++) { if (starColumns.Contains(i.ToString())) grid.ColumnDefinitions[i].Width = new GridLength(1, GridUnitType.Star); } } private static void SetStarRows(Grid grid) { string[] starRows = GetStarRows(grid).Split(','); for (int i = 0; i < grid.RowDefinitions.Count; i++) { if (starRows.Contains(i.ToString())) grid.RowDefinitions[i].Height = new GridLength(1, GridUnitType.Star); } } }
This is what I was looking for but when I put a textblock inside of the grid in one of the columns it tells me that my column range is only (0-0). How do I select which column I want to put the textblock in?
Specify the Grid.Column and Grid.Row of the child control
This is exactly what i am looking right now, Thanks for the help
However, i am not able to set the RowCount and Column Count. I have added teh GridHelper.cs and added the xmlns: local at top of my XAML page.
Where to set the RowCount ?
Hi Anikita, you would set the the local:GridHelpers.RowCount on the Grid tag itself.
Ok, I’m gonna steal this class and keep it in my vault! FOREVER!! Joke 😛 Thanks so much for this Rachel! 🙂
Hi Rachel,
I tried your GridHelpers and it’s very handy and useful.
However, I have encountered an very small issue, which is that NullReferenceException occurred in the code e.NewValue.ToString() in void StarRowsChanged, when I bind GridHelpers.StarRows to a null string. To resolve this issue, simply change e.NewValue.ToString() to e.NewValue?.ToString().
I understand that binding this attached property to a null string is meaningless, but just in case that someone unpurposely set the property to null.
PS: similar issue will occur when binding GridHelpers.StarColumns to null.
Environment: .NET 4.5.2/WIn7 x64/Visual Studio Community 2015 with Update 1
Thanks Wei, that is useful to know! In most cases, I don’t mind throwing exceptions in my converters so it tells me there is a problem I need to address, however in cases where null is an acceptable value, this is a good change to make.
Hello Rachel,
an identical class / source code can be found here:
As your post is from 2011 and the first upload on CodePlex is from 2012, I guess you are the original author of that code. Is that correct?
If yes, can you grant me following rights without any obligations:
– Use the code in a proprietary software
– Modify the code as part of a properietary software
– Re-distribute the code as part of a proprietary software
Hope to hear from you (either as private email or as comment on this site).
Hi David, yes you are welcome to use the code for any project you have. It is pretty basic, and would be easy to replicate by anyone who wanted the same functionality. Thank you for checking with me!
Hi Rachel, Thank you for the nice post. Is this code has any licensing or can we use this in our program ? Comes under Open Source license or any other. Please let us know. Thank you.
Hi Kiran,
You’re welcome to use this code in your program. Its nothing really complex, and easy to duplicate if anyone wants to 🙂
Thanks for checking though!
Rachel
Thank you so much.
Excellent! I needed this so thank you. I don’t know why Microsoft didn’t think of this :p
Thanks for your GridHelper. Very Handy! I use it all the time.
Hi!
Is there a possibility to add ItemSource propertz to grid control and fill cells by binding?
Hi Marcin,
If that is your requirement, I’d recommend looking into an ItemsControl with an ItemsTemplate overridden to use a Grid or UniformGrid. Make sure you also set the ItemContainerStyle so it binds Grid.Row and Grid.Column properties too!
Thanks,
Rachel
Very convenient. I use it all the time now! Thanks!!
Nice solution Rachel. Thanks for sharing!
Rachel, How to define rowcount and column count? I am not getting how to set rowcount and columncount in the above code
Hi Rakesh, you’ll need to make sure you have an xmlns setup at the top of your Window or UserControl that points to the namespace of your class containing the Grid’s attached properties. If you’re having problems with it, I’d suggest asking a question on StackOverflow with your code and a description of your problem, and I’m sure someone could help you out. If you wanted you could even leave the link to your question here and I could take a look if nobody else helps you out first =)
Hi Rachel,
Is there any source code for this? I have a quick question, are you create a class and inherits the control as your base? Else, how are you able to create dependency properties for that. Please advise.
Thank you.
Hi DB,
No, we do not have to inherit the Grid for this. That’s the beauty of Dependency Properties – they can be used to define a property that can be attached to any class, and do not have to exist on that class itself. The only code used is included in the post.
Thanks,
Rachel
Can you please elaborate on where RowCount and ColumnCount would be defined? I tried defining them as properties in my MainWindow but they’re not binding. Are these properties supposed to be defined somewhere in particular?
In my case, I put them in their own class called GridHelpers and make sure that class is in a namespace that is defined in my XAML using an xmlns alias. In the example, I used the alias “local”, so can refer to the properties using “local:GridHelpers.RowCount”.
Hi Rachel,it is an excellent post. I am new on WPF and I would like to know if it is possible to add a GridSplitter between each column for example:
column | gridsplitter | columns
column | gridsplitter | column | gridsplitter | column
If you can help me 🙂
Thanks,
Richard
Hi Richard, I don’t think that can be easily done with a simple Attached Property.
The problem is you’d need to insert the GridSplitter between the ContentPresenter objects, which means you’d really need (ColumnCount * 2) – 1 Columns to account for the GridSplitters, and you’ll have to adjust the Grid.Column property of all child objects except the first one to place them at Grid.Column * 2, or they’ll overlap the GridSplitter columns.
Such complexity is probably better suited for a new custom panel instead of an Attached Property.
Thanks Rachel !!!!!
Nice worked rachel!
Hi,
first of all, thanks for the great code! I wanted to use it on my Windows Phone 8 project, but got a strange “Ambiguous match found” error message from the compiler.
The solution is to rename the *private* methods SetStarRows and SetStarColumns to something else, like DoSetStarRows and DoSetStarColumns. For some reason, the compiler does not like having private methods named exactly like the public methods…
Hope that helps,
Stefan
Rachel, thanks for your blog!
Stefan, thanks for your solution!
I’ve got “Ambiguous match found” error message too. It happened when I tried to bind all four properties.
I wish I had run across this sooner, I’ve been trying to figure out how to do this for a while. I figured it would be easy to do, I just had no clue where to start. Thanks for posting this.
Hi Rachel, I really admire your work here and on StackOverflow! Keep it up!
Hi,
I’m using it in order to add ItemsControl items in the ItemsPanelTemplate as a Grid but the items always appear in the column 0 of my grid even if I bind the Grid.Column property in the ItemContainerStyle to a ColumnCount dp in my VM…
Any guess why?
i just found a way to do it, i just had to add this part at the end of the ColumnCountChanged event:
for (int i = 0; i < childCount; i++)
{
var child = grid.Children[i] as FrameworkElement;
Grid.SetColumn(child, i);
}
and i used a converter for a binding on a ColumnCount Property in my ViewModel to make all the column equally sized andd tadaa!!! it worked!!!
thx anyway for all your amazing pieces of code!
Glad you got it worked out 🙂
Hi,
Thanks for your great post. I have done the same thing but slightly different. The advantage of your approach is that it suitable for binding.
I have posted my approach here:
Wow! Works in design time too! Thanks a lot!
Cool! I can see part of the code on iPad There is no scroll bar somehow.
Hi Frank,
I’ll have to report that bug to WordPress. You should be able to highlight the code and drag your mouse to scroll/copy it since it’s all there. It’s just not showing the scrollbars. You can also use View Source to see it. Thanks for letting me know of that!
Very neat! I’ll definitely find this handy, thanks 🙂