2017-07-06 12 views
1

ComboBoxの中にチェックボックスを含むアイテムを表示するカスタムコントロールがあります。これを実現するために、CheckBoxDataTemplateを使用しました。 ComboBoxItemSourceは、フィルタ値を含むObserableCollection<FilterValue>へのバインディングを使用します。 FilterValueは、INotifyPropertyChangedを実装するカスタムクラスです。 CheckBoxContentIsCheckedのプロパティは、myリストの値を使用するバインドも使用します。このコントロールはSilverlightで使用されます。自分自身をバインドCheckBoxバインドされたコレクションの更新が行われる前に、チェックされたイベントが発生します

ここに見られるように、正常に動作します:私はCheckedまたはUncheckedイベントを登録するとき
Binding itself works fine, as seen here:

問題が表示されます。

チェックボックスのいずれかが状態を変更すると、イベントは期待どおりに発生しますが、この時点ではバインドされたリストの値は更新されません。 デバッグ中に見たのは、FilterValuePropertyChangedイベントの前に、Checked/Uncheckedというイベントが発生していることです。 これは、イベントが発生した時点で、すべてのアクティブ(チェックされた)フィルタをリストに尋ねることができないことを意味します。これを達成するために私は何ができますか?

FilterControl.xaml:

<UserControl 
    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:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" 
    xmlns:local="clr-namespace:Controls" x:Class="Controls.FilterControl" 
    mc:Ignorable="d" 
    d:DesignHeight="45" d:DesignWidth="140"> 

    <StackPanel x:Name="LayoutRoot"> 
     <sdk:Label x:Name="LblFilterDescription" Content="-" /> 
     <ComboBox x:Name="Filter" Width="120" ItemsSource="{Binding AvailableFilters, RelativeSource={RelativeSource FindAncestor, AncestorType=local:FilterControl}}"> 
      <ComboBox.ItemTemplate> 
       <DataTemplate> 
        <CheckBox Content="{Binding Path=Text}" IsChecked="{Binding Path=IsChecked, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Checked="FilterChanged" Unchecked="FilterChanged" /> 
       </DataTemplate> 
      </ComboBox.ItemTemplate> 
     </ComboBox> 
    </StackPanel> 
</UserControl> 

FilterControl.xaml.cs:

public partial class FilterControl : UserControl 
{ 
    public delegate void FilterChangedHandler(object sender); 
    public event FilterChangedHandler OnFilterChanged; 

    public ObservableCollection<FilterValue> AvailableFilters { get; set; } 
    public List<string> AppliedFilters 
    { 
     get 
     { 
      return new List<string>(AvailableFilters.Where(filter => filter.IsChecked).Select(filter => filter.Text)); 
     } 
    } 

    public FilterControl() 
    { 
     InitializeComponent(); 

     AvailableFilters = new ObservableCollection<FilterValue>(); 
    } 

    public bool AddFilterValue(string filterValue) 
    { 
     bool found = false; 

     foreach (FilterValue f in AvailableFilters) 
     { 
      if (f.Text == filterValue) 
      { 
       found = true; 
       break; 
      } 
     } 

     if (!found) 
      AvailableFilters.Add(new FilterValue() { IsChecked = false, Text = filterValue }); 

     return found; 
    } 

    private void FilterChanged(object sender, RoutedEventArgs e) 
    { 
     //Here if I check AvailableFilters, the value is not changed yet. 
     //PropertyChanged allways fires after this, what makes me unable to 
     //get all currently applied filters (checked items)... 
    } 
} 

FilterValue:

public class FilterValue : INotifyPropertyChanged 
{ 
    private bool _IsChecked; 
    private string _Text; 

    public bool IsChecked 
    { 
     get { return _IsChecked; } 
     set 
     { 
      _IsChecked = value; 
      PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("IsChecked")); 
     } 
    } 
    public string Text 
    { 
     get { return _Text; } 
     set 
     { 
      _Text = value; 
      PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Text")); 
     } 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 
} 

答えて

2

だから私はこの現象を再現しようとしたとして、私はそれを実現これはSilverlightの場合と同じようにしか発生しない動作です。 WPFでこの例を試した場合、はの後にの後にバインドされたプロパティが更新されます。したがって、FilterChangedメソッドのAppliedFiltersプロパティにアクセスするだけで、実際の現在の状況が反映されます。しかしSilverlight上ではそれほど多くはありません。さらに悪いことに、この行動は私にとって一貫しているようにも見えませんでした。私は、イベントが更新されたの後にプロパティが更新された(予期される出力をもたらした)状況に遭遇しました。

これを回避する方法は、コンポーネントロジックをクリーンアップすることです。それを見ると、イベントドリブンUIロジックとデータバインディングの2つの異なるコンセプトが混在しています。もちろん、それを「適切に」行うことは、既存のプロジェクトでは保証できない複数の効果がありますが、ここで正しい方向に進み、この問題も解決する必要があります。

あなたのロジックはデータバインディングを使用してビューのデータを提供し、表示されたアイテムの変更を反映します。しかし、アイテムレベルでイベントを使用して、前の変更に応じて追加のロジックを実行しています。すでに説明したように、実行順序はプラットフォーム間で保証されているわけではないので、頼りにする必要はありません。

この場合、データを真実のソースにして、適用されたフィルタが変更されたときのデータを変更する必要があります。あなたは既にObservableCollectionと、INotifyPropertyChangedを実装しているアイテムを持っています。残念ながら、観察可能なコレクションは、コレクションの変更についてのみ通知しますが、含まれるアイテムの変更については通知しません。しかし、コレクション内のアイテムを見るようにコレクションを拡張する複数のソリューションもあります。

This related questionは正確にそのトピックをカバーしており、正確にその動作のために観察可能なコレクションを拡張する方法については複数のアイデアがあります。私の場合は、FullyObservableCollection implementation by Bob Sammersを使用しています。

あなたはそれのためにしなければならないすべてはFullyObservableCollection<FilterValue>にあなたのObservableCollection<FilterValue>を変更し、ItemPropertyChangedイベントをサブスクライブすることです:そのイベントハンドラで

AvailableFilters = new FullyObservableCollection<FilterValue>(); 
AvailableFilters.ItemPropertyChanged += AvailableFilters_ItemPropertyChanged; 

、あなたはその後、正しく、適切な行動が表示されます。

+0

私はリンクされたObservableCollectionに同様のアプローチを使用しました。これは、ちょっとしたやりかたの後で私の問題を解決しました。将来のプロジェクトにも非常に役立ちます、ありがとう! –

関連する問題