将ComboBox绑定到ObservableCollection的部分内容

4

我有一个使用C#编写的WPF应用程序,在其中我有一个类MyCollection的对象,它扩展了ObservableCollection<MyType>,其目的是为了将项目绑定到多个ComboBoxes上。

然而,每个ComboBox都必须显示此集合的子集(基于其元素的某个属性),并且这可能会根据用户输入而改变。

在保持每个子集更新具有原始集合数据的情况下,我如何获得此行为?这种情况是否有一些公认的设计模式?


编辑:由于我的问题表述很容易被误解,以下是一个示例。 我有一个ObservableCollection<Person>对象,其中类Person具有AgeName属性。 我有三个组合框,前两个必须显示具有奇数AgePerson对象的Name,而第三个则必须显示具有偶数AgePerson对象的Name。他们的角色可能会在运行时更改(例如,第一个和最后一个必须显示奇数年龄,第二个必须显示偶数年龄) 如果从集合中添加或删除Person对象,则必须在相应的ComboBoxes上反映更改。 NameAge属性可以视为常数。


实现 INotifyPropertyChanged。 - failedprogramming
1
我不明白,你能具体一些吗? ObservableCollection<T> 已经实现了 INotifyPropertyChanged 接口,但这并不能解决问题,因为其元素的属性没有改变。 我应该在哪里实现它?怎么实现? - user3313590
T对象需要实现INPC接口,否则你需要手动连接项目更改事件,否则当T对象的属性发生更改时,它不会报告更改。例如,你可以将一个ObservableCollection<Person>绑定到你的组合框中,但是你看不到对集合中每个人所做的更改。 - failedprogramming
我认为这不是OP所问的。如果我理解正确,问题是,对于一个包含“1, 2, 3”的列表,如何让一个组合框显示“1”,另一个显示“1, 2”,第三个显示“1, 3”,并且这些可以根据用户输入而改变。内部的对象永远不会改变,因此这不是NotifyPropertyChanged问题。 - BradleyDotNET
你说的没错,但好像没有解决我的问题。然而,现在我读了一遍我的问题,发现我描述得有点不准确: 我关心的不是集合元素属性的变化,而是子集的变化。 当一个元素被添加/删除到主集合中时,我希望它们能够被添加/删除到显示相应子集的每个ComboBox中。编辑:正如@LordTakkera所说,我应该添加一些细节到问题中,以便更好地理解。 - user3313590
@Pharanoise 请查看我的答案,它完全符合您所描述的要求,甚至会显示集合中新增或删除的项目。 - grizzly
1个回答

2
如果我理解您的问题正确,您需要一种过滤机制。请看一下ICollectionView接口及其实现,例如CollectionViewSource,这可能会帮助您实现此目标。您需要处理实现过滤逻辑的Filter事件。以下是MSDN上的该类(http://msdn.microsoft.com/en-us/library/system.windows.data.collectionviewsource(v=vs.110).aspx)。一个例子:容器类:
public string Name { get; set; }
public string Capital { get; set; }

public Country(string name, string capital) {
    this.Name = name;
    this.Capital = capital;
}

模型类:

private ObservableCollection<Country> _countries;
private ICollectionView _european;
private ICollectionView _american;

public ObservableCollection<Country> Countries {
    get {
        if (_countries == null) {
            _countries = new ObservableCollection<Country>();
        }

        return _countries;
    }
}

public ICollectionView European {
    get {
        if (_european == null) {
            _european = new CollectionViewSource {
                Source = this.Countries
            }.View;
            _european.Filter += (e) => {
                Country c = e as Country;
                if (c.Name == "UK" || c.Name == "Ireland" || c.Name == "France") {
                    return true;
                }

                return false;
            };
        }

        return _european;
    }
}

public ICollectionView American {
    get {
        if (_american == null) {
            _american = new CollectionViewSource {
                Source = this.Countries
            }.View;
            _american.Filter += (e) => {
                Country c = e as Country;
                if (c.Name == "USA" || c.Name == "Canada" || c.Name == "Mexico") {
                    return true;
                }

                return false;
            };
        }

        return _american;
    }
}

初始化代码:

private Model _model;

public Model Model {
    get {
        if (_model == null) {
            _model = new Model();
        }

        return _model;
    }
}

public MainWindow() {
    InitializeComponent();
    this.DataContext = this.Model;
    this.Model.Countries.Add(new Country("UK", "London"));
    this.Model.Countries.Add(new Country("Ireland", "Dublin"));
    this.Model.Countries.Add(new Country("France", "Paris"));
    this.Model.Countries.Add(new Country("USA", "Washington D. C."));
    this.Model.Countries.Add(new Country("Mexico", "Mexico City"));
    this.Model.Countries.Add(new Country("Canada", "Ottawa"));
}

XAML:

<StackPanel>
    <ComboBox
        ItemsSource='{Binding Path=European}'>
        <ComboBox.ItemTemplate>
            <DataTemplate>
                <StackPanel
                    Orientation='Horizontal'>
                    <TextBlock
                        Text='{Binding Path=Name}' />
                    <TextBlock
                        Text=', ' />
                    <TextBlock
                        Text='{Binding Path=Capital}' />
                </StackPanel>
            </DataTemplate>
        </ComboBox.ItemTemplate>
    </ComboBox>

    <ComboBox
        ItemsSource='{Binding Path=American}'>
        <ComboBox.ItemTemplate>
            <DataTemplate>
                <StackPanel
                    Orientation='Horizontal'>
                    <TextBlock
                        Text='{Binding Path=Name}' />
                    <TextBlock
                        Text=', ' />
                    <TextBlock
                        Text='{Binding Path=Capital}' />
                </StackPanel>
            </DataTemplate>
        </ComboBox.ItemTemplate>
    </ComboBox>
</StackPanel>

我不确定当每个组合框需要自己的来源时,那个会不会起作用。 - BradleyDotNET
在我进一步了解之前,我不能确定这是否是我一直在寻找的东西(我即将开始阅读),但它肯定看起来朝着那个方向发展。同时,感谢您的回答! - user3313590
我非常确定它会起作用。我正在处理一个示例,希望一分钟后能在这里发布它。 - grizzly
这正是我一直在寻找的:使用单个集合作为源,并查看“同步”的子集。 我尝试了你的代码,它按预期工作。 我对指定筛选条件的语法不是很熟悉,但我会去查找。 谢谢你的回答! - user3313590
@Pharanoise 这只是一个(委托)函数,如果您想在子集中包括该项,则返回true,否则返回false。该示例使用了朴素方法。 - grizzly

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接