JChristian
JChristian

Reputation: 4170

How can I get a ListView GridViewColumn to fill the remaining space in my grid?

I want to create a ListView that has two columns with a fixed width and a third column to fill in the remaining space. So something like this:

<ListView>
    <ListView.View>
        <GridView>
            <GridViewColumn Header="Name" Width="*" />
            <GridViewColumn Header="Age" Width="50" />
            <GridViewColumn Header="Gender" Width="50" />
        </GridView>
    </ListView.View>
</ListView>

The problem is I can't find a way to get the Name column to fill in the remaining space, as setting the width to * doesn't work. It looks like there is a way to do this with a value converter, but it seems like there should be a simpler way. Like with a DataGrid control, you can specify the widths of columns with *s.

Upvotes: 41

Views: 57110

Answers (11)

VgrSoft
VgrSoft

Reputation: 1

Yet another MVVM solution. We can use MultiValueConverter for our column that should fill the remaining space. This method requires reflection to get DesiredWidth set of other columns.

<ListView>
    <ListView.Resources>
        <conv:GridViewColumnWidthConverter x:Key="GridViewColumnWidthConverter"/>
    </ListView.Resources>
    <ListView.View>
        <GridView>
            <GridViewColumn Header="Name">
                <GridViewColumn.Width>
                    <MultiBinding Converter="{StaticResource GridViewColumnWidthConverter}" ConverterParameter="*">
                        <Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type ListView}}" Path="ActualWidth" />
                        <Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type GridView}}" />
                        <Binding RelativeSource="{RelativeSource Self}"/>
                    </MultiBinding>
                </GridViewColumn.Width>
            </GridViewColumn>
            <GridViewColumn Header="Age" Width="50" />
            <GridViewColumn Header="Gender" Width="50" />
        </GridView>
    </ListView.View>
</ListView>

Converter:

public class GridViewColumnWidthConverter : IMultiValueConverter
{
    private static readonly System.Reflection.PropertyInfo s_piDesiredWidth;

    static GridViewColumnWidthConverter()
    {
        var pi = typeof(GridViewColumn).GetProperty("DesiredWidth",
            System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
        Debug.Assert(pi != null);
        s_piDesiredWidth = pi;
    }

    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (parameter is string param && param == "*" && values.Length==3 && values[0] is double actualWidth && actualWidth > 0d)
        {
            if (values[1] is GridView gridView && values[2] is GridViewColumn column && s_piDesiredWidth != null)
            {
                double w = 0d;
                foreach (var col in gridView.Columns)
                {
                    if (col == column)
                        continue;
                    w += col.ActualWidth > 0 ? col.ActualWidth : (double)s_piDesiredWidth.GetValue(col);
                }
                double desiredWidth = actualWidth - w;
                return desiredWidth > 100 ? desiredWidth - 25/*scrollbar width*/ : double.NaN;
            }
        }
        return double.NaN;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        return null;
    }
}

Upvotes: 0

Palle Due
Palle Due

Reputation: 6312

The solution from David Hanson-Greville's OnTheBlog mentioned in another answer isn't available anymore, even though the blog still exists. I was able to find it on the Wayback Machine and with a few moderations, here it is:

The trick is that you set Stretch=true on your ListView and it will stretch the columns that do not have a width equally.

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;

namespace Demo.Extension.Properties
{
    ///
    /// ListViewColumnStretch
    ///
    public class ListViewColumns : DependencyObject
    {
        ///
        /// IsStretched Dependency property which can be attached to gridview columns.
        ///
        public static readonly DependencyProperty StretchProperty =
            DependencyProperty.RegisterAttached("Stretch",
            typeof(bool),
            typeof(ListViewColumns),
            new UIPropertyMetadata(true, null, OnCoerceStretch));

        ///
        /// Gets the stretch.
        ///
        /// The obj.
        ///
        public static bool GetStretch(DependencyObject obj)
        {
            return (bool)obj.GetValue(StretchProperty);
        }

