Связывание данных с частью коллекции
У меня есть статическая коллекция предметов (скажем, от 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>