2

I have a User class:

public partial class User : INotifyPropertyChanged
{
    private string forename;

    [MaxLength(10)]
    public string Forename
    {
        get => forename;
        set
        {
            forename = value;
            OnPropertyChanged("forename");
        }
    }

    public User(string forename)
    {
        Forename = forename;
    }
}

I also have a TextBox. The TextBox's Text property is bound to a User object:

textBox.DataBindings.Add("Text", new User("Michael"), "Forename");

I would like to get the Forename's MaxLength attribute via the TextBox. How to do that?


Note: the code above is a simplification of my real code.

Michael Haddad
  • 4,085
  • 7
  • 42
  • 82
  • You can't do it declaratively, but you can [do it via reflection](https://learn.microsoft.com/en-us/dotnet/standard/attributes/retrieving-information-stored-in-attributes) – stuartd Nov 21 '17 at 16:43
  • Since you are setting up data-binding in the code, create a method do to data-binding for you and in the method apply maxlength to the control. To get the maxlength, in addition to reflection, you can also use type descriptor. – Reza Aghaei Nov 21 '17 at 17:58
  • @RezaAghaei - could you please elaborate in an answer? – Michael Haddad Nov 21 '17 at 18:01

2 Answers2

1

You can use reflection or type descriptor to get information about a type. Where and when you need to do that, depends to the implementation.

Since the data source of your data-binding can be any object like a class or a binding source, it's more flexible and extensible to rely on type descriptors. For example if you want to call a method to apply maxlength by just passing the TextBox to the method like this:

ApplyMaxLengthToTextBox(textBox1);

Then you can create the method this way:

//using System.Linq;
public void ApplyMaxLengthToTextBox(TextBox txt)
{
    var binding = txt.DataBindings["Text"];
    if (binding == null)
        return;
    var bindingManager = binding.BindingManagerBase;
    var datasourceProperty = binding.BindingMemberInfo.BindingField;
    var propertyDescriptor = bindingManager.GetItemProperties()[datasourceProperty];
    var maxLengthAttribute = propertyDescriptor.Attributes.Cast<Attribute>()
        .OfType<MaxLengthAttribute>().FirstOrDefault();
    if (maxLengthAttribute != null)
        txt.MaxLength = maxLengthAttribute.Length;
}

To test it when binding to an object:

textBox1.DataBindings.Add("Text", new MySampleModel(), "SomeProperty");
ApplyMaxLengthToTextBox(textBox1);

To test it when binding to a BindingSource:

var bs = new BindingSource();
bs.DataSource = new MySampleModel();
textBox1.DataBindings.Add("Text", bs, "SomeProperty");
ApplyMaxLengthToTextBox(textBox1);
Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
  • This answer is great and I am sure it will help future readers. However, I am not familliar with a lot of the ideas behind it. `Expression`, `Func`, `MemberExpression` and more. Could you please add a simpler code snippet that does not invlove extension methods and such? Something like @touchofevil wrote, but not hard coded? Thanks a lot! – Michael Haddad Nov 22 '17 at 08:00
  • Now it's the easiest possible solution which respects windows forms data binding rules as well. Reflection is not a good idea when data binding. The data binding works with type descriptor and property descriptor. – Reza Aghaei Nov 22 '17 at 15:59
  • What is the problem with combining reflection with data-binding? – Michael Haddad Nov 24 '17 at 09:37
  • You can use reflection as well. But It's not good idea. Because data-binding works on top of PropertyDescriptors and type descriptors to bring more flexibility and extensibility. As you can see `bindingManager.GetItemProperties()` returns a collection of `PropertyDescriptor` objects. – Reza Aghaei Nov 24 '17 at 09:39
  • Thanks. I think my problem is that I do not fully understand Property Descriptors. Could you please link to a reference about this subject? Thanks! – Michael Haddad Nov 24 '17 at 09:42
  • 1
    `PropertyDescriptor` is somehow equivalent to `PropertyInfo`. You get property descriptor from `TypeDescriptor.GetProperties()` while you get the property info from `Type.GetProperties()`. For more information, take a look at [Type Descriptor Overview](https://msdn.microsoft.com/en-us/library/ms171819.aspx?f=255&MSPPError=-2147217396). – Reza Aghaei Nov 24 '17 at 09:45
  • 1
    Apart from the fact that data-binding works on top of type description, as another benefit, using the type descriptor mechanism brings a big extensibility to your application. You can separate metadata from your model classes. For more information take a look at my post [here](https://stackoverflow.com/a/34481967/3110834). – Reza Aghaei Nov 24 '17 at 09:45
  • 1
    In the current answer, you don't need to know anything about TypeDescriptor. You need just know the `BindingManagerBase` property of the `Binding` object, has a `GetItemProperties` which returns a list of properties which the model (data source) have. That's enough for you. – Reza Aghaei Nov 24 '17 at 09:50
-1

you can try below snippet

var prop = typeof(User).GetProperty("Forename");
var att = prop.GetCustomAttributes(typeof(MaxLength), false);
touchofevil
  • 595
  • 4
  • 21
  • 1
    Thanks! You have hard-coded the `User` type, the property-name `Forename`, and the `MaxLength` attribute. I would like to use my `textBox` object to get all of this information, since it is bound to a `User` object. Thanks again! – Michael Haddad Nov 22 '17 at 07:34