        ///
        /// Sets the stretch.
        ///
        /// The obj.
        /// if set to true [value].
        public static void SetStretch(DependencyObject obj, bool value)
        {
            obj.SetValue(StretchProperty, value);
        }

        ///
        /// Called when [coerce stretch].
        ///
        ///If this callback seems unfamilar then please read
        /// the great blog post by Paul Jackson found here.
        /// http://compilewith.net/2007/08/wpf-dependency-properties.html
        /// The source.
        /// The value.
        ///
        public static object OnCoerceStretch(DependencyObject source, object value)
        {
            ListView lv = (source as ListView);

            //Ensure we dont have an invalid dependancy object of type ListView.
            if (lv == null)
            {
                throw new ArgumentException("This property may only be used on ListViews");
            }

            //Setup our event handlers for this list view.
            lv.Loaded += new RoutedEventHandler(lv_Loaded);
            lv.SizeChanged += new SizeChangedEventHandler(lv_SizeChanged);
            return value;
        }

        ///
        /// Handles the SizeChanged event of the lv control.
        ///
        /// The source of the event.
        /// The instance containing the event data.
        private static void lv_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            ListView lv = (sender as ListView);
            if (lv.IsLoaded)
            {
                //Set our initial widths.
                SetColumnWidths(lv);
            }
        }

        ///
        /// Handles the Loaded event of the lv control.
        ///
        /// The source of the event.
        /// The instance containing the event data.
        private static void lv_Loaded(object sender, RoutedEventArgs e)
        {
            ListView lv = (sender as ListView);
            //Set our initial widths.
            SetColumnWidths(lv);
        }

        ///
        /// Sets the column widths.
        ///
        private static void SetColumnWidths(ListView listView)
        {
            //Pull the stretch columns fromt the tag property.
            List<GridViewColumn> columns = (listView.Tag as List<GridViewColumn>);
            double specifiedWidth = 0;
            GridView gridView = listView.View as GridView;
            if (gridView != null)
            {
                if (columns == null)
                {
                    //Instance if its our first run.
                    columns = new List<GridViewColumn>();
                    // Get all columns with no width having been set.
                    foreach (GridViewColumn column in gridView.Columns)
                    {
                        if (!(column.Width >= 0))
                        {
                            columns.Add(column);
                        }
                        else
                        {
                            specifiedWidth += column.ActualWidth;
                        }
                    }
                }
                else
                {
                    // Get all columns with no width having been set.
                    foreach (GridViewColumn column in gridView.Columns)
                    {
                        if (!columns.Contains(column))
                        {
                            specifiedWidth += column.ActualWidth;
                        }
                    }
                }

                // Allocate remaining space equally.
                foreach (GridViewColumn column in columns)
                {
                    double newWidth = (listView.ActualWidth - specifiedWidth) / columns.Count;
                    if (newWidth >= 10)
                    {
                        column.Width = newWidth - 10;
                    }
                }

                //Store the columns in the TAG property for later use.
                listView.Tag = columns;
            }
        }
    }
}

Then you just add the namespace to the XAML file:

xmlns:Extensions="clr-namespace:Demo.Extension.Properties"

and use it on your list view:

<ListView ItemsSource="{Binding Path=Items}" DisplayMemberPath="Name"
                          ScrollViewer.VerticalScrollBarVisibility="Auto"
                          Grid.Column="0" Margin="8" Extensions:ListViewColumns.Stretch="true">

Upvotes: 9

G&#225;bor
G&#225;bor

Reputation: 10274

