locked
List Dependency Property not triggering PropertyChangedCallback

    Question

  • I have a UserControl that I'm trying to build a stackpanel in its XAML based on a List property but I can't build the stackpanel because when I add items to "Items" it doesn't call the PropertyChangedCallback.

        public sealed partial class ScrollControl : UserControl
        {
    
            public List<string> Items
            {
                get { return (List<string>)GetValue(ItemsProp); }
                set { SetValue(ItemsProp, value); }
            }
    
            public void AddItem(string str)
            {
                this.Items.Add(str);
            }
    
            public ScrollControl()
            {
                this.Items = new List<string>();
                this.InitializeComponent();
    
            }
    
            public static DependencyProperty ItemsProp = DependencyProperty.Register(
            "Items",
            typeof(List<string>),
            typeof(ScrollControl),
            new PropertyMetadata(
                new List<string>(),
                new PropertyChangedCallback(onItemsListChanged)
                )
            );
    
            private static void onItemsListChanged(DependencyObject d, DependencyPropertyChangedEventArgs args)
            {
                var obj = d as ScrollControl;
                List<string> ov = args.OldValue as List<string>;
                List<string> nv = args.NewValue as List<string>;
           //STUFF
            );
    
    
    }

    The user control seems to be working. And when I call the .addItem() function it adds it to the list.  It just doesn't break the break point in onItemsListChanged so it's not changing the XAML. 

    EDIT: Actually it appears the callback doesn't work in either callback.  So that's at least good that it's consistently broken. :D
    Tuesday, April 7, 2015 6:54 AM

Answers

  • 1) Maybe I'm missing something, but I don't see any reason that an event would be bubbled to handle an .AddItem function call.

    2) Why not use ObservableCollection if you want the List to dynamically change?  That's literally what it's designed to do.

    Matt Small - Microsoft Escalation Engineer - Forum Moderator
    If my reply answers your question, please mark this post as answered.

    NOTE: If I ask for code, please provide something that I can drop directly into a project and run (including XAML), or an actual application project. I'm trying to help a lot of people, so I don't have time to figure out weird snippets with undefined objects and unknown namespaces.

    Tuesday, April 7, 2015 1:28 PM
    Moderator

All replies

  • 1) Maybe I'm missing something, but I don't see any reason that an event would be bubbled to handle an .AddItem function call.

    2) Why not use ObservableCollection if you want the List to dynamically change?  That's literally what it's designed to do.

    Matt Small - Microsoft Escalation Engineer - Forum Moderator
    If my reply answers your question, please mark this post as answered.

    NOTE: If I ask for code, please provide something that I can drop directly into a project and run (including XAML), or an actual application project. I'm trying to help a lot of people, so I don't have time to figure out weird snippets with undefined objects and unknown namespaces.

    Tuesday, April 7, 2015 1:28 PM
    Moderator
  • 1) I have some additional logic that I was going to use to sanitize inputs with.

    2) Because I didn't know it existed.  ;)   Reading up on it, it sounds like exact what I needed.  Thanks!
    Tuesday, April 7, 2015 4:08 PM
  • OK,  Just posting a few lines of code.  Is this the correct approach?

    public sealed partial class ScrollControl : UserControl
    {
            
            public ObservableCollection<string> Items
            {
                get { return (ObservableCollection<string>)GetValue(ItemsProp); }
                set { SetValue(ItemsProp, value); }
            }
    
            public static DependencyProperty ItemsProp = DependencyProperty.Register(
            "Items",
            typeof(ObservableCollection<string>),
            typeof(ScrollControl),
            new PropertyMetadata(
                new ObservableCollection<string>(),
                new PropertyChangedCallback(onItemsListChanged)
                )
            );
    
            public ScrollControl()
            {
                this.Items = new ObservableCollection<string>();
                this.InitializeComponent();
            }
    
            private static void onItemsListChanged(DependencyObject d, DependencyPropertyChangedEventArgs args)
            {
                var obj = d as ScrollControl;
    
    
                if (args.OldValue != null)
                {
                    var coll = (ObservableCollection<string>)args.OldValue;
                    coll.CollectionChanged -= onItemsListCollectionChanged;
    
                }
    
                if (args.NewValue != null)
                {
                    var coll = (ObservableCollection<string>)args.NewValue;
                    coll.CollectionChanged += onItemsListCollectionChanged;
    
                }
    
      
    
                
            }
    
    
            private static void onItemsListCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
            {
    
    }
    
    }





    Tuesday, April 7, 2015 6:00 PM
  • I think this looks ok. Depends on whether or not it runs. :-)

    Matt Small - Microsoft Escalation Engineer - Forum Moderator
    If my reply answers your question, please mark this post as answered.

    NOTE: If I ask for code, please provide something that I can drop directly into a project and run (including XAML), or an actual application project. I'm trying to help a lot of people, so I don't have time to figure out weird snippets with undefined objects and unknown namespaces.

    Wednesday, April 8, 2015 12:55 PM
    Moderator
  • Ok.  It "works" in that it triggers.  But it doesn't work quite the same way.   When onItemsListCollectionChanged aka when something is added or removed the sender is just the collection.  As a result I can't modify my ScrollControl class based on the change which makes it mostly useless.   Can I pass the entire ScrollControl instead of just the ObservableCollection instead? 

    So unlike all of the other callbacks that one is kind of a black box from which nothing can enter or leave. 
    Thursday, April 9, 2015 3:12 AM
  • 1) What's the goal of this code?

    2) Can you share a project for debugging?


    Matt Small - Microsoft Escalation Engineer - Forum Moderator
    If my reply answers your question, please mark this post as answered.

    NOTE: If I ask for code, please provide something that I can drop directly into a project and run (including XAML), or an actual application project. I'm trying to help a lot of people, so I don't have time to figure out weird snippets with undefined objects and unknown namespaces.

    Thursday, April 9, 2015 1:15 PM
    Moderator
  • Here is a minimalist project.   I've commented out the functionality in the update function since it would error out (due to scope). 

    http://1drv.ms/1GPAIKH

    Oh I should mention that I did find a solution  and that is to add a lambda function instead of calling a separate function like onItemsListCollectionChanged.  But that doesn't feel like the optimal solution for maintainability.  I also worry that it might introduce a memory leak since the function then is never detached. 

            private static void onItemsListChanged(DependencyObject d, DependencyPropertyChangedEventArgs args)
            {
                var obj = d as MyUserControl;
    
                if (args.NewValue != null)
                {
                    var coll = (ObservableCollection<string>)args.NewValue;
                    coll.CollectionChanged += (sender, eventargs) =>
                        {
                            if (eventargs.Action == NotifyCollectionChangedAction.Add)
                            {
                                foreach (string s in eventargs.NewItems)
                                {
                                    TextBlock tb = new TextBlock();
                                    tb.Text = s;
                                    tb.Foreground = new SolidColorBrush(Colors.Black);
                                    obj.Body.Children.Add(tb);
                                }
                            }
                        };
    
                }
            }


    Thursday, April 9, 2015 5:37 PM