数据绑定到集合的一部分

时间:2011-06-07 12:53:05

标签: wpf data-binding

我有一个静态的项目集合(比如1到100之间的数字),我在ComboBox中展示。我可以将这些项目绑定到ComboBox没问题。

我的问题是如何将第二个ComboBox绑定到这些项的子集。我想要的行为是让第二个ComboBox绑定到选择第一个ComboBox后剩余的项目子集。例如,第一个ComboBox将显示1,2,3 ...,100。如果在第一个ComboBox中选择了数字43,那么第二个ComboBox应该显示44,45,...,100。

如果在没有大量代码隐藏的情况下更改第一个ComboBox,如何实现并更新第二个ComboBox?

6 个答案:

答案 0 :(得分:3)

我会使用MVVM模式执行此操作。 创建一个实现INotifyChange的类并公开三个属性。

  • ICollection FullCollection
  • int FirstIndex
  • ICollection PartialCollection

将此类用作Control的DataContext,并将第一个组合框的SelectedIndex绑定到FirstIndex属性,将第一个组合框的ItemSource绑定到FullCollection,将第二个集合的ItemSource绑定到PartialCollection(确保SelectedIndex绑定模式为双向)。 然后在FirstIndex属性集上设置PartialCollection属性。 请记住,您必须在每个属性的set方法上使用NotifyPropertyChange。 希望这有帮助。

答案 1 :(得分:2)

我会使用MVVM设计,但如果您只想进行测试并希望在不进入设计模式的情况下快速查看结果,那么我建议使用类似CLINQ / BLINQ / {的内容{3}}框架,以保持LINQ的强大功能,同时保持组合框的结果。

由于LoSciamano已经发布了回复(当我发布这个时!)我不会深入了解MVVM实现的细节

答案 2 :(得分:1)

我会将第一个集合公开为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保留在VM之外:

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

请记住,这需要{View}才能将SelectedItem正确绑定到ViewModel中。列出的第一种方法允许SelectedItem绑定到任何地方(或无处)。

答案 3 :(得分:0)

有2个Observable集合,这样当第一个框中的项目被选中时,它将启动清除并重新填充第二个集合的方法。因为它是observableCollection,它会自动反映在WPF GUI中

答案 4 :(得分:0)

这是一个具体的例子(像往常一样,可能会有所改进,但想法就在这里):

视图模型背后的代码:

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>

答案 5 :(得分:0)

您可以使用我的ObservableComputations库:

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

itemsSubset反映了ItemsObservableCollection中的所有更改。