Building on some other answers (eg. that of Timores), this is the one I normally use. Unlike those, this solution allows both relative and automatic sizes to co-exist:

  public class GridViewColumnRelativeSize {
    public static readonly DependencyProperty IsEnabledProperty = DependencyProperty.RegisterAttached("IsEnabled", typeof(bool), typeof(GridViewColumnRelativeSize), new FrameworkPropertyMetadata(IsEnabledPropertyChanged));
    public static bool GetIsEnabled(DependencyObject d) => (bool)d.GetValue(IsEnabledProperty);
    public static void SetIsEnabled(ListView d, bool value) => d.SetValue(IsEnabledProperty, value);

    public static void IsEnabledPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
      if (d is ListView list) {
        if (!KnownLists.Contains(list)) {
          KnownLists.Add(list);
          list.Unloaded += OnUnloaded;
          list.SizeChanged += OnSizeChanged;
        }
      }
      else
        throw new Exception("ListView expected");
    }

    public static readonly DependencyProperty RelativeWidthProperty = DependencyProperty.RegisterAttached("RelativeWidth", typeof(double), typeof(GridViewColumnRelativeSize), new FrameworkPropertyMetadata(double.NaN));
    public static double GetWidth(DependencyObject d) => (double)d.GetValue(RelativeWidthProperty);
    public static void SetWidth(GridViewColumn d, double value) => d.SetValue(RelativeWidthProperty, value);

    private static readonly List<FrameworkElement> KnownLists = new();

    private static void OnUnloaded(object sender, RoutedEventArgs e) {
      var element = (FrameworkElement)sender;
      _ = KnownLists.Remove(element);
      element.Unloaded -= OnUnloaded;
      element.SizeChanged -= OnSizeChanged;
    }

    private static void OnSizeChanged(object sender, SizeChangedEventArgs e) {
      if (sender is ListView listView) {
        bool isEnabled = listView.GetValue(IsEnabledProperty) is bool enabled && enabled;
        if (isEnabled && listView.View is GridView gridView) {
          double TotalUnits = gridView.Columns.Sum(column => {
            double width = (double)column.GetValue(RelativeWidthProperty);
            return double.IsNaN(width) ? 1 : width;
          });
          double ActualWidth = listView.ActualWidth - SystemParameters.VerticalScrollBarWidth;
          double UnitWidth = Math.Floor(ActualWidth / TotalUnits);
          foreach (var column in gridView.Columns) {
            double unit = (double)column.GetValue(RelativeWidthProperty);
            if (!double.IsNaN(unit))
              column.Width = unit * UnitWidth;
          }
        }
      }
    }

  }
<ListView ns:GridViewColumnRelativeSize.IsEnabled="True">
  <ListView.View>
    <GridView>
      <GridViewColumn />
      <GridViewColumn Width="50" />
      <GridViewColumn ns:GridViewColumnRelativeSize.Width="2" />
    </GridView>
  </ListView.View>

Columns with a specific Width obey the size, without a Width will be automatically sized, with a GridViewColumnRelativeSize.Width will be proportionally sized using the value as a factor.

Upvotes: 0

Andy Stagg
Andy Stagg

Reputation: 413

I realize this is an old question, but I found this post while trying to get star based solution for multiple columns in a ListView/GridView. So I figured I could help some future folks out, as it will answer this question as well.

I had initially implemented a WidthConverter but this has the obvious limitation that the last column won't "fill", and it was never guaranteed that the row would fit its space, but here it is for those who are curious:

public class WidthConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var actualWidth = (double)value;
            var desiredPercentage = SafeConvert(parameter);

            if (actualWidth == 0.0 || desiredPercentage == 0.0) return double.NaN;

            var result = actualWidth * (desiredPercentage / 100);

            return result;
        }

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

        private double SafeConvert(object pInput)
        {
            if (pInput == null) return default;

            if (double.TryParse(pInput.ToString(), out var result))
            {
                return result;
            }

            return default;
        }
    }
<GridViewColumn Header="My Header"
                DisplayMemberBinding="{Binding MyColumnData}"
                Width="{Binding Path=ActualWidth, ElementName=MyListView, Converter={StaticResource WidthConverter}, ConverterParameter='10.5'}" />

