【问题标题】:Why isn't my dependency property set when binding to it?为什么绑定时我的依赖属性没有设置?
【发布时间】:2010-09-08 13:27:45
【问题描述】:

我的 WPF 应用程序中显示了两个集合,我希望在另一个中禁用其中一个集合中的元素。这样做我正在创建一个继承 ListBox 的自定义控件 FilteringListBox,并且我想在其中添加一些处理以禁用通过 FilteringListBox 上的属性设置在集合集中的元素。现在,我的问题是没有设置获取我想要从中过滤元素的 ObservableCollection 的依赖属性——即使我在 xaml.xml 中绑定到它也是如此。

我创建了一个简化的应用程序来重现问题。这是我的 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 中的 DependencyProperty FilteringCollection 的 setter 和 getter 中添加了断点,但它从未被触发。为什么?我该如何解决?

【问题讨论】:

    标签: .net wpf dependency-properties


    【解决方案1】:

    绑定系统绕过依赖属性的 set 和 get 访问器。如果要在依赖属性更改时执行代码,则应在 DependencyProperty 定义中添加 PropertyChangedCallback

    【讨论】:

    • +1。我希望它更简单......创建 DependancyProperties 的一些更好的约定......但这是我们必须忍受的。 getter/setter 对开发人员来说只是一种方便,但对数据绑定没有任何意义。它实际上是相反的。绑定系统调用GetValueSetValue 并且属性只是在没有绑定的情况下执行它。调用 SetValue 时会触发该事件。太令人困惑了。
    • 嗯.. 不是很直观或方便,但解决了它。谢谢!
    【解决方案2】:

    MSDN有一个关于Dependency Property Callbacks and Validation的部分,需要注册一个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));
    }
    

    【讨论】:

      【解决方案3】:

      WPF 框架从不直接使用 get 和 set 属性。您只是为自己提供便利。相反,您需要向依赖项属性注册添加回调。当值绑定到依赖属性时,将调用回调。因此,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 实例,除非该值被覆盖。
      • 好点。在这种情况下,默认值是不必要的。它被与视图模型的绑定覆盖。
      猜你喜欢
      • 2021-07-31
      • 2012-01-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-26
      • 1970-01-01
      • 1970-01-01
      • 2010-12-15
      相关资源
      最近更新 更多