views:

2394

answers:

3

I'm having some trouble getting this to work in a WPF app I'm working on. Basically, what I'm after is something like the Task pane in an MMC:

  • The app has three columns in the main part of the display. I need a column on the right side which is resizable. I presume this means using a Grid with a GridSplitter but anything that works will do.
  • I want to be able to save the width of the right-side column when the app is closed and load it when the app is opened but this should be an initial size: the user should be able to resize it.
  • When I resize the window, I want the left- and right-side columns to stay the same size and the middle column to resize with the window width.
  • The left- and right-side columns need to have a minimum width. When I resize the right-side column I want the centre column to get smaller but not the left-side column.
  • I also want to be able to toggle the visibility of the right-side column with a toggle button which is outside the column and when it returns to visibility I want it to be the same width it was before.

I'm trying to do as much as possible in XAML and with binding.

And can I have it topped with cream, ice cream and chocolate chips, please? :-)

+3  A: 

Set the columndefinition Width to Auto and put a control inside that column and give Star for the other columns . Whenever you want to hide the column with content, set the control.Visibility=Collapsed and since column width is Auto, you wont see that column and the remaining columns will take the space.

Jobi Joy
I can't get this to work with the desired behaviour. Setting Width to Auto means resizing the window affects the columns in what would appear to the user as non-sensical ways.
serialhobbyist
+3  A: 

As I read your requirements, instead of thinking of a Grid, I think of a DockPanel.

<DockPanel>
    <Grid Name="right"
        DockPanel.Dock="Right" MinWidth="100" />
    <Grid Name="Left"
        DockPanel.Dock="Left" MinWidth="100" />
    <Grid Name="middle" />
</DockPanel>

If you make a way to resize right, then middle will change as right is resized. If you resize the window, only middle will change. Storing and setting the Width of right is up to you, but shouldn't be hard.

As for allowing the user to resize right, that will a bit trickier, but I found this article that should help. This other article might help even more.

For the visibility of right, you can set its Visibility to Collapsed to hide it and restore it by setting it to Visible.

Note: The panels inside don't have to be Grids, but you will want to use some sort of Panel for each. Whatever you have inside your current Grid columns should work just fine.

Joel B Fant
I used your approach and the articles you suggested and it works like a charm. I bind the Visibility properties of both the content and the splitter to the toggle button and all works as planned. Thanks very much for your help.
serialhobbyist
You're welcome.
Joel B Fant
+2  A: 

I used a Grid with GridSplitters since this made it really easy to resize the middle column while maintaining the widths of the left and right columns.

XAML:

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="MainWindow"
    Title="Main Window"
    Width="640" Height="480">

    <Grid>
        <Grid.ColumnDefinitions>
            <!-- Left column -->
                <ColumnDefinition Width="200" MinWidth="100"/>
                <!-- Left GridSplitter column -->
                <ColumnDefinition Width="5"/>
                <!-- Center column. A width of * means the column will fill
                     any remaining space. -->
                <ColumnDefinition Width="*"/>
                <!-- Right GridSplitter column -->
                <ColumnDefinition x:Name="RightSplitterColumn" Width="5"/>
                <!-- Right column -->
                <ColumnDefinition x:Name="RightColumn" Width="200"
                                  MinWidth="100"/>
                </Grid.ColumnDefinitions>
                <GridSplitter Grid.Column="1" HorizontalAlignment="Stretch" />
                <GridSplitter Grid.Column="3" HorizontalAlignment="Stretch" />
                <Button x:Name="ToggleButton" Grid.Column="2"
                        Content="Toggle Right Column" Width="150" Height="25"
                        Click="ToggleButton_Click" />
    </Grid>
</Window>

Code-Behind

When hiding the right column, I just set the column width to 0 since grid columns don't have a visibility property.

public partial class MainWindow : Window
{
    private double rightColumnWidth;
    private double rightColumnMinWidth;
    private bool rightColumnHidden;

    public MainWindow()
    {
        this.InitializeComponent();
    }

    private void ToggleButton_Click(object sender, RoutedEventArgs e)
    {
        if (rightColumnHidden)
        {
            // Restore the widths.
            RightColumn.MinWidth = rightColumnMinWidth;
            RightColumn.Width = new GridLength(rightColumnWidth);
            RightSplitterColumn.Width = new GridLength(5);
        }
        else
        {
            // Remember the user-set widths for the columns.
            rightColumnWidth = RightColumn.Width.Value;
            rightColumnMinWidth = RightColumn.MinWidth;

            // Remember to set the minimum width to 0 before changing the actual
            // width.
            RightColumn.MinWidth = 0;
            RightColumn.Width = new GridLength(0);
            RightSplitterColumn.Width = new GridLength(0);
        }

        rightColumnHidden = !rightColumnHidden;
    }
}

As for saving and restoring the column widths on startup, I would just store the width variables to a settings file and then apply them when your app is reopened.

Greg
Thanks for the suggestion: it does achieve what I wanted so I've 'upped' it but I think this sort of thing is better done in XAML with binding wherever possible. I've accepted Joel's answer because it's closer to that approach. Thanks again for your work.
serialhobbyist