This works, but it's very reliant on the programmer to hard code parameter values and messy in the sense that the binding needs the ActualWidth property from the ListView element. Ultimately it's fairly limited to what it can actually do, especially considering most WPF folks are used to working with GridLength when it comes to star sizing.

I took various bits and pieces from the solutions posted here as well as elsewhere and developed an MVVM friendly solution based on Attached Properties and Behaviors.

I created an Attached Property using GridLength to leverage the existing absolute/auto/star logic matching XAML width patterns we're all used to.

public class ColumnAttachedProperties : DependencyObject
{
    public static readonly DependencyProperty GridLength_WidthProperty = DependencyProperty.RegisterAttached(
        name: "GridLength_Width",
        propertyType: typeof(GridLength),
        ownerType: typeof(ColumnAttachedProperties),
        defaultMetadata: new FrameworkPropertyMetadata(GridLength.Auto));

    public static GridLength GetGridLength_Width(DependencyObject dependencyObject) 
        => (GridLength)dependencyObject.GetValue(GridLength_WidthProperty);

    public static void SetGridLength_Width(DependencyObject dependencyObject, string value) 
        => dependencyObject.SetValue(GridLength_WidthProperty, value);
}

The Attached Behavior hooks to both the Loaded and SizeChanged events, and performs some shared logic to size the columns.

Essentially, on the first pass over the columns, it will tally the star values (but doesn't set a width for star columns yet), and then on the second pass targets the star columns, setting the width as a percentage of the remaining width available. I am sure this could be optimized in some way.

public static class ListViewStarSizingAttachedBehavior
{
    public static readonly DependencyProperty UseGridLength_WidthProperty = DependencyProperty.RegisterAttached(
        name: "UseGridLength_Width",
        propertyType: typeof(bool),
        ownerType: typeof(ListViewStarSizingAttachedBehavior),
        new UIPropertyMetadata(false, RegisterEventHandlers));

    public static bool GetUseGridLength_Width(DependencyObject dependencyObject) 
        => (bool)dependencyObject.GetValue(UseGridLength_WidthProperty);
    public static void SetUseGridLength_Width(DependencyObject dependencyObject, bool value) 
        => dependencyObject.SetValue(UseGridLength_WidthProperty, value);

    private static void RegisterEventHandlers(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is ListView listView)
        {
            if (e.NewValue is bool booleanValue && booleanValue == true)
            {
                listView.SizeChanged += ListView_SizeChanged;
                listView.Loaded += ListView_Loaded;
            }
            else
            {
                listView.SizeChanged -= ListView_SizeChanged;
                listView.Loaded -= ListView_Loaded;
            }
        }
    }

    private static void ListView_Loaded(object sender, RoutedEventArgs e)
    {
        CalculateGridColumnWidths(sender);
    }

    private static void ListView_SizeChanged(object sender, SizeChangedEventArgs e)
    {
        if (sender is ListView listView && !listView.IsLoaded) return;

        CalculateGridColumnWidths(sender);
    }

    private static void CalculateGridColumnWidths(object sender)
    {
        if (sender is ListView listView && listView.View is GridView gridView)
        {
            // the extra offset may need to be altered per your application.
            var scrollOffset = SystemParameters.VerticalScrollBarWidth - 7;

            var remainingWidth = listView.ActualWidth - scrollOffset;
            var starTotal = 0.0;
                
            foreach (var column in gridView.Columns)
            {
                var gridLength = ColumnAttachedProperties.GetGridLength_Width(column);

                if (gridLength.IsStar)
                {
                    // Get the cumlative star value while passing over the columns
                    // but don't set their width until absolute and auto have been set.
                    starTotal += gridLength.Value;
                    continue;
                }

                if (gridLength.IsAbsolute)
                {
                    column.Width = gridLength.Value;
                }
                else
                {
                    column.Width = double.NaN;
                }

                remainingWidth -= column.ActualWidth;
            }

            if (starTotal == 0.0) return;

            // now eval each star column
            foreach (var column in gridView.Columns)
            {
                var gridLength = ColumnAttachedProperties.GetGridLength_Width(column);

                if (!gridLength.IsStar) continue;

                var starPercent = (gridLength.Value / starTotal);
                column.Width = remainingWidth * starPercent;
            }
        }
    }
}

