Using C# I'm trying to filter a RibbonComboBox list using RibbonRadioButtons but I can't solve an error I keep receiving.
The list of countries is in an ObservableCollection which I'm filtering using ListCollectionView. With help from another user (C# WPF Filter ComboBox based on RadioButtons) I now have it partially working but if I click on a radio button the list in the ComboBox is updated but nothing is shown in the ComboBox; I expected the first item in the list to be displayed. If I select a country then click another continent from a RadioButton I receive the error shown below on the line 'public bool Africa {... CountryView.Refresh()}' or whichever button I clicked on. [Code updated 25 Sep to reflect comments.]
RibbonComboBox errors
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'
When I changed the RibbonComboBox to a ComboBox in the XAML as shown below it does seem to work correctly though it generates another error. However I would prefer to use the RibbonComboBox but not sure how to resolve the problem. Any help you can give to get it working would be appreciated.
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# Code behind (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));
}
}
I have modified your class to solve your issues. The changes in code is:
CountryView = new ListCollectionView(countries)
do CountryView = (ListCollectionView)CollectionViewSource.GetDefaultView(countries)
CheckBox
set the SelectedItem
of your ComboBox i.e SelectedCountry
in your case as following :
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;
Call OnPropertyChanged
for all the properties.
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));
}