8

In a Winforms form, I want to provide visual cues to the user when an input field contains an invalid value. To that end, I want to bind the ForeColor property of a input field's label to the (boolean) IsPropertyValid property of the underlying model such that the label turns red when IsPropertyValid == false.

What I currently have is an event handler for the binding's Format event:

Controls["dateOfBirthLabel"].DataBindings["ForeColor"].Format += convertBoolToColor;
// (dateOfBirthLabel.ForeColor is bound to a boolean IsDateOfBirthValid property.)

void convertBoolToColor(object sender, ConvertEventArgs e)
{
    e.Value = (bool)e.Value ? Color.Black : Color.Red;
}

If I wanted to do this in WPF, I suppose I would specify a custom value converter (bool to Color) directly with the binding in the XAML. Most importantly, I wouldn't have to refer to a specific control via its name.

I would like to do the same thing with my Winforms form. Ideally, I could specify a TypeConverter object for a particular binding directly in the Forms Designer. Is this possible?

stakx - no longer contributing
  • 83,039
  • 20
  • 168
  • 268

2 Answers2

11

My previous answer (now deleted) was incorrect: This can be done, using a custom TypeConverter.

First, one needs a suitable converter. (Unlike with XAML, one does not implement a IValueConverter, but derive from TypeConverter.) For example:

// using System;
// using System.ComponentModel;
// using System.Drawing;
// using System.Globalization;

sealed class BooleanToColorConverter : TypeConverter
{
    public override bool CanConvertTo(ITypeDescriptorContext context,
                                      Type destinationType)
    {
        return destinationType == typeof(Color);
    }

    public override object ConvertTo(ITypeDescriptorContext context,
                                     CultureInfo culture,
                                     object value, 
                                     Type destinationType)
    {
        return (bool)value ? Color.Green : Color.Red;
    }
}

Next, (and also unlike with XAML data binding,) this converter is not applied to the binding itself; it must be attached to the data source's property using the [TypeConverter] attribute:

// using System.ComponentModel;

partial class DataSource : INotifyPropertyChanged
{
    [TypeConverter(typeof(BooleanToColorConverter))] // <-- add this! 
    public bool IsValid { get { … } set { … } }
}

Finally, formatting must be enabled on the data binding:

// Control control = …;
// DataSource dataSource = …;

control.DataBindings.Add("ForeColor", dataSource, "IsValid", formattingEnabled: true);
//                                                           ^^^^^^^^^^^^^^^^^^^^^^^

Note that this example only deals with one-way (data source to control) data binding. For two-way data binding, you would additionally have to override the TypeConverter.ConvertFrom and TypeConverter.CanConvertFrom methods.

Community
  • 1
  • 1
stakx - no longer contributing
  • 83,039
  • 20
  • 168
  • 268
  • If only I'd known enough to ask the right question. Now that I've figured it out, this was easy to find. – Roger Willcocks Apr 11 '20 at 01:05
  • 1
    @RogerWillcocks: I'm often having the same problem when learning something new. It may help others if you asked your question on SO but in the terms how you would have phrased it originally, then mark it as a duplicate of this one here (or add a comment "related to ..."), in order to help people find their way to a solution. – stakx - no longer contributing Apr 11 '20 at 09:09
-3
c.DataBindings.Add("Checked", dataSource, dataMember, true, DataSourceUpdateMode.OnPropertyChanged );


class Someclass
{
[TypeConverter(typeof(IntBoolConverter))]
        public int p_isEnable
        {
            get { return nc_isEnable; }
            set { m_isEnable= value); }
        }
}
 public class IntBoolConverter:TypeConverter
    {
        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {

            if (sourceType == typeof(bool))
            {
                return true;
            }
            return base.CanConvertFrom(context, sourceType);
        }

        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
            if (destinationType == typeof(int))
            {
                return true;
            }
            return base.CanConvertFrom(context, destinationType);
        }

        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
        {
            if (value is bool)
            {
                var objectToInt = value.ObjectToBool();
                return objectToInt;
            }
            return base.ConvertFrom(context, culture, value);
        }
        public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
        {
            if (destinationType == typeof(bool))
            {
                return value.ObjectToBool();
            }
            return base.ConvertTo(context, culture, value, destinationType);
        }
    }
laaposto
  • 11,835
  • 15
  • 54
  • 71
Mat
  • 1