C# 如果上一条记录在ItemsSource集合中没有匹配项,则WPF ComboBox绑定工作,如果有匹配项,则绑定失败

C# 如果上一条记录在ItemsSource集合中没有匹配项,则WPF ComboBox绑定工作,如果有匹配项,则绑定失败,c#,wpf,xaml,combobox,null,C#,Wpf,Xaml,Combobox,Null,我有以下WPF组合框: <Window.Resources> <CollectionViewSource x:Key="performanceItemsource" Source="{Binding Path=SelectedReport.Performances}" > <CollectionViewSource.SortDescriptions> <scm:SortDescription Proper

我有以下WPF组合框:

<Window.Resources>
    <CollectionViewSource x:Key="performanceItemsource" Source="{Binding Path=SelectedReport.Performances}"  >
        <CollectionViewSource.SortDescriptions>
            <scm:SortDescription PropertyName="Name"/>
        </CollectionViewSource.SortDescriptions>
    </CollectionViewSource>
</Window.Resources>
 ...
    <ComboBox Name="cbxPlanPerf" Grid.ColumnSpan="2"
      SelectedValuePath="MSDPortfolioID" DisplayMemberPath="Name" 
      SelectedValue="{Binding Path=PlanPerfID}"
      ItemsSource="{Binding Source={StaticResource performanceItemsource}}"/>
绑定属性
PlanPerfID
是一个字符串


我使用列表框控件在记录之间移动。如果前一条记录的ComboBox.ItemsSource中没有任何项,则ComboBox工作正常。如果以前记录的ComboBox.ItemsSource中有任何项,则新记录将无法在
ItemsSource
集合中找到其匹配项。我尝试在XAML和代码隐藏中设置
ItemsSource
,但没有改变这种奇怪的行为。我如何才能让这个该死的东西正常工作?

在Xaml中处理列表/ObservableCollection时,尝试将
ICollectionView
IsSynchronizedWithCurrentItem
属性结合使用。viewmodel中的ICollectionView可以处理所有需要的事情,例如排序、过滤、跟踪选择和状态

Xaml:


视图模型:

