结合主 - 细节数据绑定

时间:2010-03-31 02:06:20

标签: .net vb.net winforms data-binding

我已经在我的项目中从我的对象创建了数据源,其中一些还有其他对象作为成员。当我想将一些对象绑定到数据网格时,我想显示数据网格中成员对象的一些值,但我遇到的示例似乎使用整个其他数据网格或控件来显示这些成员对象值。

如何将这些值组合到一个数据网格中? (最好不要创建包装类) 我能以某种方式用LINQ实现这个目标吗?

非常感谢您的帮助。

由于

P.S。 C#或VB示例都可以。

4 个答案:

答案 0 :(得分:1)

在WinForms中没有干净的方法。你有几个选择,从最简单到最难:

<强> LINQ

(特别是这里的匿名类型)。

您可以这样做:

dataGridView.DataSource = from x in dataSource
                          select new
                          {
                              Property = x.Property,
                              SubProperty = x.ObjectProperty.SubProperty // etc.
                          };

这是迄今为止最简单的方法,但它也是单向绑定;您无法使用此机制更新数据。这也将为网格提供 no 设计时支持,因为在设计时无法发现属性名称。

包装类

这是,我猜,你很熟悉。您创建一个包装实际数据绑定类的类,通过属性并将“子属性”调出到类级别。例如:

public string SubProperty
{
    get { return yourObject.ObjectProperty.SubProperty; }
    set { yourObject.ObjectProperty.SubProperty = value; }
}

这将为您提供网格的设计时支持(假设您使用BindingSource组件并正确设置此对象的对象数据源)并允许双向绑定。但是,它确实要求您滚动包装类并创建要绑定到的每个属性。

自定义类型描述符

这是最困难的,但它也是最灵活,最快速和最干净的(从外部API角度来看)。我不会在这篇文章中详细介绍如何做到这一点,但有关于此的文章。我会链接到自定义TypeDescriptors上的MSDN页面,但它有点令人生畏,并且超出了你想要做的范围。

简而言之,您创建了一个自定义TypeDescriptor类,该类提供了PropertyDescriptor实例列表。这些允许您完全控制绑定体验,而不是依赖于反射(如上述两种情况中的情况)。不过,这是一个非常重要的代码,因此在开始之前,您需要确定这是否是您真正想要做的。

This是对TypeDescriptor类及其功能的一个很好的介绍。你在这里感兴趣的是PropertyDescriptorCollection。您只需要为要与之交互的每个属性返回PropertyDescriptor,包括嵌套属性。

答案 1 :(得分:1)

我最近遇到了同样的问题,并使用上面提到的第三种方法解决了它。下面的DeepBindingList类似于常规的BindingList,但有两个增强功能:

1)它遍历每个属性的子属性,并将它们全部公开为可绑定属性。

2)它增加了对排序的支持。

以任何你喜欢的方式使用,我希望它有用。

using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Text;

namespace DeepBindingList
{
    /// Extends the BindingList to provide sorting and deep property
    /// binding (e.g. Address.Street).
    public class DeepBindingList<T> : BindingList<T>, ITypedList
    {
        //-----------------------------------------------------------------------
        #region ** IBindingList overrides (to provide sorting)

        PropertyDescriptor _sort;
        ListSortDirection _direction;

        protected override bool IsSortedCore
        {
            get { return _sort != null; }
        }
        protected override void RemoveSortCore()
        {
            _sort = null;
        }
        protected override ListSortDirection SortDirectionCore
        {
            get { return _direction; }
        }
        protected override PropertyDescriptor SortPropertyCore
        {
            get { return _sort; }
        }
        protected override bool SupportsSortingCore
        {
            get { return true; }
        }
        protected override void ApplySortCore(PropertyDescriptor pd, ListSortDirection direction)
        {
            // get list to sort
            var items = this.Items as List<T>;

            // apply the sort
            if (items != null)
            {
                var pc = new PropertyComparer<T>(pd, direction);
                items.Sort(pc);
            }

            // save new settings and notify listeners
            _sort = pd;
            _direction = direction;
            this.OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
        }

        // PropertyComparer (used to sort the list)
        class PropertyComparer<TC> : IComparer<TC>
        {
            PropertyDescriptor _pd;
            ListSortDirection _direction;
            public PropertyComparer(PropertyDescriptor pd, ListSortDirection direction)
            {
                _pd = pd;
                _direction = direction;
            }
            public int Compare(TC x, TC y)
            {
                try
                {
                    var v1 = _pd.GetValue(x) as IComparable;
                    var v2 = _pd.GetValue(y) as IComparable;

                    int cmp =
                        v1 == null && v2 == null ? 0 :
                        v1 == null ? +1 :
                        v2 == null ? -1 :
                        v1.CompareTo(v2);

                    return _direction == ListSortDirection.Ascending ? +cmp : -cmp;
                }
                catch 
                {
                    return 0; // comparison failed...
                }
            }
        }