And finally to use it in XAML, the ListView needs implement the attached behavior, and each of the columns implement the attached property. However, if you leave the attached property off the column, it will default Auto as per the DependencyPropertys defaultMetadata.

<ListView local:ListViewStarSizingAttachedBehavior.UseGridLength_Width="True"
          ItemsSource="{Binding MyItems}" >
    <ListView.View>
        <GridView>
            <GridView.Columns>
                <!-- Absolute -->
                <GridViewColumn local:ColumnAttachedProperties.GridLength_Width="250"
                                Header="Column One"
                                DisplayMemberBinding="{Binding Item1}" />
                
                <!-- Star -->               
                <GridViewColumn local:ColumnAttachedProperties.GridLength_Width="2*"
                                Header="Column Two"
                                DisplayMemberBinding="{Binding Item2}" />
                
                <!-- Auto -->               
                <GridViewColumn local:ColumnAttachedProperties.GridLength_Width="Auto"
                                Header="Column Three"
                                DisplayMemberBinding="{Binding Item3}" />

                <!-- Star -->
                <GridViewColumn local:ColumnAttachedProperties.GridLength_Width="*"
                                Header="Column Four"
                                DisplayMemberBinding="{Binding Item4}" />
            </GridView.Columns>
        </GridView>
    </ListView.View>
</ListView>

Seems to work well so far, but I am sure there are some edge cases which will need to be considered. The XAML is a ton cleaner than the width converter. The overall result is more flexible than those already posted as well.

Upvotes: 3

Josh
Josh

Reputation: 2975

The issue is the column width of a GridViewColumn is double, rather than a GridLength object, and there is no conversion in place to handle the *. Not sure if this is an oversight by the WPF team or not. You would think it should be supported.

Aside from the converter, the only other way I've seen it done is here: http://www.ontheblog.net/CMS/Default.aspx?tabid=36&EntryID=37.

Both are additional work that should not be required. I have found other "weird" things with the ListView and GridView combo so I quit using them. If I need a datagrid I use the 3rd party one we license, if I need a complex ListBox style menu, I just use a templated ListBox.

Upvotes: 9

GONeale
GONeale

Reputation: 26484

I was trying to achieve the same thing but then decided I would like my ListView columns to consume a percentage of the ListView instead, the result of this is all columns consuming a portion of space and all space being consumed in the ListView. You could set this up to have whatever percentage you like on the last column to directly achieve your 'fill remaining space on last column' goal.

I find this method fairly robust and reliable (even on resize!) so thought I might share.

I have four columns in my ListView for this example. All you need is to register the SizeChanged event in your ListView with the below event handler:

private void ProductsListView_SizeChanged(object sender, SizeChangedEventArgs e)
{
    ListView listView = sender as ListView;
    GridView gView = listView.View as GridView;

    var workingWidth = listView.ActualWidth - SystemParameters.VerticalScrollBarWidth; // take into account vertical scrollbar
    var col1 = 0.50;
    var col2 = 0.20;
    var col3 = 0.15;
    var col4 = 0.15;

    gView.Columns[0].Width = workingWidth*col1;
    gView.Columns[1].Width = workingWidth*col2;
    gView.Columns[2].Width = workingWidth*col3;
    gView.Columns[3].Width = workingWidth*col4;
}

Upvotes: 55

Timores
Timores

Reputation: 14589

