JokerMartini
JokerMartini

Reputation: 6157

wpf toggling visibility collapse only works once

What i want to do is collapse the bottom section of the WPF ui based on the checkbox toggle. This mostly works as expected so far. Below as you step through the images you'll see the collapse stops working once the grid splitter is moved. I do not understand why or how to fix this.

Start of the application, appears correct.

Initial opening of the application

Toggle the checkbox and the botttom section disappears as expected.

enter image description here

Toggle the checkbox once more and then move the splitter vertically, everything appears as expected.

enter image description here

Now toggle the checkbox one last time and you'll notice the top section doesn't fill the application like it once did before. This is where it appears broken! I would expect the yellow section to fill the UI like it did in the initial toggle.

enter image description here

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="250" Width="525"
        WindowStartupLocation="CenterScreen">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <CheckBox Name="ToggleVisibility" Margin="10" IsChecked="True" Content="Toggle Bottom Section"></CheckBox>

        <StackPanel Background="#feca00" Grid.Row="1">
            <TextBlock FontSize="20" Foreground="#58290A">Top Section</TextBlock>
        </StackPanel>

        <GridSplitter Grid.Row="2" Height="5" HorizontalAlignment="Stretch">
            <GridSplitter.Style>
                <Style TargetType="GridSplitter">
                    <Setter Property="Visibility" Value="Visible" />
                    <Setter Property="Background" Value="Red" />
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding ElementName=ToggleVisibility, Path=IsChecked}" Value="False">
                            <Setter Property="Visibility" Value="Collapsed"></Setter>
                            <Setter Property="Background" Value="Red"></Setter>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </GridSplitter.Style>
        </GridSplitter>

        <StackPanel Grid.Row="3" Background="LightBlue">
            <StackPanel.Style>
                <Style TargetType="StackPanel">
                    <Setter Property="Visibility" Value="Visible" />
                    <Setter Property="Background" Value="Red" />
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding ElementName=ToggleVisibility, Path=IsChecked}" Value="False">
                            <Setter Property="Visibility" Value="Collapsed"></Setter>
                            <Setter Property="Background" Value="Red"></Setter>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </StackPanel.Style>

            <TextBlock FontSize="20" Foreground="black">Bottom Section</TextBlock>
        </StackPanel>

    </Grid>
</Window>

Upvotes: 0

Views: 1747

Answers (2)

Grx70
Grx70

Reputation: 10359

The reason why this happens is that when you move the splitter it overwrites the Height property of the last RowDefinition and sets it to actual height of whatever is presented in the according row of the Grid - the second StackPanel (bottom section) in your case. It no longer is set to Auto, so even if you collapse the bottom section the row maintains it's height - hence the empty space.

I sometimes find myself in a similar situation, and what I do then is to span the control, which I want to take up all the space, across the remaining rows using Grid.RowSpan attached property. In your case you could achieve it by applying the following style to your first StackPanel (top section):

<Style TargetType="{x:Type StackPanel}">
    <Style.Triggers>
        <DataTrigger Binding="{Binding IsChecked, ElementName=ToggleVisibility}" Value="False">
            <Setter Property="Grid.RowSpan" Value="3" />
        </DataTrigger>
    </Style.Triggers>
</Style>

Then the StackPanel will span to the bottom of the Grid regardless of the following rows' heights.

EDIT

Since you're not satisfied with spanning the control, here's another solution: derive from RowDefinition class and wire your own visibility logic. Here's an example:

public class MyRowDefinition : RowDefinition
{
    static MyRowDefinition()
    {
        HeightProperty.OverrideMetadata(
            typeof(MyRowDefinition),
            new FrameworkPropertyMetadata
            {
                CoerceValueCallback = CoerceHeightPropertyValue
            });
    }

    private static object CoerceHeightPropertyValue(DependencyObject d, object baseValue)
    {
        if (Equals(d.GetValue(IsVisibleProperty), false))
            return new GridLength(0d);
        else
            return baseValue;
    }

    public bool IsVisible
    {
        get { return (bool)GetValue(IsVisibleProperty); }
        set { SetValue(IsVisibleProperty, value); }
    }

    public static readonly DependencyProperty IsVisibleProperty =
        DependencyProperty.Register(
            "IsVisible",
            typeof(bool),
            typeof(MyRowDefinition),
            new FrameworkPropertyMetadata
            {
                DefaultValue = true,
                PropertyChangedCallback = IsVisiblePropertyChanged
            });

    private static void IsVisiblePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        d.InvalidateProperty(RowDefinition.HeightProperty);
    }
}

Then use that class instead of RowDefinition in your grid and bind the IsVisible property to the check state of the checkbox.

Upvotes: 2

PScr
PScr

Reputation: 459

It looks like the StackPanel and the GridSplitter are collapsed, but that Grid.Row 3 is not. I'm not sure why the Auto height does no close it out. EDIT: I see that @Grx70 has explained it.

This: Hide grid row in WPF suggests that setting all children to Visibility.Collapsed ought to work, but you have the option of setting the height of Grid.Row 3 to 0.

I think the Height="5" of your GridSplitter might also keep it taking up space, taking precedence over the style setting (it is collapsed and gets Height 0 from that, but gets Height 5 from the Height="5", which wins (but it is still invisible). It might be better to but that in the style too:

 <Setter Property="Height" Value="5" />

Upvotes: 0

Related Questions