2

私は次の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}}"/>

ソースCollectionViewSourceは次のとおりです。

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

バインドされたプロパティPlanPerfIDは文字列です。

ListBoxコントロールを使用してレコード間を移動します。前のレコードのComboBox.ItemsSourceにアイテムがない場合、ComboBoxは正常に機能します。前のレコードのComboBox.ItemsSourceにアイテムがあった場合、新しいレコードはItemsSourceコレクション内で一致するアイテムを見つけられません。XAMLと分離コードの両方でを設定しようとしましたItemsSourceが、この奇妙な動作は何も変わりません。どうすればこのくそったれを機能させることができますか?

4

2 に答える 2

0

Xamlでリスト/ObservableCollectionを処理するときに、プロパティICollectionViewsと組み合わせて使用​​してみてください。IsSynchronizedWithCurrentItemビューモデルのICollectionViewは、並べ替え、フィルタリング、選択と状態の追跡など、必要なすべてのものを処理できます。

Xaml:

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

ViewModel:

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 に答える
0

私は自分の問題に対する迅速で汚い解決策を見つけました。たまたまNotifyPropertyChanged()ReportエンティティにpublicメソッドがSelectedReport.NotifyPropertyChanged("PlanPerfID")あり、Report ListBoxのイベントを呼び出した場合、を再評価して、に一致するアイテムを見つけるSelectionChangedのに十分な衝撃であることがわかりました。ええ、それはクルージです...ComboBoxItemsSource

SelectedReport.NotifyPropertyChanged("Performances")更新:私はまた、いくつかの状況のた​​めに追加する必要がありました...

更新2:わかりました、上記は防弾ではなかったことがわかりました、そして私はそれを壊した状況に遭遇したので、私はより良い回避策を考え出さなければなりませんでした:

  1. ウィンドウの分離コードのプロパティを変更しSelectedReport、プライベートフラグ(_settingCombos)を追加して、ほこりが変化して落ち着くまでバインディングがバインドされた値を台無しにしないようにしました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をリセットするコードとともに追加の通知を追加しました。_settingCombosfalse

    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. プロパティに直接バインドするのではなくComboBox、プロパティにバインドします。PlanPerfID_ProxySelectedReport.PlanPerfID

うわー、なんて面倒なんだ!これは、.NETのバインディングロジックがの動的な性質によって混乱しているだけの場合だと思いますが、これでComboBox.ItemSource修正されたようです。それが他の誰かを助けることを願っています。

于 2012-12-04T22:35:59.920 に答える