Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / Windows Forms / WinForm Data Binding / September 2008

Tip: Looking for answers? Try searching our database.

Binding a DataGridView to properties of objects contained inside the list member

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
TimYaukey - 27 Sep 2008 23:41 GMT
I need to bind a DataGridView to a BindingList<TOuter>. But I don't want to
bind to properties of TOuter, instead TOuter is a wrapper around 2 other
objects of type TInner1 and TInner2. I want to bind to selected public
properties of TInner1 and TInner2, without writing code in TOuter which
exposes those "inner" properties. In essence TInner1 and TInner2 are like
table rows I want to join, and create a new type for them which exposes some
of their public properties, and bind the DataGridView to a BindingList<> of
that new type. Further, I don't want to use automatically constructed columns
- I want to manually add the columns to the DataGridView so I can control the
types, widths and other attributes of those columns. I'd like to use
reflection to explore TInner1 and TInner2 to find all their public properties,
and somehow "push" them out to somewhere the DataGridView can see. I have not
been able to solve this problem. I've tried implementing
ICustomTypeDescriptor on TOuter to return a PropertyDescriptorCollection of
the "inner" properties I want to expose, using static TypeDescriptor methods
to do most of the work. ICustomTypeDescriptor.GetProperties() is called, but
ICustomTypeDescriptor.GetPropertyOwner() is not, and the properties of the
TInner1 and TInner2 never seem to be accessed (it acts like the grid is
unbound). I also tried subclassing BindingList and implementing ITypedList on
it to expose the PropertyDescriptorCollection, no joy. It seems to me that
DataGridView does not use the ICustomTypeDescriptor and/or ITypedList
interfaces the way I think it does, I'm implementing them wrong, or manually
creating the columns is bypassing some of the normal property binding logic.
Does anyone have some advice or an explanation for why my attempts have
failed?
Marc Gravell - 30 Sep 2008 09:10 GMT
This is quite complex to do. I would recommend TypeDescriptionProvider
instead of implementing ICustomTypeDescriptor, since this can apply in a
few additional scenarios. Anyway, here's a simplified version of the
code... I have cheated a little by using a PropertyInfo for the two
branches - it is possible to get a PropertyDescriptor for these, but you
need a few more tricks to do it...

I've used C# 3.0 just for the example - the main code should work in C#
2.0 too.

Marc

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using System.Windows.Forms;

static class Program {
    static void Main()
    {
        Application.EnableVisualStyles();
        BindingList<Tuple> list = new BindingList<Tuple>() {
            new Tuple { Foo = { Name = "Fred"}, Bar = { DateOfBirth =
DateTime.Today}},
            new Tuple { Foo = { Name = "Jo"}, Bar = { DateOfBirth =
DateTime.Today.AddDays(-24)}}
        };
        using(Form form = new Form())
        using (DataGridView grid = new DataGridView())
        {
            grid.Dock = DockStyle.Fill;
            form.Controls.Add(grid);
            grid.DataSource = list;
            Application.Run(form);
        }
    }
}

class Foo
{
    public string Name { get; set; }
}
class Bar
{
    public DateTime DateOfBirth { get; set; }
}
[TypeDescriptionProvider(typeof(TupleDescriptionProvider))]
class Tuple
{
    public Tuple()
    {
        Foo = new Foo();
        Bar = new Bar();
    }
    public Foo Foo { get; private set; }
    public Bar Bar { get; private set; }
}
internal class TupleDescriptionProvider : TypeDescriptionProvider
{
    public override ICustomTypeDescriptor GetTypeDescriptor(Type
objectType, object instance)
    {
        return TupleTypeDescriptor.Default;
    }
}
internal class TupleTypeDescriptor : CustomTypeDescriptor
{
    private TupleTypeDescriptor() { }

    public static readonly TupleTypeDescriptor Default = new
TupleTypeDescriptor();

    public override PropertyDescriptorCollection GetProperties()
    {

        List<PropertyDescriptor> props = new List<PropertyDescriptor>();
        Wrap(props, typeof(Tuple).GetProperty("Foo"),
TypeDescriptor.GetProperties(typeof(Foo)));
        Wrap(props, typeof(Tuple).GetProperty("Bar"),
TypeDescriptor.GetProperties(typeof(Bar)));
        return new PropertyDescriptorCollection(props.ToArray(), true);
    }
    public override PropertyDescriptorCollection
GetProperties(Attribute[] attributes)
    {
        return GetProperties();
    }

    static void Wrap(List<PropertyDescriptor> list, PropertyInfo
parent, PropertyDescriptorCollection properties)
    {
        foreach (PropertyDescriptor child in properties)
        {
            list.Add(new WrappedDescriptor(parent, child));
        }
    }

}
internal class WrappedDescriptor : PropertyDescriptor
{
    private readonly PropertyInfo parent;
    private readonly PropertyDescriptor child;
    public WrappedDescriptor(PropertyInfo parent, PropertyDescriptor child)
        : base(GetName(parent,child), GetAttribs(child))
    {
        this.parent = parent;
        this.child = child;
    }

    static string GetName(PropertyInfo parent, PropertyDescriptor child)
    {
        return parent.Name + "_" + child.Name;
    }
    static Attribute[] GetAttribs(PropertyDescriptor property)
    {
        Attribute[] attribs = new Attribute[property.Attributes.Count];
        property.Attributes.CopyTo(attribs, 0);
        return attribs;
    }

    public override bool ShouldSerializeValue(object component)
    {
        return child.ShouldSerializeValue(parent.GetValue(component,
null));
    }
    public override object GetValue(object component)
    {
        return child.GetValue(parent.GetValue(component, null));
    }
    public override void SetValue(object component, object value)
    {
        child.SetValue(parent.GetValue(component, null), value);
    }
    public override void ResetValue(object component)
    {
        child.ResetValue(parent.GetValue(component, null));
    }
    public override bool CanResetValue(object component)
    {
        return child.CanResetValue(parent.GetValue(component, null));
    }
    public override Type PropertyType
    {
        get { return child.PropertyType; }
    }
    public override Type ComponentType
    {
        get { return parent.ReflectedType; }
    }
    public override bool IsReadOnly
    {
        get { return child.IsReadOnly; }
    }

}

Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.