简体   繁体   中英

WPF ComboBox Errors with Data Binding

I'm new to C# and I keep getting the errors below which I can't remove.

System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.ItemsControl', AncestorLevel='1''. BindingExpression:Path=HorizontalContentAlignment; DataItem=null; target element is 'RibbonGalleryItem' (Name=''); target property is 'HorizontalContentAlignment' (type 'HorizontalAlignment')
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.ItemsControl', AncestorLevel='1''. BindingExpression:Path=VerticalContentAlignment; DataItem=null; target element is 'RibbonGalleryItem' (Name=''); target property is 'VerticalContentAlignment' (type 'VerticalAlignment')

My code is below and I can't tell exactly which line is causing the error but I suspect it's related to the RibbonRadioButtons as, if I remove them, I don't get errors. The errors only appear after clicking on two or more radio buttons. An answer at ComboBoxItem continues to throw binding error despite style suggested it was multiple Refresh() statements causing the problem but I can't see how to avoid this.

Can anyone help me resolve this issue?

XAML

<Grid>
  <DockPanel>
    <r:Ribbon DockPanel.Dock="Top" x:Name="Ribbon" SelectedIndex="0">
      <r:RibbonGroup Header="Continent" Width="Auto">
        <r:RibbonComboBox x:Name="CountryList" Height="Auto" VerticalAlignment="Center" HorizontalContentAlignment="Left">
          <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#

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

    public bool All
    {
        get => _All;
        set
        {
            _All = value;
            CountryView.Refresh();
            SelectedCountry = _All ? countries.FirstOrDefault().DisplayName : SelectedCountry;
            OnPropertyChanged("All");
        }
    }

    public bool Africa
    {
        get => _Africa;
        set
        {
            _Africa = value;
            CountryView.Refresh();
            SelectedCountry = _Africa ? countries.Where(_ => _.Continent == Continent.Africa).FirstOrDefault().DisplayName : SelectedCountry;
            OnPropertyChanged("Africa");
        }
    }

    public bool America
    {
        get => _America;
        set
        {
            _America = value;
            CountryView.Refresh();
            SelectedCountry = _America ? countries.Where(_ => _.Continent == Continent.America).FirstOrDefault().DisplayName : SelectedCountry;
            OnPropertyChanged("America");
        }
    }

    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 => continents;
        set
        {
            continents = value;
            OnPropertyChanged("Continents");
        }
    }

    public ListCollectionView CountryView
    {
        get => 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 => Enum.GetName(typeof(Continent), Model);
    }

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

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

    public int SelectedRadioGroup
    {
        get => selectedRadioGroup;
        set
        {
            selectedRadioGroup = value;
            OnPropertyChanged("SelectedRadioGroup");
        }
    }

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

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

I used mvvmlight and made some changes to simplify your code:

<Window x:Class="WpfTest.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:WpfTest"
    DataContext="{Binding Source={StaticResource Locator}, Path=Main}"
    mc:Ignorable="d"
    Title="MainWindow" Height="450" Width="800">
<Grid>
    <DockPanel>
        <Ribbon DockPanel.Dock="Top" x:Name="Ribbon" SelectedIndex="0">
            <RibbonGroup Header="Continent" Width="Auto">
                <RibbonComboBox x:Name="CountryList" Height="Auto" VerticalAlignment="Center" HorizontalContentAlignment="Left">
                    <RibbonGallery x:Name="cbSelectedCountry" SelectedItem="{Binding SelectedCountry, Mode=TwoWay}">
                        <RibbonGalleryCategory x:Name="cbCountryList" ItemsSource="{Binding CountryView}" DisplayMemberPath="DisplayName" />
                    </RibbonGallery>
                </RibbonComboBox>
                <WrapPanel>
                    <RibbonRadioButton x:Name="All" Label="All" GroupName="ContinentGroup" Height="Auto" Width="Auto" HorizontalAlignment="Left" IsChecked="{Binding Path=All}"></RibbonRadioButton>
                    <RibbonRadioButton x:Name="Africa" Label="Africa" GroupName="ContinentGroup" Height="Auto" Width="Auto" HorizontalAlignment="Left" IsChecked="{Binding Path=Africa}"></RibbonRadioButton>
                    <RibbonRadioButton x:Name="America" Label="America" GroupName="ContinentGroup" Height="Auto" Width="Auto" HorizontalAlignment="Left" IsChecked="{Binding Path=America}"></RibbonRadioButton>
                </WrapPanel>
            </RibbonGroup>
        </Ribbon>
    </DockPanel>
</Grid>

I created a property SelectedCountry of type Country to bind the combobox to, rather than setting a string like your code was doing.

using GalaSoft.MvvmLight;
using System.Collections.ObjectModel;
using System.Windows.Data;

namespace WpfTest.ViewModel
{
/// <summary>
/// This class contains properties that the main View can data bind to.
/// <para>
/// Use the <strong>mvvminpc</strong> snippet to add bindable properties to this ViewModel.
/// </para>
/// <para>
/// You can also use Blend to data bind with the tool's support.
/// </para>
/// <para>
/// See http://www.galasoft.ch/mvvm
/// </para>
/// </summary>
public class MainViewModel : ViewModelBase
{
    private readonly ObservableCollection<Country> countries;
    private ListCollectionView _countryView;

    public ListCollectionView CountryView
    {
        get { return _countryView; }
        set { this.Set(ref _countryView, value); }
    }

    private Country _SelectedCountry;
    public Country SelectedCountry
    {
        get { return _SelectedCountry; }
        set { this.Set(ref _SelectedCountry, value); }
    }

    private bool _All;
    public bool All
    {
        get { return _All; }
        set
        {
            this.Set(ref _All, value);
            CountryView.Refresh();
        }
    }

    private bool _Africa;
    public bool Africa
    {
        get { return _Africa; }
        set
        {
            this.Set(ref _Africa, value);
            if (SelectedCountry != null && SelectedCountry.Continent != Continent.Africa)
            {
                SelectedCountry = null;
            }
            CountryView.Refresh();
        }
    }

    private bool _America;
    public bool America
    {
        get { return _America; }
        set
        {
            this.Set(ref _America, value);
            if (SelectedCountry != null && SelectedCountry.Continent != Continent.America)
            {
                SelectedCountry = null;
            }
            CountryView.Refresh();
        }
    }


    /// <summary>
    /// Initializes a new instance of the MainViewModel class.
    /// </summary>
    public MainViewModel()
    {
        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;
        CountryView.Refresh();


        ////if (IsInDesignMode)
        ////{
        ////    // Code runs in Blend --> create design time data.
        ////}
        ////else
        ////{
        ////    // Code runs "for real"
        ////}
    }

    private bool CountryFilter(object obj)
    {
        if (obj is Country c)
        {
            if (Africa && c.Continent != Continent.Africa)
            {
                return false;
            }
            if (America && c.Continent != Continent.America)
            {
                return false;
            }
        }

        return true;
    }
}

public enum Continent
{
    All,
    Africa,
    America
}

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

I don't get the binding errors you were having, but my datacontext is being set from the locator created by mvvmlight.

If your code is being used inside of a usercontrol, you can change this by taking the grid and moving it inside a usercontrol (without the datacontext line) and having it derive from the parents datacontext.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM