ListView column auto sizing
Asked Answered
V

9

15

Let's say I have the following ListView:

<ListView ScrollViewer.VerticalScrollBarVisibility="Auto">
  <ListView.View>
    <GridView>
      <GridViewColumn Header="Something" 
                      DisplayMemberBinding="{Binding Path=ShortText}" />
      <GridViewColumn Header="Description"
                      DisplayMemberBinding="{Binding Path=VeryLongTextWithCRs}" />
      <GridViewColumn Header="Something Else" 
                      DisplayMemberBinding="{Binding Path=AnotherShortText}" />
    </GridView>
  </ListView.View>
</ListView>

I'd like the short text columns to always fit in the screen, and the long text column to use the remaining space, word-wrapping if necessary.

Is that possible?

Vennieveno answered 14/5, 2010 at 14:38 Comment(0)
P
8

There is no easy way to do this with a GridListView since it doesn't support setting the width of a column to "*" (fill remaining space).

Here is a discussion of how you could fake it by using an IValueConverter to set the width of the column to TotalListWidth - SumOfColumnWidths

On the other hand, have you considered using a DataGrid instead? This will support the kind of layout you are looking for, though is a considerably heavier control. It is also only native in .NET 4 - though you can get an equivalent for 3.5 through the WPF Toolkit.

