Matt Vukomanovic
Matt Vukomanovic

Reputation: 1440

String DependencyProperty to Nullable

I want to make a string DependencyProperty which can handle assigning to a nullable number value (like the textbox TextProperty does).

I basically copied the decompiled code from TextBox and changed it for the DependencyProperty I was creating.

[DefaultValue(null)]
//[DefaultValue(string.Empty)]
[Localizability(LocalizationCategory.Text)]
public string Number
{
    get { return (string)GetValue(NumberProperty); }
    set { SetValue(NumberProperty, value); }
} 

public static readonly DependencyProperty NumberProperty =
        DependencyProperty.Register(
                "Number", // Property name
                typeof(string), // Property type
                typeof(NumberControl), // Property owner 
                new FrameworkPropertyMetadata( // Property metadata
                        null, // default value 
                        //string.Empty, // default value
                        FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | // Flags 
                            FrameworkPropertyMetadataOptions.Journal,
                        OnNumberPropertyChanged,    // property changed callback 
                        CoerceText,
                        true, // IsAnimationProhibited
                        UpdateSourceTrigger.PropertyChanged   // DefaultUpdateSourceTrigger
                        ));
private static object CoerceText(DependencyObject d, object value)
{
    var sv = value as string;
    if (sv == String.Empty) return null;
    return value;
    //if( value == null) return string.Empty;
}

private static void OnNumberPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    var tb = (NumberControl) d;
    if (tb.Number != tb.Text)
    {
        tb.Text = tb.Number;
    }
}

However when binding this property to a decimal? this is the exception which occurs

System.Windows.Data Error: 7 : ConvertBack cannot convert value '' (type 'String'). BindingExpression:Path=DecimalProperty; DataItem='MainWindowViewModel' (HashCode=58431997); target element is 'NumberControl' (Name=''); target property is 'Number' (type 'String') FormatException:'System.FormatException: Input string was not in a correct format.
at System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)
at System.Number.ParseDecimal(String value, NumberStyles options, NumberFormatInfo numfmt)
at System.Convert.ToDecimal(String value, IFormatProvider provider)
at System.Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider)
at System.Windows.Data.BindingExpression.ConvertBackHelper(IValueConverter converter, Object value, Type sourceType, Object parameter, CultureInfo culture)'

Note: I tried leaving the default string.empty behaviour which didn't work, and the code I've provided tries null which also doesn't work

Is there some way that TextBox.Text has specified some other default converter?

Upvotes: 1

Views: 1048

Answers (1)

Matt Vukomanovic
Matt Vukomanovic

Reputation: 1440

Quick Answer

You need to set the DependancyProperty directly to null with in the PropertyChangedCallback method specified for that DependancyProperty so that it doesn't use the default converter.

So in my case the OnNumberPropertyChanged method which is being used as the PropertyChangedCallback in the DependancyProperty needed to call

tb.SetValue(NumberProperty, null);

Long Answer

Damn I found this question and answer after posting my question, that question wasn't really phrased in a way that my searching came up with until I'd posted "default converter" at the end of my question.

The way I got it working was to manually set the DependancyProperty value to null at the appropriate time with the code:

    if (tb.Number == null)
    {
        tb.SetValue(NumberProperty, null);
    }

So the full OnNumberPropertyChanged method is:

    /// <summary> 
    /// Callback for changes to the Number property 
    /// </summary>
private static void OnNumberPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    var tb = (NumberControl) d;
    if (tb.Number != tb.Text)
    {
        tb.Text = tb.Number;
    }
    if (tb.Number == null)
    {
        tb.SetValue(NumberProperty, null);
    }
}

Upvotes: 2

Related Questions