简体   繁体   English

与转换器的自定义WinForms数据绑定不适用于可为null的类型(双精度?)

[英]Custom WinForms data binding with converter not working on nullable type (double?)

In my WinForms application I implemented custom data binding with support for value converters , similar to WPF. 在WinForms应用程序中,我实现了自定义数据绑定,并支持值转换器 ,类似于WPF。

The sample has a new binding class that derives from Binding and allows to attach a custom converter: 该示例具有一个新的绑定类,该类从Binding派生,并允许附加一个自定义转换器:

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: 还有一个接口IValueConverter与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. 到目前为止,我已经构建了一个TimeSpanStringValueConverter ,它使我可以将TimeSpan字段绑定到文本框,还可以将InvertBooleanValueConverter绑定到布尔字段的对立面到任何控件的任何布尔属性。 They work as expected! 他们按预期工作!

Now I want to bind a property of the type double? 现在我想绑定一个double?类型的属性double? to a textbox Text field. 到文本框“ Text字段。 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. 当我在值转换器的ConvertBack方法中设置断点时,我可以清楚地看到成功离开文本框后,如何将我的字符串转换为可为空的double值。 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. 而且,如果我在值转换器的Convert方法中设置了一个断点,我会看到它会检索MyProperty的初始值,该值在离开文本框后更新Text框的Text时将为null。 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? )? 我如何使其与可为null的类型( double? )一起使用? If I change the type of MyProperty in the data context object to double it will accept my changed value. 如果将数据上下文对象中MyProperty的类型更改为double ,它将接受更改后的值。 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. 因此,当文本框为空时,我想存储null以及在值为null时显示一个空文本框。

Download of Problem Case Solution 下载问题案例解决方案

In fact, the bug is not in your code, but in the one you have downloaded ( CustomBinding ). 实际上,该错误不在您的代码中,而是在您已下载的代码中( CustomBinding )。 Modify constructors to become 修改构造函数成为

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;
}

and the problem will be solved. 问题就会解决。 The essential part is that Binding.FormatingEnabled must be true in order all this to work (note the last argument to the base call, but you can set it later too). 基本部分是Binding.FormatingEnabled必须为true才能使所有这些正常工作(请注意基本调用的最后一个参数,但您也可以稍后进行设置)。 Also note that I removed 另请注意,我删除了

this.DataSourceUpdateMode = DataSourceUpdateMode.OnPropertyChanged;

line. 线。 The default value for this is OnValidation which is applicable for any control. 此项的默认值为OnValidation ,适用于任何控件。 OnPropertyChanged is applicable for immediate update controls like check boxes, radio buttons and non editable combo boxes. OnPropertyChanged适用于即时更新控件,例如复选框,单选按钮和不可编辑的组合框。 In any case, it is better to leave the responsibility of setting this property to the user of the class. 无论如何,最好将设置此属性的责任留给班级的用户。

A side note unrelated to the question: You'd better off not stripping the invalid characters and let the exception to be thrown inside your ConvertBack method, otherwise you get a weird input values if the user types for instance "1-3" 与问题无关的旁注:最好不要剥离无效字符,并让异常抛出到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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM