如果将 wpf ItemsControl 与 MVVM 模式结合使用,如何将一个 ObservableCollection 作为 ItemsSource 绑定到不同的组合框(模型)?

How to bind one ObservableCollection as ItemsSource to different comboBoxes (models) if using wpf ItemsControl with MVVM pattern?

我使用 MVVM 模式使用 wpf ItemsControl 元素为不同模型动态创建组合框列表。我想要一个逻辑,如果我 select 一个组合框中的一个元素,那么在所有其他组合框中它不会出现。当我使用 ItemsControl(ItemsSource - 我的模型列表)并在 VievModel 中为其创建元素时,我遇到了困难 - 绑定不起作用,只有当我为每个模型(模型 class),不适用于所有组合框(在 ViewModel class 中)。例如,我可以为 ViewModel 中的组合框项目设置 1 个 ObservableCollection 并使用 ItemsControl 来创建组合框吗?

我的看法:

    <ItemsControl ItemsSource="{Binding Items}" Grid.Row="1">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*"  />
                            <ColumnDefinition Width="*"  />
                        </Grid.ColumnDefinitions>
                        <Label  Content="{Binding Name}" Grid.Column="0" />
                        <ComboBox  ItemsSource="{Binding ComboBoxItems}" Grid.Column="1"  
                                   SelectedItem="{Binding SelectedItem}"/>
</Grid>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>

视图模型:

public ObservableCollection<Model> Items { get; set; }

// if I add here public ObservableCollection<string> ComboBoxItems { get; set; } 
// binding isn't working, so I add it to Model class, but in it it does not work as I need.

public ViewModel()
{
Items = new ObservableCollection<Model>();
Items.Add(new Model {Name = "11111"});
Items.Add(new Model {Name = "22222"});
}

型号:

 public string Name
    {
        get { return _name; }
        set
        {
            _name = value;
            OnPropertyChanged("Name");
        }
    }
 public string SelectedItem
    {
        get { return _selectedItem; }
        set
        {
            _selectedItem = value;
            OnPropertyChanged("SelectedItem");
        }
    }

public ObservableCollection<string> ComboBoxItems {get;set;}

public Model()
{
ComboBoxItems = new ObservableCollection<string>();
ComboBoxItems.Add("q");
ComboBoxItems.Add("w");
ComboBoxItems.Add("e");
ComboBoxItems.Add("r");
ComboBoxItems.Add("t");
ComboBoxItems.Add("y");
}

1 ObservableCollection 用于 ViewModel 中的组合框项,还使用 ​​ItemsControl 创建组合框 这意味着你想将相同的 ObservableCollection 绑定到你的 ItemsControlComboBox 如果不是请在评论中告诉我

答案

   <ItemsControl ItemsSource="{Binding Items}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*"  />
                            <ColumnDefinition Width="*"  />
                        </Grid.ColumnDefinitions>
                        <Label  Content="{Binding Name}" Grid.Column="0" />
                        <ComboBox  ItemsSource="{Binding Items ,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ItemsControl}}" Grid.Column="1"  
                                  DisplayMemberPath="Name" SelectedItem="{Binding SelectedItem}"/>
                    </Grid>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>

我绑定了 ItemsComboBox

为了实现您的目标,您可以过滤 ComboBoxItems 的视图。你可以用单源单一视图的方式来做,你只需要改变你的ViewModel的构造函数,比如:

public ViewModel()
{
    Items = new ObservableCollection<Model>();
    Items.Add(new Model {Name = "11111"});
    Items.Add(new Model {Name = "22222"});
    Items.Add(new Model {Name = "33333"});

    foreach (var item in Items)
    {
        CollectionViewSource.GetDefaultView(item.ComboBoxItems).Filter =
            (x) => !Items.Where((y) => y != item).Select(y => y.SelectedItem).Any(y => y == (string)x);

        item.PropertyChanged += (s, e) =>
        {
            foreach (var obj in Items.Where((x) => x != item).Select(x => x.ComboBoxItems))
                CollectionViewSource.GetDefaultView(obj).Refresh();
        };
    }    
}

或者您也可以采用单源多视图方式,这样您就可以丢弃模型。

public class ViewModel
{
    private List<string> _comboBoxItems = new List<string> { "q", "w", "e", "r", "t", "y" };
    private List<ICollectionView> _comboBoxViews = new List<ICollectionView>();

    public ObservableCollection<string> Names { get; set; } = new ObservableCollection<string> { "111", "222", "333" };

    public ICollectionView ComboBoxView
    {
        get
        {
            var view = new CollectionViewSource { Source = _comboBoxItems}.View;
            _comboBoxViews.Add(view);
            view.MoveCurrentToPosition(-1);

            view.Filter = (x) => !_comboBoxViews.Where(y => y != view).Any(y => (string)y.CurrentItem == (string)x);

            view.CurrentChanged += (s, e) =>
            {
                foreach (var v in _comboBoxViews.Where(x => x != view))
                    v.Refresh();
            };

            return view;
        }
    }
}

<ItemsControl ItemsSource="{Binding Names}" Grid.Row="1">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*"  />
                    <ColumnDefinition Width="*"  />
                </Grid.ColumnDefinitions>
                <Label  Content="{Binding}" Grid.Column="0" />
                <ComboBox  ItemsSource="{Binding DataContext.ComboBoxView, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
                           Grid.Column="1"/>
            </Grid>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>