Phylloid answered 14/5, 2010 at 14:50 Comment(2)
Martin, the problem with the IValueConverter solution is that it requires all the other widths to be set (I'd rather not set any). At this point, I can't change the ListViews in this project, but I'll definitely using DataGrid for the next one. Thanks!Vennieveno
Avoid the datagrid if you can! It's a pile of crap and full of bugsRabbitry
D
7
<Grid Name="dummygrid" Visibility="Hidden">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="0.5*"></ColumnDefinition>
                <ColumnDefinition Width="0.2*"></ColumnDefinition>
                <ColumnDefinition Width="0.1*"></ColumnDefinition>
                <ColumnDefinition Width="0.2*"></ColumnDefinition>
                <ColumnDefinition Width="150"></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <Border Grid.Column="0" Name="dummywidth1"></Border>
            <Border Grid.Column="1" Name="dummywidth2"></Border>
            <Border Grid.Column="2" Name="dummywidth3"></Border>
            <Border Grid.Column="3" Name="dummywidth4"></Border>
            <Border Grid.Column="5" Name="dummywidth5"></Border>
        </Grid>
        <ListView  Name="Installer_LV" Grid.Row="1" ItemContainerStyle="{StaticResource LV_ItemStyle}" ScrollViewer.HorizontalScrollBarVisibility="Disabled"  AlternationCount="2">
            <ListView.View>
                <GridView ColumnHeaderContainerStyle="{StaticResource LV_HeaderStyle}">
                    <GridViewColumn  Width="{Binding ElementName=dummywidth1, Path=ActualWidth}"  DisplayMemberBinding="{Binding DisplayName}" >
                        <GridViewColumn.Header>
                            <GridViewColumnHeader Tag="DisplayName" Click="InstallerLV_HeaderClick">Name</GridViewColumnHeader>
                        </GridViewColumn.Header>
                    </GridViewColumn>
                    <GridViewColumn  Width="{Binding ElementName=dummywidth2, Path=ActualWidth}" DisplayMemberBinding="{Binding Publisher}">
                        <GridViewColumn.Header>
                            <GridViewColumnHeader Tag="Publisher" Click="InstallerLV_HeaderClick">Publisher</GridViewColumnHeader>
                        </GridViewColumn.Header>
                    </GridViewColumn>
                    <GridViewColumn  Width="{Binding ElementName=dummywidth3, Path=ActualWidth}" DisplayMemberBinding="{Binding Version}">
                        <GridViewColumn.Header>
                            <GridViewColumnHeader Tag="Version" Click="InstallerLV_HeaderClick">Version</GridViewColumnHeader>
                        </GridViewColumn.Header>
                    </GridViewColumn>
                    <GridViewColumn  Width="{Binding ElementName=dummywidth4, Path=ActualWidth}" DisplayMemberBinding="{Binding Size}">
                        <GridViewColumn.Header>
                            <GridViewColumnHeader Tag="Size" Click="InstallerLV_HeaderClick">Size</GridViewColumnHeader>
                        </GridViewColumn.Header>
                    </GridViewColumn>
                    <GridViewColumn Header="Action" Width="150">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <Button  Height="38" Width="130" Style="{DynamicResource RoundedButton}" Content="{Binding Status}" Tag="{Binding ModuleId}"  HorizontalAlignment="Center" VerticalAlignment="Center" Click="onActionClick"></Button>
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                </GridView>
            </ListView.View>
        </ListView>

In the above example i have used a dummy grid and split into 5 columns and using binding assign that size to "GridViewColum" by

Width="{Binding ElementName=dummywidth4, Path=ActualWidth}"

So that when the hidden dummy grid column size changes it will get reflect in gridview column size also.

Downfall answered 13/1, 2016 at 14:4 Comment(1)
This is insane ... I LOVE ITKafka
P
6

Set Width="Auto" on your GridViewColumns. However, due to virtualization you may encounter some problems with auto-sizing.

See this question.

So, long-story-short, if you want accurate auto-sizing of columns you'll need to recalculate your widths when the visible data changes, due to virtualization.

Plebiscite answered 14/5, 2010 at 14:45 Comment(1)
Isn't Auto the default? The problem is, it seems to create horizontal scrolling because it expands beyond the available space.Vennieveno
O
4

This works for me, toggling the Width to ActualWidth and then back to NaN for any columns that don't have widths explicitly set. This will only work if the listview columns do not contain controls. I usually call this after data in the list has changed.

Public Shared Sub AutoResizeListView(lst As Windows.Controls.ListView)
    Dim gv = DirectCast(lst.View, Windows.Controls.GridView)
    For Each gvc In gv.Columns
        If Double.IsNaN(gvc.Width) Then
            gvc.Width = gvc.ActualWidth
            gvc.Width = Double.NaN
        End If
    Next
End Sub
Oblivion answered 22/2, 2013 at 22:22 Comment(0)
C
1

First set the name in the column header like so:

<GridViewColumn Header="Description" Width="350" x:Name="lvhDescription"/>

And then on resize modify width.

Private Sub winMain_SizeChanged(sender As Object, e As SizeChangedEventArgs) Handles Me.SizeChanged


    If Me.IsLoaded = False Then Exit Sub

    lvhDescription.Width = e.NewSize.Width - 665


End Sub
Chrisoula answered 17/11, 2016 at 21:43 Comment(0)
S
0

I'd like to present an another approach in order to size each column with the largest element width. Do this following on each list view item with the help of a loop.

No need to change size after a re-size window event.

Leaves UnitWidth as the constant font width size.

You can also define a delegate to the SourceUpdated event.

GridView gv = (myListView.View as GridView);
if (titleLen < c.Title.Length)
{
    titleLen = c.Title.Length;
    gv.Columns[0].Width = titleLen * UnitWidth;
}
if (cssLen < c.CSSName.Length)
{
    cssLen = c.CSSName.Length;
    gv.Columns[1].Width = cssLen * UnitWidth;
}
if (valueLen < c.Value.Length)
{
    valueLen = c.Value.Length;
    gv.Columns[2].Width = valueLen * UnitWidth;
}
Sodamide answered 30/12, 2014 at 14:55 Comment(0)
D
0

Try this code instead,

  private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
    {
        double remainingSpace = mylistviewname.ActualWidth;

        if (remainingSpace > 0)
        {

            (mylistviewname.View as GridView).Columns[1].Width = Math.Ceiling(remainingSpace / 3);
            (mylistviewname.View as GridView).Columns[2].Width = Math.Ceiling(remainingSpace / 3);
            (mylistviewname.View as GridView).Columns[3].Width = Math.Ceiling(remainingSpace / 3);
        }
    }

Here i user SizeChanged event so when the windows size change this function is triggered and update the width of the listviewheader. I have 3 listviewheaders so divided by 3 if you have more than 3 divide by appropriate value.

Downfall answered 5/2, 2016 at 7:9 Comment(0)
T
0
<ListView ScrollViewer.VerticalScrollBarVisibility="Auto" Name="someList">
  <ListView.View>
    <GridView>
      <GridViewColumn Width={Binding ElementName=someList, Path=ActualWidth/3} Header="Something" DisplayMemberBinding="{Binding Path=ShortText}" />
      <GridViewColumn  Width={Binding ElementName=someList, Path=ActualWidth/3} Header="Description" DisplayMemberBinding="{Binding Path=VeryLongTextWithCRs}" />
      <GridViewColumn  Width={Binding ElementName=someList, Path=ActualWidth/3} Header="Something Else" DisplayMemberBinding="{Binding Path=AnotherShortText}" />
    </GridView>
  </ListView.View>
</ListView>
Therron answered 8/6, 2017 at 17:10 Comment(0)
F
0

With @joee solution, it produces an "ArgumentOutOfRangeException":

System.ArgumentOutOfRangeException: 'Index was out of range. Must be non-negative and less than the size of the collection. Arg_ParamName_Name'

Try this instead:

private void UserControl_SizeChanged(object sender, SizeChangedEventArgs e)
{
   double remainingSpace = SomeListViewName.ActualWidth;

   var gridView = (GridView)OrderListView.View;

   if (remainingSpace > 0)
   {
      foreach (var column in gridView.Columns)
      {
         column.Width = Math.Ceiling(remainingSpace / 12);
      }
   }
}

In my case, I have 12 columns. Hence diving by 12

Frankhouse answered 7/8 at 13:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.