Связывание данных с частью коллекции

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

Мой вопрос заключается в том, как связать второй ComboBox с подмножеством этих элементов. Мне нужно, чтобы второй ComboBox был привязан к подмножеству элементов, оставшихся после выбора первого ComboBox. Например, первый ComboBox будет показывать 1,2,3...,100. Если число 43 выбрано в первом ComboBox, то второй ComboBox должен показывать 44,45,...,100.

Как это может быть достигнуто и иметь второе обновление ComboBox, если первое изменено без большого количества кода?

5 ответов

Решение

Я бы сделал это с использованием шаблона MVVM. Создайте класс, который реализует INotifyChange и выставьте три свойства.

  • ICollection FullCollection
  • int FirstIndex
  • ICollection PartialCollection

Используйте этот класс в качестве DataContext для своего элемента управления и привяжите SelectedIndex первого поля со списком к свойству FirstIndex, ItemSource первого поля со списком к FullCollection и ItemSource из второй коллекции к PartialCollection (убедитесь, что режим привязки SelectedIndex является двухсторонним). Затем в наборе свойства FirstIndex установите свойство PartialCollection как хотите. Помните, что вы должны использовать NotifyPropertyChange для метода set каждого свойства. Надеюсь, это поможет.

Я бы пошел с дизайном MVVM, но если вы хотите просто провести тестирование и хотите увидеть быстрые результаты, не вдаваясь в шаблоны проектирования, то я бы предложил использовать что-то вроде среды CLINQ / BLINQ / Obtics, чтобы усилить мощь LINQ при сохранении результатов. жить для поля со списком.

Так как LoSciamano уже опубликовал ответ на это (пока я писал это!), Я не буду вдаваться в детали реализации MVVM

Я бы выставил первую коллекцию как ObservableCollection<T> а вторая коллекция как голая IEnumerable, Затем вы можете использовать представление коллекции по умолчанию для обработки событий, необходимых для повторной фильтрации вложенной коллекции.

class FilteredVM : ViewModelBase
{
    public ObservableCollection<MyVM> Items { get; private set; }
    public IEnumerable<MyVM> SubItems { get; private set; }

    public FilteredVM()
    {
        this.Items = new ObservableCollection<MyVM>();
        this.SubItems = Enumerable.Empty<MyVM>();

        var view = CollectionViewSource.GetDefaultView(this.Items);
        view.CurrentChanged += (sender, e) => { SetupFilter();  };
        view.CollectionChanged += (sender, e) => { SetupFilter(); };
    }

    private void SetupFilter()
    {
        var view = CollectionViewSource.GetDefaultView(this.Items);
        var current = view.CurrentItem;
        if (current != null)
        {
            this.SubItems = this.Items.Where((vm,idx) => idx > view.CurrentPosition);
        }
        else
        {
            this.SubItems = Enumerable.Empty<MyVM>();
        }

        this.OnPropertyChanged("SubItems");
    }
}

В качестве альтернативы, если вы хотите сохранить CollectionViewSource из вашей виртуальной машины:

class FilteredVM : ViewModelBase
{
    private MyVM selectedItem;
    public MyVM SelectedItem
    {
        get { return this.selectedItem; }
        set
        {
            if (value != this.selectedItem)
            {
                this.selectedItem = value;
                this.OnPropertyChanged("SelectedItem");
                this.SetupFilter();
            }
        }
    }

    public ObservableCollection<MyVM> Items { get; private set; }
    public IEnumerable<MyVM> SubItems { get; private set; }

    public FilteredVM()
    {
        this.Items = new ObservableCollection<MyVM>();
        this.SubItems = Enumerable.Empty<MyVM>();

        this.Items.CollectionChanged += (sender, e) => { this.SetupFilter(); };
    }

    private void SetupFilter()
    {
        if (this.SelectedItem != null)
        {
            var item = this.SelectedItem; // save for closure
            this.SubItems = this.Items.SkipWhile(vm => vm != item).Skip(1);
        }
        else
        {
            this.SubItems = Enumerable.Empty<MyVM>();
        }

        this.OnPropertyChanged("SubItems");
    }
}

Имейте в виду, это потребует SelectedItem быть правильно привязанным в представлении к ViewModel. Первый из перечисленных подходов позволяет SelectedItem быть привязанным ни к чему (или нигде).

Вы можете использовать мою библиотеку ObservableComputations:

ObservableCollection<Item> itemsSubset = ItemsObservableCollection
    .Skiping(fromIndex)
    .Taking(itemsInSubsetCount);

itemsSubset отражает все изменения в ItemsObservableCollection.

Имейте 2 наблюдаемых коллекции, чтобы при выборе элемента в 1-м поле он запускал метод, который очищает и повторно заполняет 2-ю коллекцию. Поскольку это observableCollection, оно автоматически отражается в графическом интерфейсе WPF.

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

Код модели представления:

public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();
        DataContext = new ViewModel();
    }
}


public class ViewModel : INotifyPropertyChanged
{
    public ViewModel()
    {
        Initialsource = new ObservableCollection<int>();
        for (int i = 0; i < 101; i++)
        {
            Initialsource.Add(i);
        }
    }

    private int _selectedsourceItem;
    public int SelectedsourceItem
    {
        get { return _selectedsourceItem; }
        set
        {
            _selectedsourceItem = value;
            SubsetSource = new ObservableCollection<int>(Initialsource.Where(p => p > _selectedsourceItem));
            InvokePropertyChanged(new PropertyChangedEventArgs("SubsetSource"));
            InvokePropertyChanged(new PropertyChangedEventArgs("SelectedsourceItem"));
        }
    }

    private ObservableCollection<int> _initialsource;
    public ObservableCollection<int> Initialsource
    {
        get { return _initialsource; }
        set
        {
            _initialsource = value;
            InvokePropertyChanged(new PropertyChangedEventArgs("Initialsource"));
        }
    }

    private ObservableCollection<int> _subsetSource;
    public ObservableCollection<int> SubsetSource
    {
        get { return _subsetSource ?? (_subsetSource = new ObservableCollection<int>()); }
        set
        {
            _subsetSource = value;
            InvokePropertyChanged(new PropertyChangedEventArgs("SubsetSource"));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public void InvokePropertyChanged(PropertyChangedEventArgs e)
    {

        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, e);
    }
}

XAML:

<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
    <StackPanel Orientation="Horizontal">
        <ComboBox Width="100" ItemsSource="{Binding Initialsource}" SelectedItem="{Binding SelectedsourceItem, Mode=TwoWay}"></ComboBox>
        <ComboBox Width="100" ItemsSource="{Binding SubsetSource}"></ComboBox>
    </StackPanel>
</Grid>
Другие вопросы по тегам