0

I have a class with a simple dependency property that I wrote with the help of the propdp snippet in Visual Studio. I derived the class from TextBox and gave it a PrePend property that I want to be a dependency property. I want it so that if the user sets PrePend to "Hello " and Text to "World", the Text getter will return "Hello World". My problem is that the code is seemingly ignoring my PrePend dependency property.

My class is here:

internal class MyTextBox : TextBox
{
    public new string Text
    {
        get { return this.PrePend + base.Text; }
        set { base.Text = value; }
    }

    public string PrePend
    {
        get { return (string)GetValue(PrePendProperty); }
        set { SetValue(PrePendProperty, value); }
    }

    // Using a DependencyProperty as the backing store for PrePend.  
    //   This enables animation, styling, binding, etc...
    public static readonly DependencyProperty PrePendProperty =
        DependencyProperty.Register("PrePend", typeof(string), 
          typeof(MyTextBox), new PropertyMetadata(""));
}

I got this code by using the propdp snippet. I only added the Text setter.

My XAML code is simply this:

<local:MyTextBox PrePend="Hello " Text="World" />

When I run the code, the screen displays "World". I put breakpoints in the Text getter and setter, and the PrePend getter and setter, but the code never breaks there.

What am I doing wrong with this simple example? Should I not use the propdp code snippet to do this? I don't understand dependency properties very well.

user2023861
  • 8,030
  • 9
  • 57
  • 86
  • Fire the PropertyChanged event? – Vincent Hubert Aug 26 '14 at 19:05
  • 1
    I'm not sure it's safe to override your `Text` value. `Text` in `TextBox` is also a Dependency Property. Reimplementing it with no property changed doesn't sound like a good idea. – Kcvin Aug 26 '14 at 19:08
  • Where should I put the PropertyChanged event? In the Text setter? Doesn't my call to the base.Text setter fire that event? Or the PrePend setter? In that case, doesn't the call to SetValue fire that event? Besides that, wouldn't the propdp snippet include this a call to PropertyChanged if it was required? – user2023861 Aug 26 '14 at 19:17

2 Answers2

1

This is "illegal" as WPF always access the DependencyProperties directly, the property is nothing more than a wrapper. That's why you should never change or put logics in the properties, they will never be used by wpf internally!

public new string Text // Bad pie!
{
    get { return this.PrePend + base.Text; } // Bad pie!
    set { base.Text = value; }
}

If you want to do something when a property changes add a OnPrePendPropertyChanged handler when declaring your dependency property.

IE:

public static readonly DependencyProperty PrePendProperty = DependencyProperty.Register("PrePend", typeof(string), typeof(MyTextBox), new PropertyMetadata(String.Empty, OnPrePendPropertyChanged ));

private static void OnPrePendPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
   var ctl = sender as MyTextBox ;
   ctl.OnPrePendChanged();
}

protected virtual void OnPrePendChanged()
{
   // do your magic here
}

You can also add handlers to existing dependency properties, like your text property there. This must be done in the static constructor of the class. This is described in this post.

static MyTextBox 
{
    DefaultStyleKeyProperty.OverrideMetadata(typeof(MyTextBox), new FrameworkPropertyMetadata(typeof(MyTextBox)));
    TextBox.TextProperty.OverrideMetadata(typeof(MyTextBox), new FrameworkPropertyMetadata(new PropertyChangedCallback(OnTextPropertyChanged)));
}

// Add your handlers as above

Personally when possible, I would rather create a Behavior for just I.E. extending functionality of controls.

In this current situation(I suspect you are experimenting?), I would consider using a std TextBox and then just bind to the Text property to a prop on the vm and do your magic there, and/or use a IValueConverter in the binding.

Hope it helps

Cheers,

Stian

Community
  • 1
  • 1
Stígandr
  • 2,874
  • 21
  • 36
  • I removed my Text setter, changed my XAML to `` and changed my Prepend setter so that it calls `SetValue(TextProperty, value);`. This way I'd expect the PrePend value "Hello " to appear. Instead I get nothing at all. The Text is never set even though I'm setting the Text via `Setvalue(...)`. When I put a breakpoint in my PrePend setter and run the program, the breapoint never trips: my PrePend setter is not getting called at all. I think something else is the problem here. – user2023861 Aug 26 '14 at 19:52
  • @user2023861 Well that will not work unless you alter the control template of the textbox to display the PrePend property instead of the Text Property. Mate do this in your viewmodel or use a behaviour instead! :) Behaviours are very easy and nice. – Stígandr Aug 26 '14 at 20:07
1

The setter is not used by WPF when changing the value due to databinding/animation updates.

If you want to respond to changes in a dependency property you can do one of two things:

  1. Add a PropertyChanged handler to the PropertyMetaData in the register call.

  2. Do not handle the change in the control, instead handle it in the ViewModel.

Emond
  • 50,210
  • 11
  • 84
  • 115
  • I ended up going with your first option. Thanks. I find it interesting that the propdp snippet that comes with Visual Studio doesn't seem to be working. Maybe I'm understanding it wrong. I had to create my own snippet that includes the PropertyChanged handler because I'll never be able to remember that boilerplate code. – user2023861 Sep 02 '14 at 20:11
  • They just didn't add the handler in the snippet. In general most dependency properties won't handle this event. Be careful, it can slow down your app when badly implemented and the property changes often – Emond Sep 02 '14 at 20:38