views:

544

answers:

4

I have a list box that has a grid in it's template to allow 4 columns. One column is the actual text of the list box. I want that column to fill the available horizontal space.

The column definitions are:

<ControlTemplate TargetType="ListBoxItem">
    <Grid ScrollViewer.CanContentScroll="True" Margin="2">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="20" />
            <ColumnDefinition Width="50" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="30" />
        </Grid.ColumnDefinitions>
        <CheckBox VerticalAlignment="Center" Grid.Column="0" IsChecked="{Binding IsSelected,
                                        RelativeSource={RelativeSource TemplatedParent},
                                        Mode=TwoWay}" />
        <TextBlock VerticalAlignment="Center" Grid.Column="1" Margin="5,0,5,0" Text="{Binding Id}" />
        <TextBlock VerticalAlignment="Center" Grid.Column="2" Margin="5,0,5,0" Text="{Binding Title}" />
        <Button HorizontalAlignment="Right" Grid.Column="3" Tag="{Binding Id}" Margin="5,0,5,0">-&gt;</Button>
    </Grid>
</ControlTemplate>

I have tried several permutations of this and nothing will get it to fix in the window except for an explicit size setting. Then when I resize the window it does not resize with it.

The list box is wrapped in a scrollviewer to allow vertical scrolling (I am guessing that is what is messing me up.)

Any ideas on how to get the third column to fill just the available space and no more?

A: 

Yes I would suggest the scroll viewer is messing you up, you could bind the Grid's width to the parent objects actual width, so there is a defined size in which the * has to actually fill.

Andrew
+1  A: 

Do you need the control behaviours of ListBox, or are you using it for layout only.

If you are using it just for layout, you could swap the ListBox for a ItemsControl inside of a ScrollViewer.

This code (butchered to work in designer) show this:

<ScrollViewer>
 <ItemsControl>
  <!--<ListBox>-->
  <Grid Margin="2">
   <Grid.ColumnDefinitions>
    <ColumnDefinition Width="20" />
    <ColumnDefinition Width="50" />
    <ColumnDefinition Width="*" />
    <ColumnDefinition Width="30" />
   </Grid.ColumnDefinitions>

   <CheckBox VerticalAlignment="Center" Grid.Column="0" IsChecked="True" />
   <TextBlock VerticalAlignment="Center" Grid.Column="1" Margin="5,0,5,0" Text="Id" />
   <TextBlock VerticalAlignment="Center" Grid.Column="2" Margin="5,0,5,0" Text="Title" />
   <Button HorizontalAlignment="Right" Grid.Column="3" Margin="5,0,5,0">-&gt;</Button>
  </Grid>
  <!--</ListBox>-->
 </ItemsControl>
</ScrollViewer>

The ListBox has some property of killing width on it's children, just like a TreeView does. So if you can avoid using it, for just layout reasons, this may be your fastest options.

Otherwise you'll need to databind the width of you Grid to the width of the dummy object outside the ListView, but the messy party is dealing with the width change as the scrollbar appears/disappears.

Simeon Pilgrim
This is a great idea, but the ItemsControl does not have SelectionMode or a Selection changed event.
Vaccano
Thought you might want that.
Simeon Pilgrim
+1  A: 

Why do you need to wrap the ListBox in a ScrollViewer? The ListBox automatically displays the ScrollViewer when its contents don't fit in the available space. You can remove it from the XAML and it should still work okay. At least, it did for me. :)

I tried your ControlTemplate out and it seemed to work really well. The problem is that while the 3rd column resizes to the width of the ListBox, if the text in the 3rd column is very long, the 4th column won't appear until all of the text is visible.

I also tried another layout method, which I'm not sure about performance-wise, but the idea was to use a DockPanel instead of a Grid. I added the third column last in the XAML to make it fill the remaining space, and docked it to the left (I docked the 4th column to the right). It still would want to display the entire string in the 3rd column. I don't know why this is happening, and why the text won't wrap or use an ellipsis when the available space isn't enough for the text to display.

<Window.Resources>
    <Style TargetType="ListBoxItem">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="ListBoxItem">
                    <DockPanel Width="Auto">
                        <CheckBox Width="20" VerticalAlignment="Center" DockPanel.Dock="Left" IsChecked="{Binding IsSelected,
                                        RelativeSource={RelativeSource TemplatedParent},
                                        Mode=TwoWay}" />
                        <TextBlock Width="50" VerticalAlignment="Center" DockPanel.Dock="Left" Margin="5,0,5,0" Text="hi" />
                        <Button Width="30" HorizontalAlignment="Right" DockPanel.Dock="Right" Margin="5,0,5,0">-&gt;</Button>
                        <TextBlock VerticalAlignment="Center" DockPanel.Dock="Left" Margin="5,0,5,0" Text="afdjfksdhahjklfadshjlasffasdlhjsafdlhjasdfasfhdjlaslfhdjsfdhjlsfdlhjsfdhjsfdddsd" TextTrimming="CharacterEllipsis" TextWrapping="WrapWithOverflow" />
                    </DockPanel>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>
Dave
This still causes the same issue. I don't want the text to run off the screen. It needs to truncate.
Vaccano
A: 

I finally figure this out.

The containing list box needed to set the horizontal scroll bar to disabled like this:

<ListBox ScrollViewer.HorizontalScrollBarVisibility="Disabled"

Once I did that the grid sized perfectly.

One of these days I will figure out how all this "declarative stuff" works.

Vaccano
That fix is not related to "declarative". Xaml is declarative. Your problem was a flow problem, as your solution shows that you disabled horizontal flow behavior of the control.
Simeon Pilgrim
Not quite sure what you mean by this. Are you saying my fix is the wrong way? I am using the declarative XAML properties to get the solution I need. Either way, there is no other way to do this.
Vaccano