简体   繁体   中英

How to resize a certain control based on Window size in WPF?

I have a ListView control where I want to resize the last column in sync with the size of the Window . So if the Window 's width increases by 100 units, I want the columns' width to also increase by 100.

Should I be using the Resize event on the Window and use a magic number to resize the column header manually, sort of like?:

columnHeader.Width = windowSize.X - 400;

Here is a solution that uses databinding and some converter magic to get the job done.

First, let's describe a simple ListView with some data and 3 columns.

<ListView>
    <ListView.View>
        <GridView>
            <GridViewColumn Width="140" Header="Date" />
            <GridViewColumn Width="140" Header="Day" 
                            DisplayMemberBinding="{Binding DayOfWeek}" />
            <GridViewColumn Width="140" Header="Year" 
                            DisplayMemberBinding="{Binding Year}"/>
        </GridView>
    </ListView.View>

    <sys:DateTime>1/2/3</sys:DateTime>
    <sys:DateTime>4/5/6</sys:DateTime>
    <sys:DateTime>7/8/9</sys:DateTime>
</ListView>

This will get us to where you are. Now, in order to have the last column grow and shrink based on the parents width, we need to do build a converter and hook it up. First, let's adjust the last column of the GridView to make the width dynamic.

<GridViewColumn Header="Year" DisplayMemberBinding="{Binding Year}">
    <GridViewColumn.Width>
        <MultiBinding Converter="{StaticResource lastColumnMaximizerConverter}">
            <Binding Path="ActualWidth" 
                     RelativeSource="{RelativeSource AncestorType=ListView}"/>
            <Binding Path="View.Columns" 
                     RelativeSource="{RelativeSource AncestorType=ListView}"/>
        </MultiBinding>
    </GridViewColumn.Width>
</GridViewColumn>

What we've done here is created a MultiBinding object, hooked up an IMultiValueConverter , and described several parameters that we want to send into the IMultiValueConverter implementation. The first parameter is the ActualWidth of the parent ListView. The second parameter is the View.Columns collection on the parent ListView. We now have everything we need to calculate the final width of the last column in our view.

Now we need to create an IMultiValueConverter implementation. I happen to have one right here.

public class WidthCalculationMultiConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, 
                          object parameter, CultureInfo culture)
    {
        // do some sort of calculation
        double totalWindowWidth;
        double otherColumnsTotalWidth = 0;
        double.TryParse(values[0].ToString(), out totalWindowWidth);
        var arrayOfColumns = values[1] as IList<GridViewColumn>;

        for (int i = 0; i < arrayOfColumns.Count - 1; i++)
        {
            otherColumnsTotalWidth += arrayOfColumns[i].Width;
        }

        return (totalWindowWidth - otherColumnsTotalWidth) < 0 ? 
                     0 : (totalWindowWidth - otherColumnsTotalWidth);
    }

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

Psst.. by the way, this is not the safest code. You'll want to spruce it up! It's just a demo and you know how demo code is! :)

Last of all, we need to instantiate our Converter instance in our XAML.

<Grid.Resources>
    <Converters:WidthCalculationMultiConverter 
                  x:Key="lastColumnMaximizerConverter"/>
</Grid.Resources>

So now we have aa converter that will figure out how wide we want to make the last column, based on the widths of the columns (not including the last one) in the ListView and a binding that will use that converter and send in the required parameters and get the "right answer" out and apply it to the last column's width.

If you've put this together right, you should now have a ListView in which the last column will always stretch to the maximum width of the parent ListView.

I hope that this gets you going but also helped you understand how we can do this without writing some code-behind and using more of the facilities provided by WPF.

I can't take full credit for this answer, because I got it here

But basically this is the idea:

Replace the "View" in the listView with a GridView. Then, you can specify whatever width you want for the first columns. In this case 100, 50, 50. Then we add a binding on the final column, that takes as input the parent ListView control. But we are allowing a converter to do the dirty work for us.

<ListView>
    <ListView.View>
      <GridView>
        <GridViewColumn Header="Title" DisplayMemberBinding="{Binding}" Width="100"/>
        <GridViewColumn Header="Title" DisplayMemberBinding="{Binding}" Width="50"/>
        <GridViewColumn Header="Title" DisplayMemberBinding="{Binding}" Width="50"/>
        <GridViewColumn Header="4" DisplayMemberBinding="{Binding}">
        <GridViewColumn.Width>
          <MultiBinding Converter="{StaticResource starWidthConverter}">
            <Binding Path="ActualWidth"  RelativeSource="{RelativeSource AncestorType=ListView}"/>
            <Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=ListView}"/>
          </MultiBinding>
        </GridViewColumn.Width>
      </GridViewColumn>
     </GridView>
    </ListView.View>
    <ListViewItem>item1</ListViewItem>
    <ListViewItem>item2</ListViewItem>
    <ListViewItem>item3</ListViewItem>
    <ListViewItem>item4</ListViewItem>
  </ListView>

So, in the converter 'Convert' function we do this:

ListView listview = value[1] as ListView;
double width = listview.ActualWidth;
GridView gv = listview.View as GridView;
for(int i = 0;i < gv.Columns.Count-1;i++)
{
  if(!Double.IsNaN(gv.Columns[i].Width))
    width -= gv.Columns[i].Width;
}
return width - 5;// this is to take care of margin/padding

Which grabs the listview width, and calculates the new size.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM