2010-09-08 3 views
3

J'ai deux collections affichées dans mon application WPF, et j'aimerais que les éléments de l'une d'entre elles soient désactivés dans l'autre. Pour ce faire, je crée un contrôle personnalisé FilteringListBox qui hérite de ListBox, et je veux ajouter une manipulation à l'intérieur pour désactiver les éléments qui sont définis dans un ensemble de collection à travers une propriété sur le FilteringListBox. Maintenant, mon problème est que la propriété de dépendance prenant le ObservableCollection que je veux filtrer des éléments n'est pas définie - même si je me lie à elle dans le xaml.Pourquoi ma propriété de dépendance n'est-elle pas définie lors de la liaison?

J'ai créé une application simplifiée où je reproduis le problème. Voici mon Xaml:

<StackPanel> 
    <StackPanel Orientation="Horizontal"> 
     <StackPanel Orientation="Vertical"> 
      <TextBlock>Included</TextBlock> 
      <ListBox x:Name="IncludedFooList" ItemsSource="{Binding IncludedFoos}"></ListBox> 
     </StackPanel> 
     <Button Margin="10" Click="Button_Click">Add selected</Button> 
     <StackPanel Orientation="Vertical"> 
      <TextBlock>Available</TextBlock> 
      <Listbox:FilteringListBox x:Name="AvailableFooList" ItemsSource="{Binding AvailableFoos}" FilteringCollection="{Binding IncludedFoos}"></Listbox:FilteringListBox> 
     </StackPanel>     
    </StackPanel>    
</StackPanel> 

Ceci est mon composant personnalisé - qui détient actuellement seulement la propriété Dépendance:

public class FilteringListBox : ListBox 
{ 
    public static readonly DependencyProperty FilteringCollectionProperty = 
     DependencyProperty.Register("FilteringCollection", typeof(ObservableCollection<Foo>), typeof(FilteringListBox));             

    public ObservableCollection<Foo> FilteringCollection 
    { 
     get 
     { 
      return (ObservableCollection<Foo>)GetValue(FilteringCollectionProperty); 
     } 
     set 
     { 
      SetValue(FilteringCollectionProperty, value); 
     } 
    } 
} 

Et pour le code complet du code sous-jacent et les définitions de classe sont ici:

public partial class MainWindow : Window 
{ 
    private MainViewModel _vm; 

    public MainWindow() 
    { 
     InitializeComponent(); 
     _vm = new MainViewModel(); 
     DataContext = _vm; 
    } 

    private void Button_Click(object sender, RoutedEventArgs e) 
    { 
     if (AvailableFooList.SelectedItem == null) 
      return; 
     var selectedFoo = AvailableFooList.SelectedItem as Foo; 
     _vm.IncludedFoos.Add(selectedFoo); 
    } 
} 

public class MainViewModel 
{ 
    public MainViewModel() 
    { 
     IncludedFoos = new ObservableCollection<Foo>(); 
     AvailableFoos = new ObservableCollection<Foo>(); 
     GenerateAvailableFoos(); 
    } 

    private void GenerateAvailableFoos() 
    { 
     AvailableFoos.Add(new Foo { Text = "Number1" }); 
     AvailableFoos.Add(new Foo { Text = "Number2" }); 
     AvailableFoos.Add(new Foo { Text = "Number3" }); 
     AvailableFoos.Add(new Foo { Text = "Number4" }); 
    } 

    public ObservableCollection<Foo> IncludedFoos { get; set; } 
    public ObservableCollection<Foo> AvailableFoos { get; set; } 
} 

public class Foo 
{ 
    public string Text { get; set; } 
    public override string ToString() 
    { 
     return Text; 
    } 
} 

J'ajoute des points d'arrêt au setter et au getter de DependencyProperty FilteringCollection dans FilteringListBox, mais il n'est jamais déclenché. Pourquoi? Comment puis-je le réparer?

Répondre

5

Le système de liaison contourne l'ensemble et obtient des accesseurs pour les propriétés de dépendance. Si vous souhaitez exécuter du code lorsqu'une propriété de dépendance change, vous devez ajouter un PropertyChangedCallback à la définition DependencyProperty.

+1

+1. J'aimerais que ce soit plus simple ... une meilleure convention pour créer DependancyProperties ... mais c'est ce que nous devons vivre avec. Le getter/setter est simplement une commodité pour le développeur mais n'a aucune signification pour la liaison de données. C'est en fait l'inverse. Le système de liaison appelle 'GetValue' et' SetValue' et la propriété le fait sans liaison. L'événement est déclenché lorsque 'SetValue' est appelé. Il craint que cela soit si confus. –

+0

Hmm .. Pas très intuitif ou pratique, mais cela l'a réparé. Merci! – stiank81

1

MSDN a une section sur Dependency Property Callbacks and Validation, vous devez vous inscrire un PropertyChangedCallback

Exemple de msdn

public static readonly DependencyProperty AquariumGraphicProperty 
= DependencyProperty.Register(
    "AquariumGraphic", 
    typeof(Uri), 
    typeof(AquariumObject), 
    new FrameworkPropertyMetadata(null, 
     FrameworkPropertyMetadataOptions.AffectsRender, 
     new PropertyChangedCallback(OnUriChanged) 
) 
); 

private static void OnUriChanged(DependencyObject d, 
           DependencyPropertyChangedEventArgs e) { 
    Shape sh = (Shape) d; 
    sh.Fill = new ImageBrush(new BitmapImage((Uri)e.NewValue)); 
} 
1

Les obtenir et définir les propriétés ne sont jamais utilisées directement par le cadre WPF. Vous ne les fournissez que pour votre confort. Au lieu de cela, vous devez ajouter un rappel à votre enregistrement de propriété de dépendance. Le rappel sera appelé lorsqu'une valeur est liée à la propriété de dépendance. Par conséquent, votre code pour FilteredListBox doit être remplacé par quelque chose de similaire au suivant:

public partial class FilteringListBox : ListBox 
{ 
    public static readonly DependencyProperty FilteringCollectionProperty = 
     DependencyProperty.Register("FilteringCollection", typeof(ObservableCollection<Foo>), typeof(FilteringListBox), 
     new PropertyMetadata(null, FilteringCollectionPropertyCallback)); 

    static void FilteringCollectionPropertyCallback(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
     FilteringListBox listbox = d as FilteringListBox; 
     // Do some work here 
    } 

    public ObservableCollection<Foo> FilteringCollection 
    { 
     get 
     { 
      return (ObservableCollection<Foo>) GetValue(FilteringCollectionProperty); 
     } 
     set 
     { 
      SetValue(FilteringCollectionProperty, value); 
     } 
    } 
} 
+0

Vous réalisez que la valeur par défaut est maintenue statiquement et dans ce cas est utilisée par référence? Chaque FilteringListBox fera désormais référence à la même instance de ObservableCollection à moins que la valeur ne soit surchargée. – Bubblewrap

+0

Bon point. La valeur par défaut n'est pas nécessaire dans ce cas. Il est remplacé par la liaison au viewmodel. –

Questions connexes