Подтвердить что ты не робот

Как я могу получить ListView GridViewColumn для заполнения оставшегося пространства в моей сетке?

Я хочу создать ListView с двумя столбцами с фиксированной шириной и третьим столбцом, чтобы заполнить оставшееся пространство. Так что-то вроде этого:

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

Проблема заключается в том, что я не могу найти способ получить столбец Name, чтобы заполнить оставшееся пространство, так как установка ширины на * не работает. Похоже, есть способ сделать это с помощью преобразователя значений, но похоже, что должен быть более простой способ. Как и с элементом управления DataGrid, вы можете указать ширину столбцов с * s.

4b9b3361

Ответ 1

Проблема заключается в том, что ширина столбца GridViewColumn является двойной, а не объектом GridLength, и нет никакого преобразования для обработки *. Не уверен, что это надзор со стороны команды WPF или нет. Вы думаете, что это должно быть поддержано.

Помимо конвертера, единственный другой способ, которым я видел это, - это здесь: http://www.ontheblog.net/CMS/Default.aspx?tabid=36&EntryID=37.

Оба являются дополнительной работой, которая не должна требоваться. Я обнаружил другие "странные" вещи с помощью комбинированного списка ListView и GridView, поэтому я перестал их использовать. Если мне нужна сетка данных, я использую стороннюю лицензию, если мне нужно сложное меню стиля ListBox, я просто использую шаблонную ListBox.

Ответ 2

Я пытался добиться того же самого, но потом решил, что мне бы хотелось, чтобы мои столбцы ListView потребляли процент ListView вместо этого, результатом этого являются все столбцы, потребляющие часть пространства и все пространство, потребляемое в ListView. Вы можете установить, чтобы у вас был любой процент, который вам нравится в последнем столбце, чтобы достичь цели "заполнить оставшееся пространство на последнем столбце".

Я считаю этот метод довольно надежным и надежным (даже при изменении размера!), поэтому я мог бы поделиться им.

У меня есть четыре столбца в моем ListView для этого примера. Все, что вам нужно, - зарегистрировать событие SizeChanged в ListView с помощью обработчика событий ниже:

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;
}

Ответ 3

При просмотре подобной проблемы возникла проблема: я хотел, чтобы все столбцы были "Авто" ожидающими первого, что просто заполнило бы лишнее пространство, поэтому я расширил решение GONeale.

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;
}

Тогда XAML просто:

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

Этот код также можно использовать в более общем виде, поскольку количество столбцов не жестко закодировано и с небольшой настройкой вы, вероятно, можете сделать "заполняющий столбец" определяемым с помощью какой-то логики.

Надеюсь, это поможет кому-то:)

Ответ 4

Мне нужно было иметь все столбцы с одинаковой шириной. Вышеупомянутые решения прекрасны, но я предпочитаю обертывать такую ​​вещь в прикрепленном свойстве (MVVM, повторное использование и т.д.). Вот мой код, если он может помочь.

    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;
        }
    }
}

Чтобы использовать его:

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

(вы все равно исправляете декларацию пространства имен в XAML, конечно)

Вы можете адаптировать свои требования к размеру в методе OnSizeChanged.

Ответ 5

Решение от David Hanson-Greville OnTheBlog, упомянутое в одном из первых ответов, больше не доступно, хотя блог все еще существует, Я смог найти его на Wayback Machine и с несколькими версиями, вот он:

Фокус в том, что вы устанавливаете Stretch = true в ListView и растягиваете столбцы, которые не имеют ширины одинаково.

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

namespace Demo.Extension.Properties
{
    ///
    /// ListViewColumnStretch
    ///
    public class ListViewColumns : DependencyObject
    {
        ///
        /// IsStretched Dependancy 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;
            }
        }
    }
}

Вы просто добавляете пространство имен в файл XAML

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

и использовать его в своем списке:

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

Ответ 6

Моя проблема была похожа, но я хотел исправить ширину первого столбца, и я также не хотел, чтобы он прерывался, если я добавлял или удалял столбцы даже во время выполнения. Спасибо @Gary за отзыв в fooobar.com/questions/169433/...

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;
      }
    }

Ответ 7

Вот решение, позволяющее нескольким ListViews использовать общий обработчик событий "Resize".

    //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);
        }
    }

Ответ 8

Я взял вышеприведенный пример (это здорово) и немного улучшил его, чтобы исключить исключения во время выполнения при изменении размера:

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;

        }
    }