自定义WinForms数据绑定转换器无法处理可空类型(双?)

时间:2015-09-21 09:08:06

标签: c# .net winforms data-binding binding

在我的WinForms应用程序中,我实现了custom data binding with support for value converters,类似于WPF。

示例有一个新的绑定类,它派生自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); } } 并允许附加自定义转换器:

IValueConverter

还有接口public interface IValueConverter { object Convert(object value, Type targetType, object parameter, CultureInfo culture); object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture); } ,类似于WPF的接口:

TimeSpanStringValueConverter

有了这个,我就可以将自己的转换器连接到任何(双向)绑定。到目前为止,我已经构建了一个TimeSpan,它允许我将InvertBooleanValueConverter字段绑定到文本框以及double?以将布尔字段的反面绑定到任何布尔属性。控制。他们按预期工作!

现在我想将Text类型的属性绑定到文本框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; } } 字段。为此,我写了这个转换器:

this.textBox1.DataBindings.Add(new CustomBinding("Text", obj, "MyProperty", new NullableDoubleStringValueConverter())); // obj is the data context object

我用这种方式将它绑定到文本框:

ConvertBack

当我在值转换器的Convert方法中设置断点时,我可以清楚地看到在成功离开文本框后我的字符串将如何转换为可以为空的双值。但是,它不会更新数据上下文对象,而不是其他情况。如果我在我的值转换器的MyProperty方法中设置了一个断点,我可以看到它将检索Text的初始值,该值在离开后更新文本框的double?时为空它。因此,在输入任何数值后,我的文本框变空。

我的问题是:为什么?我如何才能使用可空类型(MyProperty)?如果我将数据上下文对象中double的类型更改为<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="x-ua-compatible" content="ie=edge, chrome=1" /> <title>Map Test</title> <script language="javascript" src="https://maps.googleapis.com/maps/api/js?sensor=true"></script> <style> #map-canvas { height: 300px; width: 980px; margin: 0; padding: 0; margin-top: 10px; } </style> </head> <body> <div id="map-canvas" class="map_canvas"></div> <script type="text/javascript"> function initialize() { var mapOptions = { center: new google.maps.LatLng(-29.09958,26.18434), zoom: 5, mapTypeControlOptions: { position: google.maps.ControlPosition.TOP_LEFT } }; map = new google.maps.Map(document.getElementById('map-canvas'),mapOptions); } google.maps.event.addDomListener(window, 'load', initialize); </script> </body> </html> ,它将接受我更改的值。不幸的是我需要可以为空的支持。因此,当文本框保持为空时,我想存储null以及显示空文本框,当值为null时。

Download of Problem Case Solution

1 个答案:

答案 0 :(得分:1)

实际上,该错误不在您的代码中,而是在您下载的(CustomBinding)中。将构造函数修改为

public CustomBinding(string propertyName, object dataSource, string dataMember, IValueConverter valueConverter, object converterParameter = null)
    : this(propertyName, dataSource, dataMember, valueConverter, Thread.CurrentThread.CurrentUICulture, converterParameter)
{ }

public CustomBinding(string propertyName, object dataSource, string dataMember, IValueConverter valueConverter, CultureInfo culture, object converterParameter = null)
    : base(propertyName, dataSource, dataMember, true)
{
    this._converter = valueConverter;
    this._converterCulture = culture;
    this._converterParameter = converterParameter;
}

问题将得到解决。关键部分是Binding.FormatingEnabled必须是true才能使所有这些工作(注意基本调用的最后一个参数,但您也可以稍后设置它)。另请注意,我删除了

this.DataSourceUpdateMode = DataSourceUpdateMode.OnPropertyChanged;

线。此默认值为OnValidation,适用于任何控件。 OnPropertyChanged适用于即时更新控件,如复选框,单选按钮和不可编辑的组合框。在任何情况下,最好将设置此属性的责任留给类的用户。

与问题无关的旁注:最好不要删除无效字符,并将异常抛出到ConvertBack方法中,否则如果用户键入例如,则会得到奇怪的输入值“1-3”

public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
    var text = value != null ? ((string)value).Trim() : null;
    return !string.IsNullOrEmpty(text) ? (object)double.Parse(text, NumberStyles.Any, culture) : null;
}