In my WinForms application I implemented custom data binding with support for value converters, similar to WPF.
The sample has a new binding class that derives from Binding and allows to attach a custom converter:
public class CustomBinding : Binding
{
private readonly IValueConverter _converter;
private readonly object _converterParameter;
private readonly CultureInfo _converterCulture;
public CustomBinding(string propertyName, object dataSource, string dataMember, IValueConverter valueConverter, CultureInfo culture, object converterParameter = null)
: base(propertyName, dataSource, dataMember)
{
if (valueConverter != null)
this._converter = valueConverter;
this.DataSourceUpdateMode = DataSourceUpdateMode.OnPropertyChanged;
this.FormattingEnabled = false;
this._converterCulture = culture;
this._converterParameter = converterParameter;
}
public CustomBinding(string propertyName, object dataSource, string dataMember, IValueConverter valueConverter, object converterParameter = null)
: base(propertyName, dataSource, dataMember)
{
if (valueConverter != null)
this._converter = valueConverter;
this._converterCulture = Thread.CurrentThread.CurrentUICulture;
this._converterParameter = converterParameter;
}
protected override void OnFormat(ConvertEventArgs cevent)
{
if (this._converter != null)
{
var converterdValue = this._converter.Convert(cevent.Value, cevent.DesiredType, _converterParameter, _converterCulture);
cevent.Value = converterdValue;
}
else base.OnFormat(cevent);
}
protected override void OnParse(ConvertEventArgs cevent)
{
if (this._converter != null)
{
var converterdValue = this._converter.ConvertBack(cevent.Value, cevent.DesiredType, _converterParameter, _converterCulture);
cevent.Value = converterdValue;
}
else base.OnParse(cevent);
}
}
There is also the interface IValueConverter that is similar to the interface of WPF:
public interface IValueConverter
{
object Convert(object value, Type targetType, object parameter, CultureInfo culture);
object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture);
}
With this I'm able to attach an own converter to any (two-way) binding. So far I've built a TimeSpanStringValueConverter that allows me to bind a TimeSpan field to a textbox as well as a InvertBooleanValueConverter to bind the opposite of a boolean field to any boolean property of any control. They work as expected!
Now I want to bind a property of the type double? to a textbox Text field. For this I wrote this converter:
public class NullableDoubleStringValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value != null ? ((double)value).ToString(culture) : String.Empty;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string text = Regex.Replace((string)value, @"[^0-9" + culture.NumberFormat.NumberDecimalSeparator + @"]", "");
if (text == String.Empty)
{
return null;
}
double convertedValue;
if (Double.TryParse(text, NumberStyles.Any, culture, out convertedValue))
{
return (double?)convertedValue;
}
return null;
}
}
And I bind it to the textbox this way:
this.textBox1.DataBindings.Add(new CustomBinding("Text", obj, "MyProperty", new NullableDoubleStringValueConverter())); // obj is the data context object
When I set a break point in the ConvertBack method of the value converter I can clearly see how my string will be converted to a nullable double value after leaving the textbox, successfully. However, instead of my other cases, it will not update the data context object. And if I set a break point in the Convert method of my value converter I can see it will retrieve the initial value of MyProperty which is null when updating the Text of the textbox after leaving it. So, my textbox gets empty after typing any numeric value in it.
My question is: Why? And how can I make it work with nullable types (double?)? If I change the type of MyProperty in the data context object to double it will accept my changed value. Unfortunately I need the nullable support. So when leaving the textbox empty I want to store null as well as showing an empty text box, when the value was null.