当前位置: 首页 > 知识库问答 >
问题:

数据绑定的WPF组合框错误

于恺
2023-03-14

我是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有关,因为如果我删除它们,我不会得到错误。只有在单击两个或更多单选按钮后,错误才会出现。ComboBoxItem上的一个回答继续抛出绑定错误,尽管style暗示是多个Refresh()语句导致了这个问题,但我不知道如何避免这个问题。

有人能帮我解决这个问题吗?

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

共有1个答案

东方谦
2023-03-14

我使用了 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来绑定组合框,而不是像代码那样设置字符串。

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创建的定位器设置的。

如果您的代码在用户控件中使用,您可以通过获取网格并在用户控件中移动它(没有datacontext行)并让它从父数据文本派生来更改它。

 类似资料:
  • 我没有在.xaml中指定任何特殊的内容,所以我将省略该部分。除此之外,我希望能够使用Name属性设置SelectedItem。我非常希望答案在代码后面,但如果它愚蠢地复杂,只有xaml的答案也一样好。我没有MVVM的任何经验,我可以假设它会被建议。随着我对WPF的深入研究,我当然会扩展我在这方面的知识,但现在我只想让它发挥作用 这不是家庭作业 编辑:忘记列出我也会遇到的错误 系统.Windows。

  • 使用Datagridtemplatecolumn将WPFDatagrid绑定到组合框。很难获得组合框绑定的selectedItem。我发现了类似的例子,但这并不能解决我的问题。 请在下面找到我的 XAML 的代码片段和数据结构: 我在上面定义了一个数据结构,它实现了INotifyPropertychanged接口。 现在,在视图模型中,有一个可观察的X列表集合,即XList,它绑定到XAML中的数

  • 我有一个用户控件,其数据上下文设置为名为 EmployeeList_VM 的视图模型。然后,我在该用户控件中有一个 ContentControl,该控件将其 datacontect 设置为视图模型的公共属性 (EmployeeSelection)。ContentControl 的数据上下文与同一用户控件中列表框的选定项绑定相同。 我希望ContentControl中的combobox(属于名为Em

  • 我有一个包含以下列的表格: 表在在线数据库中。我使用dataset.xsd文件和连接字符串在客户端和在线数据库之间进行通信。 以下是我使用的一些方法:

  • 我在wpf c#应用程序中有一个组合框。在xaml中,我尝试执行以下操作。 ItemsSource 来自一个变量。选定项在另一个变量上设置值 但我希望显示的文本来自一个新变量。 如何停止使所选项目源显示为正文?

  • 我有2个组合盒主从这样: 但当我在第一个组合框中选择一个项目时,它有空的“项目”列表,而我已经用“项目”选择了其中一个项目,并在第二个组合框选择了项目。第二个组合框中的文本不为空。我也尝试过使用IsSynchronizedWithCurrentItem=“False”。 是什么问题呢?