My need was to have all columns with the same width. The above solutions are fine, but I prefer to wrap such a thing in an attached property (MVVM, reusability, etc.). Here is my code, if it can help.

    public class StarSizeHelper {

    private static readonly List<FrameworkElement> s_knownElements = new List<FrameworkElement>();

    public static bool GetIsEnabled(DependencyObject d) {
        return (bool) d.GetValue(IsEnabledProperty);
    }

    public static void SetIsEnabled(ListView d, bool value) {
        d.SetValue(IsEnabledProperty, value);
    }

    public static readonly DependencyProperty IsEnabledProperty =
        DependencyProperty.RegisterAttached("IsEnabled", 
                                            typeof(bool), 
                                            typeof(StarSizeHelper),
                                            new FrameworkPropertyMetadata(IsEnabledChanged));

    public static void IsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {

        var ctl = d as ListView;
        if (ctl == null) {
            throw new Exception("IsEnabled attached property only works on a ListView type");
        }

        RememberElement(ctl);
    }

    private static void RememberElement(ListView ctl) {

        if (! s_knownElements.Contains(ctl)) {
            s_knownElements.Add(ctl);

            RegisterEvents(ctl);
        } 
        // nothing to do if elt is known
    }

    private static void OnUnloaded(object sender, RoutedEventArgs e) {

        FrameworkElement ctl = (FrameworkElement) sender;
        ForgetControl(ctl);
    }

    private static void ForgetControl(FrameworkElement fe) {

        s_knownElements.Remove(fe);
        UnregisterEvents(fe);
    }

    private static void RegisterEvents(FrameworkElement fe) {
        fe.Unloaded += OnUnloaded;
        fe.SizeChanged += OnSizeChanged;
    }

    private static void UnregisterEvents(FrameworkElement fe) {
        fe.Unloaded -= OnUnloaded;
        fe.SizeChanged -= OnSizeChanged;
    }

    private static void OnSizeChanged(object sender, SizeChangedEventArgs e) {

        ListView listView = sender as ListView;
        if (listView == null) {
            return; // should not happen
        }
        GridView gView = listView.View as GridView;
        if (gView == null) {
            return; // should not happen
        }

        var workingWidth = listView.ActualWidth - SystemParameters.VerticalScrollBarWidth -10; // take into account vertical scrollbar
        var colWidth = workingWidth / gView.Columns.Count;
        foreach (GridViewColumn column in gView.Columns) {
            column.Width = colWidth;
        }
    }
}

In order to use it:

<ListView ... StarSizeHelper.IsEnabled="true" ... />

(you still to fix the namespace declaration in the XAML, of course)

You can adapt your sizing needs in the OnSizeChanged method.

Upvotes: 2

Dave Oakley
Dave Oakley

Reputation: 736

I took the example above (which is great) and improved it slightly to prevent runtime exceptions on resize:

private void tpList_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            ListView listView = sender as ListView;
            GridView gView = listView.View as GridView;

            var workingWidth = listView.ActualWidth - (SystemParameters.VerticalScrollBarWidth + 20); // take into account vertical scrollbar
            var col1 = 0.50;
            var col2 = 0.50;

            var t1 = workingWidth * col1;
            var t2 = workingWidth * col2;
            gView.Columns[0].Width = t1 > 0 ? t1 : 1;
            gView.Columns[1].Width = t2 > 0 ? t2 : 1;

        }
    }

Upvotes: 1

jfm
jfm

Reputation: 1