public class ViewModel : INotifyPropertyChanged 
    {
        private readonly IReportService _reportService;
        private ObservableCollection<ReportViewModel> _reports = new ObservableCollection<ReportViewModel>();
        private PerformanceViewModel _currentPerformance;
        private ReportViewModel _currentReport;

        public ObservableCollection<ReportViewModel> Reports
        {
            get { return _reports; }
            set { _reports = value; OnPropertyChanged("Reports");}
        }

        public ReportViewModel CurrentReport
        {
            get { return _currentReport; }
            set { _currentReport = value; OnPropertyChanged("CurrentReport");}
        }

        public PerformanceViewModel CurrentPerformance
        {
            get { return _currentPerformance; }
            set { _currentPerformance = value; OnPropertyChanged("CurrentPerformance");}
        }

        public ICollectionView ReportsView { get; private set; }
        public ICollectionView PerformancesView { get; private set; }        

        public ViewModel(IReportService reportService)
        {
            if (reportService == null) throw new ArgumentNullException("reportService");
            _reportService = reportService;

            var reports = _reportService.GetData();
            Reports = new ObservableCollection<ReportViewModel>(reports);

            ReportsView = CollectionViewSource.GetDefaultView(Reports);
            ReportsView.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascending));
            ReportsView.CurrentChanged += OnReportsChanged;
            ReportsView.MoveCurrentToFirst();
        }

        private void OnReportsChanged(object sender, EventArgs e)
        {
            var selectedReport = ReportsView.CurrentItem as ReportViewModel;
            if (selectedReport == null) return;

            CurrentReport = selectedReport;

            if(PerformancesView != null)
            {
                PerformancesView.CurrentChanged -= OnPerformancesChanged;
            }

            PerformancesView = CollectionViewSource.GetDefaultView(CurrentReport.Performances);
            PerformancesView.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascending));
            PerformancesView.CurrentChanged += OnPerformancesChanged;
            PerformancesView.MoveCurrentToFirst();
        }

        private void OnPerformancesChanged(object sender, EventArgs e)
        {
            var selectedperformance = PerformancesView.CurrentItem as PerformanceViewModel;
            if (selectedperformance == null) return;

            CurrentPerformance = selectedperformance;
        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
公共类视图模型:INotifyPropertyChanged
{
专用只读IReportService\u reportService;
私有ObservableCollection_reports=新ObservableCollection();
私人绩效eviewModel\u currentPerformance;
private ReportViewModel\u currentReport;
公开收集报告
{
获取{return\u reports;}
设置{u reports=value;OnPropertyChanged(“reports”);}
}
公共报告视图模型当前报告
{
获取{return\u currentReport;}
设置{u currentReport=value;OnPropertyChanged(“currentReport”);}
}
公共性能虚拟模型当前性能
{
获取{return\u currentPerformance;}
设置{u currentPerformance=value;OnPropertyChanged(“currentPerformance”);}
}
公共ICollectionView报告视图{get;private set;}
公共ICollectionView性能视图{get;private set;}
公共视图模型(IReportService reportService)
{
如果(reportService==null)抛出新的ArgumentNullException(“reportService”);
_reportService=reportService;
var reports=_reportService.GetData();
报告=新的可观察收集(报告);
ReportsView=CollectionViewSource.GetDefaultView(报告);
ReportsView.SortDescriptions.Add(新的SortDescription(“Name”,ListSortDirection.升序));
ReportsView.CurrentChanged+=OnReportsChanged;
ReportsView.MoveCurrentToFirst();
}
私有void OnReportsChanged(对象发送方,事件参数e)
{
var selectedReport=ReportsView.CurrentItem作为ReportViewModel;
if(selectedReport==null)返回;
CurrentReport=selectedReport;
如果(PerformanceView!=null)
{
PerformanceView.CurrentChanged-=OnPerformanceChanged;
}
PerformancesView=CollectionViewSource.GetDefaultView(CurrentReport.Performances);
PerformanceView.SortDescriptions.Add(新的SortDescription(“名称”,ListSortDirection.升序));
PerformanceView.CurrentChanged+=OnPerformanceChanged;
PerformanceView.MoveCurrentToFirst();
}
private void onPerformanceChanged(对象发送方,事件参数e)
{
var selectedperformance=PerformanceView.CurrentItem作为PerformanceView模型;
if(selectedperformance==null)返回;
CurrentPerformance=所选性能;
}
公共事件属性更改事件处理程序属性更改;
受保护的虚拟void OnPropertyChanged(字符串propertyName)
{
PropertyChangedEventHandler处理程序=PropertyChanged;
if(handler!=null)handler(这是新的PropertyChangedEventArgs(propertyName));
}
}

我找到了一个快速而肮脏的解决方案。我只是碰巧在我的报表实体上有一个public
NotifyPropertyChanged()
方法,我发现如果我调用
SelectedReport.NotifyPropertyChanged(“PlanPerfID”)
在报告列表框的
选择更改的事件中,震动足以使
组合框
重新评估并在
项目资源
中找到匹配的项目。是的,是克鲁格

更新:对于某些情况,我还需要添加
SelectedReport.NotifyPropertyChanged(“性能”)

更新2:好吧,原来上面的不是防弹的,我遇到了一个破坏它的情况,所以我必须想出一个更好的解决办法:

  • 更改了窗口的“代码隐藏”中的
    SelectedReport
    属性,添加了一个私有标志(
    \u settingCombos
    ),以防止绑定扭曲绑定值,直到
    项源中的灰尘沉降下来:

    private bool _settingCombos = false;
    private Report _SelectedReport;
    public Report SelectedReport
    {
      get { return _SelectedReport; }
      set 
      {
        _settingCombos = true;
        _SelectedReport = value;
        NotifyPropertyChanged("SelectedReport");
      }
    }
    
  • 在“代码隐藏”窗口中创建了一个要绑定的代理,如果
    \u settingCombos
    标志为
    true
    ,该代理将拒绝更新属性的值:

    public string PlanPerfID_Proxy
    {
      get { return SelectedReport.PlanPerfID; }
      set
      {
        if (!_settingCombos)
        {
          SelectedReport.PlanPerfID = value;
          NotifyPropertyChanged("PlanPerfID_Proxy");
        }
      }
    }
    
  • 在报告列表框的
    SelectionChanged
    事件中添加了额外的通知,以及将
    \u settingCombos
    标志重置回
    false
    的代码:

    private void lbxReports_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
      //KLUGE: Couldn't get the ComboBoxes associated with these properties to work right
      //this forces them to re-evaluate after the Report has loaded
      if (SelectedReport != null)
      {
        NotifyPropertyChanged("PlanPerfID_Proxy");
        _settingCombos = false;
      }
    }
    
  • 将组合框
  • 绑定到
    PlanPerfID\u代理
    属性(而不是直接绑定到
    SelectedReport.PlanPerfID
    属性)


    哇,太麻烦了!我认为这只是.NET的绑定逻辑被
    组合框.ItemSource
    的动态特性弄糊涂的一个例子,但这似乎已经解决了它。希望它能帮助其他人。

    哇,这与我目前的工作方式有很大的范式转变。看起来你会让我把这个ViewModel cod
    public string PlanPerfID_Proxy
    {
      get { return SelectedReport.PlanPerfID; }
      set
      {
        if (!_settingCombos)
        {
          SelectedReport.PlanPerfID = value;
          NotifyPropertyChanged("PlanPerfID_Proxy");
        }
      }
    }
    
    private void lbxReports_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
      //KLUGE: Couldn't get the ComboBoxes associated with these properties to work right
      //this forces them to re-evaluate after the Report has loaded
      if (SelectedReport != null)
      {
        NotifyPropertyChanged("PlanPerfID_Proxy");
        _settingCombos = false;
      }
    }