首页 > 解决方案 > Data binding attached property to view model fails

问题描述

I created an attached property for ListBox like this:

using ListBoxControl = System.Windows.Controls.ListBox;

namespace App.Ui.Views.AttachedProperties
{
    public class ListBox
    {
        public static readonly DependencyProperty autoScrollProperty =
            DependencyProperty.RegisterAttached(
                "AutoScroll", 
                typeof(bool), 
                typeof(ListBoxControl), 
                new PropertyMetadata(false));

        public static void SetAutoScroll(ListBoxControl element, bool value)
        {
            element.SetValue(autoScrollProperty, value);
            if (value)
            {
                element.SelectionChanged += Element_SelectionChanged;
            }
            else
            {
                element.SelectionChanged -= Element_SelectionChanged;
            }
        }

        public static bool GetAutoScroll(ListBoxControl element)
        {
            return (bool)element.GetValue(autoScrollProperty);
        }

        private static void Element_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            var listBox = (ListBoxControl)sender;
            listBox.ScrollIntoView(listBox.SelectedItem);
        }
    }
}

When I use a static True/False value in the xaml, it works fine:

<ListBox ap:ListBox.AutoScroll="True">
    ...
</ListBox>

But if I data bind to a property in my view model:

<ListBox ap:ListBox.AutoScroll="{Binding Path=Settings.EnableAutoScroll}">
    ...
</ListBox>

Then I get the following exception: A 'Binding' cannot be set on the 'SetAutoScroll' property of type 'ListBox'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.

Is this possible, or am I going to need to derive my own custom list box to accomplish this?

标签: c#.netwpfdata-bindingattached-properties

解决方案


这一行有问题typeof(ListBoxControl)。您应该指定自定义附加属性所在的类的名称。

我建议将类从 ListBox 重命名为 ListBoxExtensions,同时将其设为静态。然后您不必使用别名 ListBoxControl。
您的最终代码将如下所示:

public static class ListBoxExtensions
{
    public static readonly DependencyProperty autoScrollProperty =
                DependencyProperty.RegisterAttached(
                    "AutoScroll", 
                    typeof(bool), 
                    typeof(ListBoxExtensions), 
                    new PropertyMetadata(false));

      ...
}

编辑: 好的,您的代码还有另一个问题。
从 setter ( SetAutoScroll) 中移除监听器的附件,并将此逻辑放入依赖属性回调中。

public static class ListBoxExtensions
{
    public static readonly DependencyProperty autoScrollProperty =
        DependencyProperty.RegisterAttached(
            "AutoScroll",
            typeof(bool),
            typeof(ListBoxExtensions),
            new PropertyMetadata(false, AutoScrollChangedCallback));

    public static void SetAutoScroll(ListBox element, bool value)
    {
        element.SetValue(autoScrollProperty, value);
    }

    public static bool GetAutoScroll(ListBox element)
    {
        return (bool)element.GetValue(autoScrollProperty);
    }

    private static void AutoScrollChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        ListBox control = (ListBox)d;

        if ((bool)e.NewValue)
        {
            control.SelectionChanged += Element_SelectionChanged;
        }
        else
        {
            control.SelectionChanged -= Element_SelectionChanged;
        }
    }

    private static void Element_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        var listBox = (ListBox)sender;
        listBox.ScrollIntoView(listBox.SelectedItem);
    }
}

推荐阅读