        #endregion

        //-----------------------------------------------------------------------
        #region ** ITypedList (to expose inner properties)

        public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
        {
            var list = new List<PropertyDescriptor>();
            foreach (PropertyDescriptor pd in TypeDescriptor.GetProperties(typeof(T)))
            {
                AddProperties(pd, null, list);
            }
            return new PropertyDescriptorCollection(list.ToArray());
        }
        void AddProperties(PropertyDescriptor pd, PropertyDescriptor parent, List<PropertyDescriptor> list)
        {
            // add this property
            pd = new DeepPropertyDescriptor(pd, parent);
            list.Add(pd);

            // and subproperties for non-value types
            if (!pd.PropertyType.IsValueType && pd.PropertyType != typeof(string))
            {
                foreach (PropertyDescriptor sub in TypeDescriptor.GetProperties(pd.PropertyType))
                {
                    AddProperties(sub, pd, list);
                }
            }
        }
        public string GetListName(PropertyDescriptor[] listAccessors)
        {
            return null;
        }

        // property descriptor with support for sub properties (e.g. Address.Street)
        class DeepPropertyDescriptor : PropertyDescriptor
        {
            PropertyDescriptor _pd;
            PropertyDescriptor _parentPD;

            public DeepPropertyDescriptor(PropertyDescriptor pd, PropertyDescriptor parentPd)
                : base(pd.Name, null)
            {
                _pd = pd;
                _parentPD = parentPd;
            }
            public override string Name
            {
                get
                {
                    return _parentPD != null
                        ? string.Format("{0}.{1}", _parentPD.Name, _pd.Name)
                        : _pd.Name;
                }
            }
            public override bool IsReadOnly
            {
                get { return _pd.IsReadOnly; }
            }
            public override void ResetValue(object component)
            {
                _pd.ResetValue(component);
            }
            public override bool CanResetValue(object component)
            {
                return _pd.CanResetValue(component);
            }
            public override bool ShouldSerializeValue(object component)
            {
                return _pd.ShouldSerializeValue(component);
            }
            public override Type ComponentType
            {
                get { return _pd.ComponentType; }
            }
            public override Type PropertyType
            {
                get { return _pd.PropertyType; }
            }
            public override object GetValue(object component)
            {
                if (_parentPD != null)
                {
                    component = _parentPD.GetValue(component);
                }
                return _pd.GetValue(component);
            }
            public override void SetValue(object component, object value)
            {
                _pd.SetValue(_parentPD.GetValue(component), value);
                OnValueChanged(component, EventArgs.Empty);
            }
        }

        #endregion
    }
}

答案 2 :(得分:1)

我发现Bernardo的 DeepBindingList 是一个救生员,现在我想通过指出代码有周期问题来回馈一些东西。意思是,如果T有一个类型为T的成员,或者它本身有一个类型为T的成员,那么 GetItemProperties ()中的递归将导致堆栈爆炸。

有不同的方法来处理这样的问题,但是我实现的那个方法充分利用了这样一个事实,即我总是事先知道哪些属性将用作网格的列。所以,我创建了一个方法来获取这些列的属性:

private PropertyDescriptorCollection propertyDescriptors;

public void SetItemProperties(IList<string> names)
{
    var list = new List<PropertyDescriptor>();
    foreach (var name in names)
        AddProperty(name, list);
    propertyDescriptors = new PropertyDescriptorCollection(list.ToArray());
}

private void AddProperty(string name, List<PropertyDescriptor> list)
{
    Type propType = typeof (T);
    PropertyDescriptor parent = null;
    foreach (var part in name.Split('.'))
    {
        PropertyDescriptor prop = TypeDescriptor.GetProperties(propType).Cast<PropertyDescriptor>().FirstOrDefault(pd => pd.Name == part);
        if (prop == null)
            return;
        list.Add(new DeepPropertyDescriptor(prop, parent));
        propType = prop.PropertyType;
        parent = prop;
    }
}

(我不打算避免在两个或多个“孙子”共享同一个父母时可能出现的重复PropertyDescriptors,如Tab和Tac他们没有给我带来任何麻烦所以我把它留给了下一个人修复。)

我在 GetItemProperties ()的开头添加了一个测试,所以:

public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
{
    if (propertyDescriptors != null)
        return propertyDescriptors;

如果我已经调用 SetItemProperties (),那么它就什么都不做。

我还发现采用list参数的构造函数太有限,所以我替换了

public DeepBindingList(IList<T> list)
    : base(list)
{
}

public DeepBindingList(IEnumerable<T> list)
{
    foreach (var t in list)
    {
        Add(t);
    }
}

我希望有人觉得这很有用。

答案 3 :(得分:0)

哎呀......我的意思是

        public override void SetValue(object component, object value)
        {
            if (_parentPD != null)
            {
                component = _parentPD.GetValue(component);
            }
            _pd.SetValue(component, value);
            OnValueChanged(component, EventArgs.Empty);
        }