Here is a solution that allows one to have multiple ListViews leverage a general "Resize" event handler.

    //Using dictionarys as trackers allows us to have multiple ListViews use the same code
    private Dictionary<string, double> _fixedWidthTracker = new Dictionary<string, double>();
    private Dictionary<string, List<GridViewColumn>> _varWidthColTracker = new Dictionary<string, List<GridViewColumn>>();
    private void ListView_SizeChanged(object sender, SizeChangedEventArgs e)
    {
        ListView lv = sender as ListView;
        if (lv != null)
        {
            //For validation during Debug
            VerifyName(lv);

            GridView gv = lv.View as GridView;
            if (gv != null)
            {
                if (!_varWidthColTracker.ContainsKey(lv.Name))
                {
                    _varWidthColTracker[lv.Name] = new List<GridViewColumn>();
                    _fixedWidthTracker[lv.Name] = 0;
                    foreach (GridViewColumn gvc in gv.Columns)
                    {
                        if (!double.IsNaN(gvc.Width)) _fixedWidthTracker[lv.Name] += gvc.Width; else _varWidthColTracker[lv.Name].Add(gvc);
                    }
                }
                double newWidthForColumns = e.NewSize.Width - SystemParameters.VerticalScrollBarWidth - _fixedWidthTracker[lv.Name];
                int columnsCount = gv.Columns.Count;
                int numberOfFixedWithColumns = columnsCount - _varWidthColTracker[lv.Name].Count;
                Double newColumnWidth = newWidthForColumns / (columnsCount - numberOfFixedWithColumns);

                foreach (GridViewColumn gvc in _varWidthColTracker[lv.Name])
                {
                    gvc.Width = newColumnWidth;
                }
            }
        }
    }

    /// <summary>
    /// Warns the developer if this object does not have
    /// a public property with the specified name. This 
    /// method does not exist in a Release build.
    /// </summary>
    [Conditional("DEBUG")]
    [DebuggerStepThrough]
    public void VerifyName(ListView listView)
    {
        if (String.IsNullOrEmpty(listView.Name))
        {
            string msg = "The Name attribute is required to be set on the ListView in order to Bind to this method";
            Debug.Fail(msg);
        }
    }

Upvotes: 0

CAD bloke
CAD bloke

Reputation: 8828

My problem was similar but I wanted to fix the width of the first column and I also didn't want it to break if I added or removed columns, even at runtime. Thanks @Gary for the tip at https://stackoverflow.com/a/14674830/492

private void ResultsListView_SizeChanged(object sender, SizeChangedEventArgs e)
    {
      double newWidthForColumnsExceptFirstColumn = ResultsListView.ActualWidth - SystemParameters.VerticalScrollBarWidth - ResultsGridView.Columns[0].Width;
      int columnsCount = ResultsGridView.Columns.Count;
      Double newColumnWidth = newWidthForColumnsExceptFirstColumn / (columnsCount -1);

      for ( int col = 1; col < columnsCount; col++ ) // skip column [0]
      {
        ResultsGridView.Columns[col].Width = newColumnWidth;
      }
    }

Upvotes: 0

Gary Connell
Gary Connell

Reputation: 305

Came across this when looking into a similar problem, my issue was I wanted all columns to be 'Auto' expect the first, which would just fill in the extra space, so I expanded on GONeale's solution.

private void ListView_SizeChanged(object sender, SizeChangedEventArgs e)
{
    ListView _ListView = sender as ListView;
    GridView _GridView = _ListView.View as GridView;
    var _ActualWidth = _ListView.ActualWidth - SystemParameters.VerticalScrollBarWidth;
    for (Int32 i = 1; i < _GridView.Columns.Count; i++)
    {
        _ActualWidth = _ActualWidth - _GridView.Columns[i].ActualWidth;
    }
    _GridView.Columns[0].Width = _ActualWidth;
}

Then the XAML is simply:

...
<ListView.View>
    <GridView>
        <GridViewColumn Header="Title" />
        <GridViewColumn Header="Artist" Width="Auto" />
        <GridViewColumn Header="Album" Width="Auto" />
        <GridViewColumn Header="Genre" Width="Auto" />
    </GridView>
</ListView.View>
...

This code could also be used on more generically as number of columns isn't hard-coded and with a little tweaking you could probably make the 'fill column' definable through some sort of logic.

Hope it helps someone :)

Upvotes: 18

Related Questions