2017-07-06 78 views
1

我有一个自定义控制,以显示与一个ComboBox内复选框项目。为了实现这一点,我使用了DataTemplateCheckBoxComboBoxItemSource使用绑定到包含我的过滤器值的ObserableCollection<FilterValue>FilterValue是一个实现INotifyPropertyChanged的自定义类。 CheckBox的属性ContentIsChecked也使用绑定来使用我的列表的值。此控件将用于Silverlight。选中复选框事件触发之前势必收集更新

结合自身工作正常,因为在这里看到:
Binding itself works fine, as seen here:

当我注册CheckedUnchecked事件出现的问题。

只要复选框一个改变其状态,如预期的事件被触发,但在这一刻,在绑定列表中的值仍然没有更新。 我看到了什么,而调试的是,Checked/Unchecked事件在PropertyChanged事件FilterValue之前发射。 这意味着在事件发生的时候,我不能要求列表中的所有活动(选中)过滤器。我能做些什么来实现这一目标?

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上尝试此示例,Changed在之后触发,绑定属性将更新。所以你可以访问FilterChanged方法中的AppliedFilters属性,它将反映实际的现状。尽管如此,Silverlight并没有太多。更糟糕的是,这种行为似乎并不符合我的观点。我确实遇到过这样的情况:在之后,该事件已经被更新(导致预期的输出),结果该事件被触发

解决此问题的一种方法是清理组件逻辑。如果你看看它,你就会混合两个不同的概念:事件驱动的UI逻辑和清晰的数据绑定。当然,“正确地”做这件事有多种效果,你可能无法确保在现有的项目中,但你至少可以尝试在正确的方向,这也应该解决这个问题。

所以你的逻辑,现在使用数据绑定为视图提供的数据,以反映显示项目的变化。但是,您正在使用项目级别的事件来根据以前的更改执行其他逻辑。正如我们所看到的,执行的顺序似乎并不是跨平台保证的,所以最好避免依赖它。

在这种情况下,你应该有你的数据是真理的来源,并在数据的变化告诉你,当应用滤镜改变。你已经有一半了ObservableCollection和实现INotifyPropertyChanged的项目。不幸的是,可观察的集合只会通知您有关集合的更改,但不会更改所包含的项目。但是有多种解决方案可以扩展集合以查看集合中的项目。

This related question正好涵盖了话题,有关于如何扩大观察集合正是出于这个行为多的想法。就我而言,我使用了FullyObservableCollection implementation by Bob Sammers

所有你需要做的因为这是改变你的ObservableCollection<FilterValue>FullyObservableCollection<FilterValue>和订阅ItemPropertyChanged事件:

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

在该事件处理程序,然后你会看到正确适当的行为。

+0

我用类似的方法来链接'ObservableCollection',解决一点修修补补后,我的问题。对未来的项目也会很有帮助,谢谢! –