首页 > 解决方案 > 如何将 WPF ItemsControl 源绑定到列表段

问题描述

我有一个ItemsControl绑定到元素列表的。我怎样才能让它只显示该列表的一部分,例如,基于给定的Offsetand Count

显然我可以将一个IEnumerable属性声明为MyList.Skip(Offset).Take(Count),在绑定中使用它并通知PropertyChanged每次Offset或被Count修改。这很好用,除了每次ItemsControl都会为其所有项目重新创建视图,即使对于那些不是新的项目也是如此。

我想一个更惯用的方法是有一个CollectionView但它似乎不支持当前段的概念。我尝试使用基于索引的过滤器,但除了感觉不对之外,它还存在与以前相同的性能问题。

所以到目前为止我看到的唯一方法是ObservableCollection使用正确的列表元素维护 an 并在绑定中使用它,但不应该有更简单的方法吗?

这是我的测试程序(我知道代码MyObservableCollection在一般情况下不起作用)

  1. MainWindow.xaml
<Window x:Class="ItemsControlSegmentView.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:ItemsControlSegmentView"
        x:Name="This"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <local:MainWindowViewModel/>
    </Window.DataContext>
    <Window.Resources>
        <Style TargetType="ItemsControl">
            <Setter Property="ItemTemplate">
                <Setter.Value>
                    <DataTemplate>
                        <TextBlock Text="{Binding}" Background="{Binding DataContext.CurrentColor, ElementName=This, Mode=OneTime}" />
                    </DataTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <StackPanel>
        <StackPanel Orientation="Horizontal">
            <ItemsControl ItemsSource="{Binding MyEnumerable}"/>
            <ItemsControl ItemsSource="{Binding MyCollectionView}"/>
            <ItemsControl ItemsSource="{Binding MyObservableCollection}"/>
        </StackPanel>
        <Slider Maximum="{Binding MyList.Count}" Value="{Binding Offset}"/>
        <Slider Maximum="{Binding MaxCount}" Value="{Binding Count}"/>
    </StackPanel>
</Window>

  1. MainWindowViewModel
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Windows.Data;
using JetBrains.Annotations;

namespace ItemsControlSegmentView
{
    public class MainWindowViewModel : INotifyPropertyChanged
    {
        private int offset;
        private int count = 7;
        private readonly Random random = new Random();

        public string CurrentColor { get; private set; } = "White";

        public int Offset
        {
            get { return offset; }
            set
            {
                if (offset == value) return;
                var atEnd = value > offset;
                offset = value;
                UpdateAllViews(atEnd);
                OnPropertyChanged(nameof(MaxCount));
                OnPropertyChanged();
            }
        }

        public int Count
        {
            get { return count; }
            set
            {
                if (count == value) return;
                count = value;
                UpdateAllViews(true);
                OnPropertyChanged();
            }
        }

        public int MaxCount => MyList.Count - Offset;

        public List<string> MyList { get; } = new List<string>(Enum.GetNames(typeof(DayOfWeek)));

        private void UpdateAllViews(bool atEnd)
        {
            CurrentColor = $"#{random.Next():X8}";
            UpdateMyEnumerable();
            UpdateMyCollectionView();
            UpdateMyObservableCollection(atEnd);
        }

        public IEnumerable<string> MyEnumerable => MyList.Skip(Offset).Take(Count);
        private void UpdateMyEnumerable() => OnPropertyChanged(nameof(MyEnumerable));

        public ListCollectionView MyCollectionView { get; }
        private void UpdateMyCollectionView() => MyCollectionView.Filter = entry =>
        {
            var indexOf = MyList.IndexOf((string)entry);
            return indexOf >= Offset && indexOf < Offset + Count;
        };

        public ObservableCollection<string> MyObservableCollection { get; }
        private void UpdateMyObservableCollection(bool atEnd)
        {
            foreach (var s in MyList.Except(MyEnumerable))
                MyObservableCollection.Remove(s);
            var i = 0;
            foreach (var s in MyEnumerable.Except(MyObservableCollection))
                if (atEnd) MyObservableCollection.Add(s);
                else MyObservableCollection.Insert(i++, s);
        }

        public MainWindowViewModel()
        {
            MyCollectionView = new ListCollectionView(MyList);
            MyObservableCollection = new ObservableCollection<string>(MyList);
        }


        public event PropertyChangedEventHandler PropertyChanged;

        [NotifyPropertyChangedInvocator]
        private void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

这是将偏移滑块逐步滚动到右端和后退后窗口的外观。只有第三个ItemsControl保留其现有条目:

截屏

标签: c#wpf

解决方案


推荐阅读