locked
How to catch a DependencyProperty value changed event, or why doesn't PropertyChangedCallback fire? RRS feed

  • Question

  • Hi

    Im developing some UserControls and have implemented DependencyProperties to use them in master-details scenarios, but I can't seem to catch any change events. All ViewModels have an int FilterID property with INotifyPropertyChanged, and all UserControls have a DependencyProperty FilterID.

    My initial Page has two UserControls where PeopleUserControl is the Master and AddressesUserControl is the Child. PeopleUserControl is bound to FilterID in the PeopleViewModel, while AddressesUserControl's FilterID is bound to the PeopleUserControl (element binding).

    Whenever SelectedItem changes in PeopleUserControl I update the PeopleViewModel's FilterID, this automatically updates the DependencyProperty in AddressesUserControl as well, but I can't catch the setting event. Neither the PropertyChangedCallback method or the property setter seems to fire.

    From the AddressesUserControl.cs:

        public const string FilterIDPropertyName = "FilterID";
        // FilterID updates correctly
        public int FilterID
        {
          get { return (int)GetValue(FilterIDProperty); }
          set { 
            SetValue(FilterIDProperty, value);
            //THIS NEVER FIRES
            vm.LoadEntities(CurrentMaster);
          }
        }
    
        public static readonly DependencyProperty FilterIDProperty = DependencyProperty.Register(
         FilterIDPropertyName,
         typeof(int), 
         typeof(AddressesUserControl),
         new PropertyMetadata(0, new PropertyChangedCallback(FilterIDChanged)));
    
        private static void FilterIDChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
          var sender = o as AddressesUserControl;
          //THIS NEVER FIRES
          sender.vm.LoadEntities(sender.CurrentMaster);
        }
    

    There are two places where I should be able to run some logic if the DependencyProperty changes, but neither fires, nor can I break on these lines. If I break on the setter for SelectedEntity in the PeopleViewModel, I can go to AddressesUserControl and the FilterID property will equal that of the PeopleViewModel.

    From PeopleViewModel:

        private Person _selectedEntity;
        public Person SelectedEntity
        {
          get { return _selectedEntity; }
          set
          {
            _selectedEntity = value;
            NotifyPropertyChanged("SelectedEntity");
            // Updates local prop and DependencyProperty in UserControls
            FilterID = _selectedEntity.PersonID;
            SaveCommand.OnCanExecuteChanged();
            DeleteCommand.OnCanExecuteChanged();
          }
        }
    
    

    What is going on?

    TIA

    Dennis

    Tuesday, February 15, 2011 4:07 AM

Answers

  • First off, you can not put code in the getter/setter of a DependencyProperty.  Secondly, your issue probably resides in code you have not provided.  If possible please provide a sample application that easily duplicates your issue.
    • Marked as answer by Min Zhu Thursday, February 24, 2011 5:11 AM
    Tuesday, February 15, 2011 4:39 AM
  • Like Brian told you, you cannot add code to the SetValue of a dependency property. The dependency property getter and setter are just dummy values, and only called when you manually call them. WPF internally directly sets the value of the dependency property (and thus never uses the setter).

    You need to add an property changed callback in the declaration of the dependency property. I see that you have correctly done this in PeopleUserControl now. Normally, user controls bind two-way by default. However, if you create a custom user control, you have to take care of this manually.

    In the definition of the dependency property, use this:

        public static readonly DependencyProperty FilterIDProperty = DependencyProperty.Register(
         FilterIDPropertyName,
         typeof(int),
         typeof(PeopleUserControl),
         new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, FilterIDChanged));
    
    Notice the last line, where I use FrameworkPropertyMetadata instead of PropertyMetadata so I can define the BindsTwoWayByDefault.


    Geert van Horrik - CatenaLogic
    Visit my blog: http://blog.catenalogic.com

    Looking for an MVVM framework for WPF and Silverlight? Check out Catel!
    • Marked as answer by Min Zhu Thursday, February 24, 2011 5:11 AM
    Wednesday, February 16, 2011 1:13 PM

All replies

  • First off, you can not put code in the getter/setter of a DependencyProperty.  Secondly, your issue probably resides in code you have not provided.  If possible please provide a sample application that easily duplicates your issue.
    • Marked as answer by Min Zhu Thursday, February 24, 2011 5:11 AM
    Tuesday, February 15, 2011 4:39 AM
  • Please, either subscribe the only issue you have, OR put it all into a zip file and upload it (for example to SkyDrive).
    Geert van Horrik - CatenaLogic
    Visit my blog: http://blog.catenalogic.com

    Looking for an MVVM framework for WPF and Silverlight? Check out Catel!
    Tuesday, February 15, 2011 11:15 AM
  • Yes, a zip file with a link would have been the preferred method of providing an example.  You need to check all your bindings.  In your PeopleView on the AddressesUserControl you have set a Path for the FilterId property.  You probably need to add FilterId={Binding Path=FilterId, ElementName=.....  Double check all you bindings.
    Tuesday, February 15, 2011 2:37 PM
  • Wednesday, February 16, 2011 12:58 PM
  • Like Brian told you, you cannot add code to the SetValue of a dependency property. The dependency property getter and setter are just dummy values, and only called when you manually call them. WPF internally directly sets the value of the dependency property (and thus never uses the setter).

    You need to add an property changed callback in the declaration of the dependency property. I see that you have correctly done this in PeopleUserControl now. Normally, user controls bind two-way by default. However, if you create a custom user control, you have to take care of this manually.

    In the definition of the dependency property, use this:

        public static readonly DependencyProperty FilterIDProperty = DependencyProperty.Register(
         FilterIDPropertyName,
         typeof(int),
         typeof(PeopleUserControl),
         new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, FilterIDChanged));
    
    Notice the last line, where I use FrameworkPropertyMetadata instead of PropertyMetadata so I can define the BindsTwoWayByDefault.


    Geert van Horrik - CatenaLogic
    Visit my blog: http://blog.catenalogic.com

    Looking for an MVVM framework for WPF and Silverlight? Check out Catel!
    • Marked as answer by Min Zhu Thursday, February 24, 2011 5:11 AM
    Wednesday, February 16, 2011 1:13 PM
  • Hi Dennis,

     

    Just checking in to see if the information was helpful. Please let us know if you would like further assistance.

    Have a great day!

     


    Min Zhu [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Monday, February 21, 2011 1:54 AM
  • Hi Dennis,

    You can override OnPropertyChanged for a DependencyObject . In this method you can verify if a Property change has happened to the said dependency property. Please see this:

    http://msdn.microsoft.com/en-us/library/system.windows.dependencyobject.onpropertychanged.aspx

    To debug binding, you can enable Trace messages for Binding to Output Window.

    http://msdn.microsoft.com/en-us/library/dd409960.aspx

    It is possible that while loading the view the SelectedItem is null which is causing exception as FilterId is an int based property. You can verify that by changing the type to Nullable (int? ) instead.

    Basically WPF runtime never uses the setter and getter you provide in your code for Dependency properties. But it directly calls the SetValue and GetValue so the code that you want here is not guaranteed execution.

    http://en.csharp-online.net/WPF_Concepts%E2%80%94Dependency_Property_Implementation

    This functionality is generally implemented using DepdencyPropertyDescriptor.AddValueChanged or by overrideing DependencyObject.OnPropertyChanged :

    http://msdn.microsoft.com/en-us/library/system.componentmodel.dependencypropertydescriptor.aspx

    Thanks,
    Muhammad
    shujaatsiddiqi.blogspot.com


    Muhammad Siddiqi
    Monday, February 21, 2011 4:22 AM