WPF: Changing sizes of grid rows and columns during runtime
At work I spent a lot of time trying to get my head around a issue with WPF grid resizing during runtime. I always found the controls don't resize the way I wanted and differently than how those in the exemplary program (MS Office in this case) do.
What I intended to do is something like the recent tab of a ribbon based UI like the one in Microsoft Office 2010. I wished to implement both the recent documents and recent locations so I did need the UI to look and work exactly the same as that of Office. Thanks to ribbon control the company purchased and some exploration the colleague has done we've already had the groundwork ready.
However the particular problem I was facing was I use a two-column grid to hold the two sections (i.e. the recent documents list and the recent places) and what I needed to achieve is the behaviour of the separation of the two sections which is like that they are separate evenly at the beginning but if the total width of them goes above a certain point, the width of the first section will stay unchanged. And a fact that I realized until very late is that the contents of the two sections from that point on stay the same as well in spite that the available space for the second section is growing.
I found if I updated the columns in the handler of change of overall width of the grid, then the actual grid width would only grow as the control/window grows but never shrink.
I also tried to search for some help online and I did notice there was an article onhttp://social.msdn.microsoft.com/ in which someone said there are caveats when one tries to resize grid dimensions during runtime and she/he suggested another thread however unfortunately that link was broken.
Most of the other articles seem quite irrelevant, so instead of wandering aimlessly online, I turned to trying to figure that out myself. I isolated the resizing code from the rest to make sure it was purely from the resizing logic itself and I found it was. Then I thought about the mechanism of the resizing process and realized that it might be to do with the fact that the way I updated the column widths has a mutual dependency with the overall grid width (fair enough, because I updated the column widths in the event handler that is invoked when the grid with changed), as updating the column width will lead to the change of the grid width and that causes the column width to change again. However I didn't manage simulate the entire process so as to explain why that happened that specific way. I didn't have to, at least I was pretty sure that that cyclic dependency was the cause of the problem and that was enough. So I quickly moved on to figuring out how to solve the issue.
After a few experiments, I finally believed that one of the best and easiest way is in the handler responding to the change of the container of the grid update the width of the columns with the overall width of the grid in mind and leave that update to naturally decide the overall width. And this works.
The lesson learnt from this is that UI development is no easy job, one should always pay attention to the property dependencies of the UI elements and the simplest way is always the best.
The following is the code sample of a program that demonstrates the solution, in which the first (left) cell has about the same size as and grows at the same rate as the right cell until the width of it reaches 300.
MainWindow.xaml
1 <Window x:Class="GridResizeTest.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 Title="MainWindow" Height="350" Width="525" Name="RootWindow"> 5 <Grid ShowGridLines="True" Name="MainGrid"> 6 <Grid.ColumnDefinitions> 7 <ColumnDefinition Name="Col1"/> 8 <ColumnDefinition Name="Col2"/> 9 </Grid.ColumnDefinitions> 10 </Grid> 11 </Window>
MainWindow.cs
using System; using System.Windows; namespace GridResizeTest { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow { #region Constructors /// <summary> /// Instantiates a main window /// </summary> public MainWindow() { InitializeComponent(); const double max = 300; RootWindow.SizeChanged += (sender, args) => { var w1 = RootWindow.ActualWidth * 0.5; w1 = Math.Min(max, w1); var w2 = RootWindow.ActualWidth - w1; Col1.Width = new GridLength(w1); Col2.Width = new GridLength(w2); }; } #endregion } }