将 RibbonComboBox 绑定到过滤后的 ObservableCollection 导致错误

Binding a RibbonComboBox to filtered ObservableCollection causing errors

我正在使用 C# 尝试使用 RibbonRadioButtons 过滤 RibbonComboBox 列表,但我无法解决不断收到的错误。

国家列表在我使用 ListCollectionView 过滤的 ObservableCollection 中。在另一个用户 (C# WPF Filter ComboBox based on RadioButtons) 的帮助下,我现在可以部分工作,但是如果我单击单选按钮,组合框中的列表会更新,但组合框中没有显示任何内容;我希望显示列表中的第一项。如果我 select 一个国家,然后从 RadioButton 单击另一个大陆,我会在 'public bool Africa {... CountryView.Refresh()}' 行或我单击的任何按钮上收到下面显示的错误。 [代码于 9 月 25 日更新以反映评论。]

RibbonComboBox 错误

Object reference not set to an instance of an object.
In VS Output window:
Error: 40 : BindingExpression path error: 'DisplayName' property not found on 'object'

当我将 XAML 中的 RibbonComboBox 更改为 ComboBox 时,如下所示,它似乎可以正常工作,但它会产生另一个错误。但是我更愿意使用 RibbonComboBox 但不确定如何解决问题。如果您能提供任何帮助使其正常工作,我们将不胜感激。

XAML

<Grid>
    <DockPanel>
        <r:Ribbon DockPanel.Dock="Top" x:Name="Ribbon">
            <r:RibbonGroup Header="Continent" Width="260">
                <!--<ComboBox x:Name="CountryList" Width="100" ItemsSource="{Binding CountryView}" SelectedItem="{Binding SelectedCountry}" DisplayMemberPath="DisplayName"/>-->
                <r:RibbonComboBox x:Name="CountryList" Height="Auto" SelectionBoxWidth="230" VerticalAlignment="Center">
                    <r:RibbonGallery x:Name="cbSelectedCountry" SelectedValue="{Binding SelectedCountry, Mode=TwoWay}" SelectedValuePath="DisplayName" >
                        <r:RibbonGalleryCategory x:Name="cbCountryList" ItemsSource="{Binding CountryView}" DisplayMemberPath="DisplayName" />
                    </r:RibbonGallery>
                </r:RibbonComboBox>
                <WrapPanel>
                    <r:RibbonRadioButton x:Name="All" Label="All" GroupName="ContinentGroup" Height="Auto" Width="Auto" HorizontalAlignment="Left" IsChecked="{Binding Path=All}">
                    </r:RibbonRadioButton>
                    <r:RibbonRadioButton x:Name="Africa" Label="Africa" GroupName="ContinentGroup" Height="Auto" Width="Auto" HorizontalAlignment="Left" IsChecked="{Binding Path=Africa}">
                    </r:RibbonRadioButton>
                    <r:RibbonRadioButton x:Name="America" Label="America" GroupName="ContinentGroup" Height="Auto" Width="Auto" HorizontalAlignment="Left" IsChecked="{Binding Path=America}">
                    </r:RibbonRadioButton>
                </WrapPanel>
            </r:RibbonGroup>
        </r:Ribbon>
    </DockPanel>
</Grid>

C# 代码隐藏 (DataContext):

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows.Data;

public class MySettings : INotifyPropertyChanged
{
    private readonly ObservableCollection<Country> countries;
    private ContinentViewModel selectedContinent;
    private static string selectedCountry;
    private int selectedRadioGroup;
    private ObservableCollection<ContinentViewModel> continents;
    private ListCollectionView countryView;
    public event PropertyChangedEventHandler PropertyChanged;
    private bool _All;
    private bool _Africa;
    private bool _America;

    public bool All { get => _All; set { _All = value; CountryView.Refresh(); SelectedCountry = countries[0].ToString(); } }
    public bool Africa { get => _Africa; set { _Africa = value; CountryView.Refresh(); SelectedCountry = countries[0].ToString(); } }
    public bool America { get => _America; set { _America = value; CountryView.Refresh(); SelectedCountry = countries[0].ToString(); } }

    public MySettings()
    {
        countries = new ObservableCollection<Country>(
            new[]
            {
                new Country() { Continent = Continent.Africa, DisplayName = "Algeria" },
                new Country() { Continent = Continent.Africa, DisplayName = "Egypt" },
                new Country() { Continent = Continent.Africa, DisplayName = "Chad" },
                new Country() { Continent = Continent.Africa, DisplayName = "Ghana" },
                new Country() { Continent = Continent.America, DisplayName = "Canada" },
                new Country() { Continent = Continent.America, DisplayName = "Greenland" },
                new Country() { Continent = Continent.America, DisplayName = "Haiti" }
            });
        CountryView = (ListCollectionView)CollectionViewSource.GetDefaultView(countries);
        CountryView.Filter += CountryFilter;
        Continents = new ObservableCollection<ContinentViewModel>(Enum.GetValues(typeof(Continent)).Cast<Continent>().Select(c => new ContinentViewModel { Model = c }));
    }

    private bool CountryFilter(object obj)
    {
        var country = obj as Country;
        if (country == null) return false;
        if (All) return true;
        if (Africa) return country.Continent == Continent.Africa;
        if (America) return country.Continent == Continent.America;
        return true;
    }

    public ObservableCollection<ContinentViewModel> Continents
    {
        get { return continents; }
        set
        {
            continents = value;
        }
    }

    public ListCollectionView CountryView
    {
        get { return countryView; }
        set
        {
            countryView = value;
        }
    }

    public class Country
    {
        public string DisplayName { get; set; }
        public Continent Continent { get; set; }
    }

    public enum Continent
    {
        All,
        Africa,
        America
    }

    public class ContinentViewModel
    {
        public Continent Model { get; set; }
        public string DisplayName
        {
            get
            {
                return Enum.GetName(typeof(Continent), Model);
            }
        }
    }

    public ContinentViewModel SelectedContinent
    {
        get { return selectedContinent; }
        set
        {
            selectedContinent = value;
            OnContinentChanged();
            this.OnPropertyChanged("SelectedContinent");
        }
    }

    private void OnContinentChanged()
    {
        CountryView.Refresh();
    }

    public int SelectedRadioGroup
    {
        get { return selectedRadioGroup; }
        set
        {
            selectedRadioGroup = value;
        }
    }

    public string SelectedCountry
    {
        get { return selectedCountry; }
        set
        {
            if (selectedCountry == value) return;
            selectedCountry = value;
        }
    }

    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

我已经修改了你的 class 来解决你的问题。代码的变化是:

  1. 代替CountryView = new ListCollectionView(countries)CountryView = (ListCollectionView)CollectionViewSource.GetDefaultView(countries)
  2. 在每次刷新列表时,即检查 CheckBox 时,设置 ComboBox 的 SelectedItem,即在您的情况下 SelectedCountry,如下所示: SelectedCountry = _All ? countries.FirstOrDefault().DisplayName : SelectedCountry; SelectedCountry = _Africa ? countries.Where(_ => _.Continent == Continent.Africa).FirstOrDefault().DisplayName : SelectedCountry; SelectedCountry = _America ? countries.Where(_ => _.Continent == Continent.America).FirstOrDefault().DisplayName : SelectedCountry;
  3. 为所有属性调用 OnPropertyChanged

    private readonly ObservableCollection<Country> countries;
    private ContinentViewModel selectedContinent;
    private static string selectedCountry;
    private int selectedRadioGroup;
    private ObservableCollection<ContinentViewModel> continents;
    private ListCollectionView countryView;
    public event PropertyChangedEventHandler PropertyChanged;
    private bool _All;
    private bool _Africa;
    private bool _America;
    public bool All
    {
        get
        {
            return _All;
        }
        set
        {
            _All = value;
            CountryView.Refresh();
            SelectedCountry = _All ? countries.FirstOrDefault().DisplayName : SelectedCountry;
            OnPropertyChanged("All");
        }
    }
    
    public bool Africa
    {
        get
        {
            return _Africa;
        }
        set
        {
            _Africa = value;
            CountryView.Refresh();
            SelectedCountry = _Africa ? countries.Where(_ => _.Continent == Continent.Africa).FirstOrDefault().DisplayName : SelectedCountry;
            OnPropertyChanged("Africa");
        }
    }
    
    public bool America
    {
        get
        {
            return _America;
        }
        set
        {
            _America = value;
            CountryView.Refresh();
            SelectedCountry = _America ? countries.Where(_ => _.Continent == Continent.America).FirstOrDefault().DisplayName : SelectedCountry;
            OnPropertyChanged("America");
        }
    }
    
    public MySettings()
    {
        countries = new ObservableCollection<Country>(
            new[]
            {
            new Country() { Continent = Continent.Africa, DisplayName = "Algeria" },
            new Country() { Continent = Continent.Africa, DisplayName = "Egypt" },
            new Country() { Continent = Continent.Africa, DisplayName = "Chad" },
            new Country() { Continent = Continent.Africa, DisplayName = "Ghana" },
            new Country() { Continent = Continent.America, DisplayName = "Canada" },
            new Country() { Continent = Continent.America, DisplayName = "Greenland" },
            new Country() { Continent = Continent.America, DisplayName = "Haiti" }
            });
        CountryView = (ListCollectionView)CollectionViewSource.GetDefaultView(countries);
        CountryView.Filter += CountryFilter;
        Continents = new ObservableCollection<ContinentViewModel>(Enum.GetValues(typeof(Continent)).Cast<Continent>().Select(c => new ContinentViewModel { Model = c }));
    }
    
    private bool CountryFilter(object obj)
    {
        var country = obj as Country;
        if (country == null) return false;
        if (All && !Africa && !America) return true;
        else if (!All && Africa && !America) return country.Continent == Continent.Africa;
        else if (!All && !Africa && America) return country.Continent == Continent.America;
        return true;
    }
    
    public ObservableCollection<ContinentViewModel> Continents
    {
        get { return continents; }
        set
        {
            continents = value;
            OnPropertyChanged("Continents");
        }
    }
    
    public ListCollectionView CountryView
    {
        get { return countryView; }
        set
        {
            countryView = value;
            OnPropertyChanged("CountryView");
        }
    }
    
    public class Country
    {
        public string DisplayName { get; set; }
        public Continent Continent { get; set; }
    }
    
    public enum Continent
    {
        All,
        Africa,
        America
    }
    
    public class ContinentViewModel
    {
        public Continent Model { get; set; }
        public string DisplayName
        {
            get
            {
                return Enum.GetName(typeof(Continent), Model);
            }
        }
    }
    
    public ContinentViewModel SelectedContinent
    {
        get { return selectedContinent; }
        set
        {
            selectedContinent = value;
            OnContinentChanged();
            this.OnPropertyChanged("SelectedContinent");
        }
    }
    
    private void OnContinentChanged()
    {
        CountryView.Refresh();
    }
    
    public int SelectedRadioGroup
    {
        get { return selectedRadioGroup; }
        set
        {
            selectedRadioGroup = value;
            OnPropertyChanged("SelectedRadioGroup");
        }
    }
    
    public string SelectedCountry
    {
        get { return selectedCountry; }
        set
        {
            if (selectedCountry == value) return;
            selectedCountry = value;
            OnPropertyChanged("SelectedCountry");
        }
    }
    
    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }