繁体   English   中英

WPF ComboBox 数据绑定错误

[英]WPF ComboBox Errors with Data Binding

我是 C# 的新手,我不断收到无法删除的错误。

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')

我的代码在下面,我无法准确判断是哪一行导致了错误,但我怀疑它与 RibbonRadioButtons 有关,因为如果我删除它们,我不会收到错误。 只有在单击两个或更多单选按钮后才会出现错误。 尽管样式表明它是多个 Refresh() 语句导致问题,但 ComboBoxItem 的答案继续引发绑定错误,但我看不出如何避免这种情况。

谁能帮我解决这个问题?

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

我使用 mvvmlight 并进行了一些更改以简化您的代码:

<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>

我创建了一个 Country 类型的属性 SelectedCountry 来将 combobox 绑定到,而不是像您的代码那样设置字符串。

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

我没有收到您遇到的绑定错误,但我的数据上下文是从 mvvmlight 创建的定位器中设置的。

如果您的代码在用户控件内使用,您可以通过获取网格并将其移动到用户控件内(没有数据上下文行)并使其派生自父数据上下文来更改此设置。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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