1

I have a wpf grid with two columns that should be equally spaced. I know about star (*) sizing, but when I set the column width to be equally spaced, the actual column changes at runtime. The overall width of the grid will change based on the window size, but I want to force the two columns to always be of equal width inside that grid.

I've currently got a lot of nested elements assigned to the first column, and the second column only has an ItemsControl panel which only shows items when a button event action is taken... so there are times when it's empty. My assumption is that because the second column is empty, the first column is automatically taking up as much space as it can. Is there a way to force the columns to remain proportionally fixed? Here's my basic setup:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="1*"></ColumnDefinition>
        <ColumnDefinition Width="1*" ></ColumnDefinition>
    </Grid.ColumnDefinitions>
    <StackPanel Grid.Column="0">
        <!-- Lots of nested WPF elements inside this stack panel -->
    </StackPanel>
    <ItemsControl Grid.Column="1" ItemsSource="{Binding Tasks}">
        <ItemsControl.Template>
            <ControlTemplate TargetType="{x:Type ItemsControl}" >
                <ItemsPresenter/>
            </ControlTemplate>
        </ItemsControl.Template>

        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>

        <ItemsControl.ItemTemplate>
            <DataTemplate DataType="cc:Tasks">
                <StackPanel Orientation="Vertical">
                    <TextBlock Text="{Binding Title}"></TextBlock>
                </StackPanel>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Grid>

I've read this thread, but I'm not entirely sure if that answer fits this problem.

andyopayne
  • 1,348
  • 3
  • 24
  • 49
  • 1
    Would a `UniformGrid` work better for you than a regular grid for what you want to do? – Bradley Uffner Jun 16 '16 at 18:54
  • Are you saying that when Tasks is null or empty, the second column changes width? How much space is the first column taking up, when it "takes up as much space as it can"? The whole window? +1 on `UniformGrid` btw. Set Rows="1" and Columns="2" on it. – 15ee8f99-57ff-4f92-890c-b56153 Jun 16 '16 at 18:56
  • The first column takes up as much space as needed. I have a textbox which displays a filepath. So, when as user changes the filepath, then the width of that whole column changes to adjust. I'd rather the textbox width be bound to the first column width which is then bound to be exactly 50% of the overall grid width. Thanks for the suggestion on UniformGrid. I'll look into it. – andyopayne Jun 16 '16 at 19:03
  • Do you have HorizontalAlignment="Stretch" on the outermost element in the left column? It sounds like the left column contents are autosizing rather than sizing to their container, which may continue to be a problem with UniformGrid. That stuff can be frustrating in WPF at times. – 15ee8f99-57ff-4f92-890c-b56153 Jun 16 '16 at 19:09
  • But you have put WPF in a real tuff spot if it does not have enough room. I would put each column in scrollview. And look at binding each width the Grid actual width with a converter to half. – paparazzo Jun 16 '16 at 19:11
  • @Paparazzi, do you have a code sample to look at for your suggestion? I've taken out any 'stretch' values in any of my elements and changed over to UniformGrid, but it still doesn't fix the issue. – andyopayne Jun 16 '16 at 19:15
  • https://msdn.microsoft.com/en-us/library/system.windows.controls.scrollviewer(v=vs.110).aspx – paparazzo Jun 16 '16 at 19:17
  • Sorry, I meant a code snippet for the converter. – andyopayne Jun 16 '16 at 19:25
  • SO is not a coding service. What have you tried? Search on width converter. – paparazzo Jun 16 '16 at 20:38
  • I was able to figure it out based on you converter suggestion. I don't have access to my computer at the moment but will post an answer in the morning. Thanks! – andyopayne Jun 17 '16 at 01:21

1 Answers1

0

Following @Paparazzi advice, I created a converter to bind the width of the scrollviewer. Here's how I did it:

In the C# code behind, I create the converter:

public class PercentageConverter : MarkupExtension, IValueConverter
{
    private static PercentageConverter _instance;

    #region IValueConverter Members

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return System.Convert.ToDouble(value) * System.Convert.ToDouble(parameter);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    #endregion

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return _instance ?? (_instance = new PercentageConverter());
    }
}

Then, in my .xaml code, I use it like this:

<UserControl.Resources>
    <ResourceDictionary>
        <local:PercentageConverter x:Key="PercentageConverter"></local:PercentageConverter>
    </ResourceDictionary>
</UserControl.Resources>

<Grid Name="ParentGrid">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"></ColumnDefinition>
        <ColumnDefinition Width="Auto" ></ColumnDefinition>
    </Grid.ColumnDefinitions>
    <ScrollViewer Grid.Column="0" 
            HorizontalScrollBarVisibility="Auto" 
            MinWidth="200" 
            Width="{Binding Path=ActualWidth, ElementName=ParentGrid, Converter={StaticResource PercentageConverter}, ConverterParameter='0.6'}">
        <StackPanel Orientation="Vertical" ScrollViewer.CanContentScroll="True">
            <!-- Lots of other WPF Framework elements -->
        </StackPanel>
    </ScrollViewer>
    <ItemsControl Grid.Column="1" ItemsSource="{Binding Tasks}">
        <ItemsControl.Template>
            <ControlTemplate TargetType="{x:Type ItemsControl}" >
                <ItemsPresenter/>
            </ControlTemplate>
        </ItemsControl.Template>

        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel></StackPanel>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>

        <ItemsControl.ItemTemplate>
            <DataTemplate DataType="cc:Tasks">
                <StackPanel Orientation="Vertical">
                    <TextBlock Text="{Binding Title}"></TextBlock>
                </StackPanel>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Grid>

The key is to define the converter in the ResourceDictionary. Then, I set the width property of the ScrollViewer element to bind to the actual width of the parent grid, using the converter to set it to a certain percentage (in this case 0.6 is the parameter or 60% of the overall width of the parent grid). I hope this helps someone in the future.

andyopayne
  • 1,348
  • 3
  • 24
  • 49