2

I have the following WPF Combobox:

<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}}"/>

The Source for the CollectionViewSource is:

public List<MSDExportProxy> Performances
{
  get
  {
    if (Portfolio != null)
    {
      return (from a in Portfolio.Accounts where a.MSDPortfolioID != null select new MSDExportProxy(a))
        .Concat<MSDExportProxy>(from g in Portfolio.Groups where g.MSDPortfolioID != null select new MSDExportProxy(g))
        .Concat<MSDExportProxy>(from p in new[] { Portfolio } where p.MSDPortfolioID != null select new MSDExportProxy(p))
        .ToList<MSDExportProxy>();
    }
    return new List<MSDExportProxy>();
  }
}

The bound property PlanPerfID is a string.

I move between records using a ListBox control. The ComboBox works fine if the previous record had no items in its ComboBox.ItemsSource. If there were any items in the previous record's ComboBox.ItemsSource then the new record won't find its matching item in the ItemsSource collection. I've tried setting the ItemsSource in both XAML and the code-behind, but nothing changes this odd behavior. How can I get this darn thing to work?

4

2 回答 2

0

我找到了一个快速而肮脏的解决方案来解决我的问题。我只是碰巧NotifyPropertyChanged()在我的报告实体上有一个公共方法,我发现如果我SelectedReport.NotifyPropertyChanged("PlanPerfID")在报告列表框的SelectionChanged事件中调用它就足以让ComboBox重新评估并在ItemsSource. 对,是克鲁格……

更新:我还需要在SelectedReport.NotifyPropertyChanged("Performances")某些情况下添加...

更新 2:好的,事实证明上面不是防弹的,我遇到了破坏它的情况,所以我不得不想出一个更好的解决方法:

  1. 更改了SelectedReportWindow 代码隐藏中的属性,添加了一个私有标志 ( _settingCombos) 以防止 Binding 搞砸绑定值,直到尘埃落定ItemSource

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

    public string PlanPerfID_Proxy
    {
      get { return SelectedReport.PlanPerfID; }
      set
      {
        if (!_settingCombos)
        {
          SelectedReport.PlanPerfID = value;
          NotifyPropertyChanged("PlanPerfID_Proxy");
        }
      }
    }
    
  3. 在报告列表框的事件中添加了一个额外的通知SelectionChanged以及将标志重置_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;
      }
    }
    
  4. 绑定ComboBoxPlanPerfID_Proxy属性(而不是直接绑定到SelectedReport.PlanPerfID属性。

哇,好麻烦!我认为这只是 .NET 的绑定逻辑被 .NET 的动态特性弄糊涂的一个例子ComboBox.ItemSource,但这似乎已经解决了这个问题。希望它可以帮助别人。

于 2012-12-04T22:35:59.920 回答
0

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

xml:

<Window x:Class="ComboBoxBinding.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <ListBox Grid.Column="0" 
                 ItemsSource="{Binding Reports}" 
                 DisplayMemberPath="Name" 
                 IsSynchronizedWithCurrentItem="True" />
        <ComboBox Grid.Column="1" 
                  ItemsSource="{Binding CurrentReport.Performances}" 
                  DisplayMemberPath="Name" 
                  IsSynchronizedWithCurrentItem="True" />
    </Grid>
</Window>

视图模型:

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));
        }
    }
于 2012-10-29T08:09:17.840 回答