I have a WPF page, with a Grid on it.
There are three rows. Row 0 contains a GridView with Height="*"
. Row 1 contains a GridSplitter with Height="auto"
. Row 2 contains a details form with Height="2*"
.
Here's the thing - I have a button that is supposed to toggle the visibility of the details form. And that works just fine. Except that it just hides the form in Row 2, it doesn't expand the Grid in Row 0 to fill the space. What I want is for the button to toggle GridView in Row 0 to take up all the space, and then to toggle back to where things were.
Clearly playing around with the Visibility of the form inside the row won't accomplish what I want.
But what do I need to be playing around with?
I had to introduce an Attached Dependency Property to handle this in my own application:
<Grid c:GridSplitterController.Watch="{Binding ElementName=GS_DetailsView}">
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="200" />
</Grid.RowDefinitions>
<SomeControl Grid.Row="0" />
<GridSplitter x:Name="GS_DetailsView"
Height="4"
Grid.Row="1"
VerticalAlignment="Top"
HorizontalAlignment="Stretch"
ResizeBehavior="PreviousAndCurrent"
ResizeDirection="Rows"
Visibility="{Binding ShowDetails,
Converter={StaticResource boolvis}}" />
<OtherControl Grid.Row="1"
Margin="0,4,0,0"
Visibility="{Binding ShowDetails,
Converter={StaticResource boolvis}}" />
</Grid>
First define a suitable attached property on a DependencyObject
:
public static GridSplitter GetWatch(DependencyObject obj)
{
return (GridSplitter)obj.GetValue(WatchProperty);
}
public static void SetWatch(DependencyObject obj, GridSplitter value)
{
obj.SetValue(WatchProperty, value);
}
public static readonly DependencyProperty WatchProperty =
DependencyProperty.RegisterAttached(
"Watch",
typeof(GridSplitter),
typeof(DependencyObject),
new UIPropertyMetadata(null, OnWatchChanged));
Then listen for IsVisibleChanged
:
private static void OnWatchChanged(DependencyObject obj,
DependencyPropertyChangedEventArgs e)
{
if (obj == null) return;
if (obj is Grid)
{
var grid = obj as Grid;
var gs = e.NewValue as GridSplitter;
if (gs != null)
{
gs.IsVisibleChanged += (_sender, _e) =>
{
UpdateGrid(
grid,
(GridSplitter)_sender,
(bool)_e.NewValue,
(bool)_e.OldValue);
};
}
}
}
Once you are watching for these changes, you need to save or restore the GridLength
values from the row or columns you are watching (for brevity I'm only including Rows):
// Given: static Dictionary<DependencyObject, GridLength> oldValues;
private static void UpdateGrid(Grid grid, GridSplitter gridSplitter, bool newValue, bool oldValue)
{
if (newValue)
{
// We're visible again
switch (gridSplitter.ResizeDirection)
{
case GridResizeDirection.Columns:
break;
case GridResizeDirection.Rows:
int ridx = (int)gridSplitter.GetValue(Grid.RowProperty);
var prev = grid.RowDefinitions.ElementAt(GetPrevious(gridSplitter, ridx));
var curr = grid.RowDefinitions.ElementAt(GetNext(gridSplitter, ridx));
if (oldValues.ContainsKey(prev) && oldValues.ContainsKey(curr))
{
prev.Height = oldValues[prev];
curr.Height = oldValues[curr];
}
break;
}
}
else
{
// We're being hidden
switch (gridSplitter.ResizeDirection)
{
case GridResizeDirection.Columns:
break;
case GridResizeDirection.Rows:
int ridx = (int)gridSplitter.GetValue(Grid.RowProperty);
var prev = grid.RowDefinitions.ElementAt(GetPrevious(gridSplitter, ridx));
var curr = grid.RowDefinitions.ElementAt(GetNext(gridSplitter, ridx));
switch (gridSplitter.ResizeBehavior)
{
// Naively assumes only one type of collapsing!
case GridResizeBehavior.PreviousAndCurrent:
oldValues[prev] = prev.Height;
prev.Height = new GridLength(1.0, GridUnitType.Star);
oldValues[curr] = curr.Height;
curr.Height = new GridLength(0.0);
break;
}
break;
}
}
}
All that is left is a suitable implementation of GetPrevious
and GetNext
:
private static int GetPrevious(GridSplitter gridSplitter, int index)
{
switch (gridSplitter.ResizeBehavior)
{
case GridResizeBehavior.PreviousAndNext:
case GridResizeBehavior.PreviousAndCurrent:
return index - 1;
case GridResizeBehavior.CurrentAndNext:
return index;
case GridResizeBehavior.BasedOnAlignment:
default:
throw new NotSupportedException();
}
}
private static int GetNext(GridSplitter gridSplitter, int index)
{
switch (gridSplitter.ResizeBehavior)
{
case GridResizeBehavior.PreviousAndCurrent:
return index;
case GridResizeBehavior.PreviousAndNext:
case GridResizeBehavior.CurrentAndNext:
return index + 1;
case GridResizeBehavior.BasedOnAlignment:
default:
throw new NotSupportedException();
}
}