Why is the binding property not set when binding to it?

I have two collections displayed in my WPF application, and I would like the elements from one of them to be disabled in the other. By doing this, I create a custom FilteringListBox that inherits the ListBox, and I want to add some processing to it to disable the items set in the collection set through the FilteringListBox property. Now my problem is that the dependency property that takes the ObservableCollection that I want to filter from the elements is not set, even if I bind to it in xaml.

I created a simplified application where I reproduce the problem. Here is my 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>

This is my custom component - only the Dependency property is currently supported:

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

:

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

DependencyProperty FilteringCollection FilteringListBox, . ? ?

+3
3

. , PropertyChangedCallback DependencyProperty.

+5

MSDN , PropertyChangedCallback

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

The get and set properties are never used directly by the WPF framework. You provide them for convenience only. Instead, you need to add a callback to your registration of dependency properties. A callback will be called when the value is bound to a dependency property. Therefore, your code for FilteredListBoxshould be changed to something similar to the following:

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);
        }
    }
}
+1
source

Source: https://habr.com/ru/post/1763797/


All Articles