为什么绑定依赖属性时它没有被设置?

3
我在我的WPF应用程序中展示了两个集合,我希望在其中一个集合中禁用另一个集合中的元素。为此,我创建了一个自定义控件FilteringListBox,它继承自ListBox,并且我想在其中添加一些处理来禁用通过FilteringListBox上的属性设置的集合中的元素。现在,我的问题是,即使我在xaml中绑定了ObservableCollection,也没有设置接收要筛选元素的依赖属性。
我创建了一个简化的应用程序来重现这个问题。以下是我的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>

这是我的自定义组件 - 目前仅包含依赖属性:

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

我在FilteringListBox中的FilteringCollection DependencyProperty的setter和getter中添加了断点,但它从未被触发。为什么?我该如何解决?
3个回答

5
绑定系统绕过了依赖属性的设置和获取访问器。如果您想在依赖属性更改时执行代码,应将 PropertyChangedCallback 添加到 DependencyProperty 定义中。

1
+1. 我希望它更简单一些...有更好的约定来创建依赖属性...但这就是我们必须面对的。Getter/Setter只是为开发人员提供方便,但对数据绑定没有意义。实际上恰恰相反。绑定系统调用GetValueSetValue,而属性只是在没有绑定的情况下执行它们。当调用SetValue时,事件被触发。很遗憾它如此令人困惑。 - Brian Genisio
嗯...不是很直观或方便,但这样解决了。谢谢! - stiank81

1

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

get和set属性从未直接被WPF框架使用。您只需要为自己提供它们作为方便。相反,您需要在依赖属性注册中添加回调。当一个值绑定到依赖属性时,回调将被调用。因此,您的FilteredListBox代码应该更改为类似以下内容:

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

你意识到默认值是静态保存的,并且在这种情况下被引用使用吗?除非被覆盖,否则每个 FilteringListBox 现在将默认引用 ObservableCollection<Foo> 的同一实例。 - Bubblewrap
好的观点。在这种情况下,默认值是不必要的。它被绑定到视图模型覆盖了默认值。 - Jakob Christensen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接