Jesse
Jesse

Reputation: 975

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?

Upvotes: 0

Views: 132

Answers (1)

lezhkin11
lezhkin11

Reputation: 414

Problem in this line typeof(ListBoxControl). You should specify the name of the class where custom attached property seats.

I would recommend rename class from ListBox to ListBoxExtensions, also, make it static. Then you don't have to use alias ListBoxControl.
Your final code will look like:

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

      ...
}

Edit: OK, your code has another problem.
Remove attachment of the listener from setter (SetAutoScroll) and put this logic into dependency property callback.

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

Upvotes: 1

Related Questions