.net - привязка - wpf listview добавление элемента




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

Вот решение, позволяющее нескольким спискам 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);
        }
    }

Я хочу создать 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.


Мне нужно было иметь все столбцы с одинаковой шириной. Вышеупомянутые решения прекрасны, но я предпочитаю обертывать такую ​​вещь в прикрепленном свойстве (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.


Похоже на это, рассматривая аналогичную проблему, моя проблема заключалась в том, что я хотел, чтобы все столбцы были «Авто» ожидающими первого, что просто заполнило бы лишнее пространство, поэтому я расширил решение 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>
...

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

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


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

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

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


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

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;

        }
    }

Я пытался добиться того же, но потом решил, что мне бы хотелось, чтобы мои столбцы 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;
}






listview