自定义的WinForms数据转换器不工作的空类型绑定(双?)自定义、转换器、绑定、类型

2023-09-04 01:21:03 作者:再晚些心动

在我的WinForms应用程序,我实现的自定义数据与支持的价值转换器结合,类似WPF。

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

还有一个接口的IValueConverter 类似于WPF界面:

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

有了这个,我可以附加一个自己的转换器的任何(双向)绑定。到目前为止,我已经建立了一个 TimeSpanStringValueConverter ,让我一个时间跨度字段绑定到一个文本框,以及一个 InvertBooleanValueConverter 为布尔场对面绑定到任何控件的任何布尔属性。他们的工作如预期!

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

当我设置的值转换器,我可以清楚地看到我的字符串离开文本框后,将被转换为可空双重价值的 ConvertBack 方法一个破发点,成功。然而,代替我的其他情况下,将不更新该数据上下文对象。如果我在我的价值转换器的转换方法,设置一个断点,我可以看到它会获取 myProperty的离开它之后更新文本框的文本时,这是空。所以,我的文本框中键入任何数值后变空。

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.

我的问题是:为什么?我怎么可以把它用可空类型的工作(双?)?如果我改变 myProperty的的数据上下文对象的类型为会接受我的改变值。不幸的是我需要为空的支持。所以离开文本框为空时,我想存储空的同时显示一个空的文本框,当值为空。

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.

下载问题案例解决方案

推荐答案

其实,这个错误并不在code,但在一个你已经下载(CustomBinding )。修改构造成为

In fact, the bug is not in your code, but in the one you have downloaded (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;
}

和问题将得到解决。的重要组成部分是,​​ Binding.FormatingEnabled 必须为了这一切工作(注意,最后一个参数基地电话,但你可以在以后将它设置得)。另请注意,我删除了

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). Also note that I removed

this.DataSourceUpdateMode = DataSourceUpdateMode.OnPropertyChanged;

行。它的默认值是 OnValidation 这是适用于任何控制权。 OnPropertyChanged 适用于像复选框,单选按钮和非可编辑组合框即时更新控制。在任何情况下,最好是离开这个属性设置为该类用户的责任。

line. The default value for this is OnValidation which is applicable for any control. OnPropertyChanged is applicable for immediate update controls like check boxes, radio buttons and non editable combo boxes. In any case, it is better to leave the responsibility of setting this property to the user of the class.

一个侧面说明无关的问题:你最好关闭没有剥离无效字符,让例外,你的 ConvertBack 方法中被抛出,否则,你得到一个奇怪的输入值,如果如用户键入1-3

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"

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