12

I have a usercontrol with a dependency property.

public sealed partial class PenMenu : UserControl, INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }         

    public bool ExpandCollapse
    {
        get
        {
            return false;
        }

        set
        {
            //code
        }
    }
public static readonly DependencyProperty ExpandCollapseProperty = DependencyProperty.Register("ExpandCollapse", typeof(bool), typeof(PenMenu), null);
//some more code
}

And I am assigning value in XAML page as:

<Controls:PenMenu x:Name="penMenu" Opened="Menu_Opened" 
                         ExpandCollapse="{Binding PenMenuVisible}" />

But it is not hitting GET-SET part of ExpandCollapse property in the usercontrol. So I added bool to bool converter just to check what value is being passed with binding like:

<Controls:PenMenu x:Name="penMenu" Opened="Menu_Opened" 
                         ExpandCollapse="{Binding PenMenuVisible, Converter={StaticResource booleanToBooleanConverter}}" />

And with breakpoint in Converter, I see the value being passed is correct. What is the possible reason it's not assigned to the Dependency Property?

Also in XAML page if I say:

<Controls:PenMenu x:Name="penMenu" Opened="Menu_Opened" 
                         ExpandCollapse="true"/>

then it hits the GET-SET part of ExpandCollapse property in the usercontrol. I am stuck. This is weird. Please help.

Jerry Nixon
  • 31,313
  • 14
  • 117
  • 233
ashish nirkhe
  • 689
  • 3
  • 10
  • 22
  • Common problem. Sorry you are having it. See my answer below. – Jerry Nixon Nov 19 '13 at 20:53
  • For developers finding this, please note that when the framework updates a dependency property it does NOT use the CLR properties, it reads and writes to the dp directly, which is why you must use the changed event. – Jerry Nixon Feb 16 '18 at 07:22

2 Answers2

23

It's frustrating isn't it? First, include a changed event handler. Like this:

public string Title
{
    get { return (string)GetValue(TitleProperty); }
    set { SetValue(TitleProperty, value); }
}
public static readonly DependencyProperty TitleProperty =
    DependencyProperty.Register("Title", typeof(string), 
    typeof(MyControl), new PropertyMetadata(string.Empty, Changed));
private static void Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    var c = d as MyControl;
    // now, do something
}

Then, please read this article so you see there are more gotchas than just that one: http://blog.jerrynixon.com/2013/07/solved-two-way-binding-inside-user.html

Best of luck!

Jerry Nixon
  • 31,313
  • 14
  • 117
  • 233
  • Excellent! That thingi worked. But the example on MSDN [http://msdn.microsoft.com/library/windows/apps/br242362] showed passing null in PropertyMetaData. I think that works without Binding... Thanks a lot Jerry :) – ashish nirkhe Nov 19 '13 at 21:33
  • Why, why the f*** is that? I spent days on this problem. Why is setting the DataContext of the UserControl itself behaving like that? – Timo Feb 28 '17 at 15:33
  • 1
    Oh my... Thanks for your blog post! That helped... somehow. – j00hi Jan 03 '18 at 10:27
5

The getter and setter of a dependency property are not guaranteed to be run, and in particular the WPF binding engine / XAML processor is documented to bypass these. Have a look on MSDN - the getter/setter should just be a wrapper around GetValue/SetValue on the DependencyProperty itself.

Instead of reacting in the setter of your property, you should add a property changed handler in the original call to DependencyProperty.Register, when you can act on the new value.

(see other questions).

Community
  • 1
  • 1
Nicholas W
  • 2,231
  • 1
  • 14
  • 15
  • Thank you Nicholas, that explains the base :) – ashish nirkhe Nov 19 '13 at 21:36
  • Adding to the confusion, the CLR properties WILL be used if you access them from code-behind, but not from XAML. So you can have cases where it seems to work inconsistently. This answer appears to state the best practice to avoid this problem entirely. Make the CLR property ONLY a wrapper around the dependency property. – StayOnTarget Apr 05 '19 at 14:46