代码之家  ›  专栏  ›  技术社区  ›  Martin Braun

带有转换器的自定义WinForms数据绑定无法处理可为null的类型(double?)

  •  0
  • Martin Braun  · 技术社区  · 9 年前

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

    示例有一个新的绑定类,该类派生自 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);
        }
    }
    

    还有界面 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);
    }
    

    有了这个,我可以将自己的转换器连接到任何(双向)绑定。到目前为止,我已经建立了一个 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 当更新 文本 离开文本框后。因此,在文本框中键入任何数值后,文本框将变为空。

    我的问题是:为什么?我如何才能使它与可为null的类型一起工作( 双重的 )? 如果我更改 我的属性 在数据上下文对象中 double 它将接受我更改的值。 不幸的是,我需要可以为空的支持。因此,当文本框为空时,我希望存储空值,并在值为空时显示空文本框。

    Download of Problem Case Solution

    1 回复  |  直到 9 年前
        1
  •  1
  •   Ivan Stoev    9 年前

    事实上,错误不在您的代码中,而是在您下载的代码中( 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;
    }