使用mvvm

时间:2016-11-23 11:56:02

标签: c# wpf mvvm datagrid

我想使用mvvm动态生成数据网格。在datagrid的每个单元格中,我必须显示一个对象。列名是对象的属性之一。 datagrid的itemsource将是对象列表。如何使用mvvm动态生成数据网格?

更新

我创建了一个自定义类,它已经使用ICustomTypeDescriptor进行了扩展。

    public class MyCustomType : ICustomTypeDescriptor
    {
    // This is instance data.
    private readonly BindingList<PropertyDescriptor> _propertyDescriptors = new BindingList<PropertyDescriptor>();

    // The data is stored on the type instance.
    private readonly IDictionary<string, object> _propertyValues = new Dictionary<string, object>();

    // The property descriptor now takes an extra argument.
    public void AddProperty(string name, Type type)
    {
        _propertyDescriptors.Add(new MyPropertyDescriptor(name, type));
    }

    public PropertyDescriptorCollection GetProperties()
    {
        return new PropertyDescriptorCollection(_propertyDescriptors.ToArray());
    }

    public PropertyDescriptorCollection GetProperties(Type type)
    {
        return new PropertyDescriptorCollection(_propertyDescriptors.ToArray());
    }

    public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
    {
        return GetProperties();
    }

    public object GetPropertyOwner(PropertyDescriptor pd)
    {
        throw new NotImplementedException();
    }

    public AttributeCollection GetAttributes()
    {
        throw new NotImplementedException();
    }

    public string GetClassName()
    {
        throw new NotImplementedException();
    }

    public string GetComponentName()
    {
        throw new NotImplementedException();
    }

    public TypeConverter GetConverter()
    {
        throw new NotImplementedException();
    }

    public EventDescriptor GetDefaultEvent()
    {
        throw new NotImplementedException();
    }

    public PropertyDescriptor GetDefaultProperty()
    {
        throw new NotImplementedException();
    }

    public object GetEditor(Type editorBaseType)
    {
        throw new NotImplementedException();
    }

    public EventDescriptorCollection GetEvents()
    {
        return null;
    }

    public EventDescriptorCollection GetEvents(Attribute[] attributes)
    {
        return null;
    }

    private class MyPropertyDescriptor : PropertyDescriptor
    {
        // This data is here to indicate that different instances of the type
        // object may have properties of the same name, but with different
        // characteristics.
        private readonly Type _type;

        public MyPropertyDescriptor(string name, Type type)
            : base(name, null)
        {
            _type = type;
        }

        public override bool CanResetValue(object component)
        {
            throw new NotImplementedException();
        }

        public override Type ComponentType
        {
            get { throw new NotImplementedException(); }
        }

        public override object GetValue(object component)
        {
            MyCustomType obj = (MyCustomType) component;
            object value = null;
            obj._propertyValues.TryGetValue(Name, out value);
            return value;
        }

        public override bool IsReadOnly
        {
            get { return false; }
        }

        public override Type PropertyType
        {
            get { return _type; }
        }

        public override void ResetValue(object component)
        {
            throw new NotImplementedException();
        }

        public override void SetValue(object component, object value)
        {
            var oldValue = GetValue(component);

            if (oldValue != value)
            {
                MyCustomType obj = (MyCustomType) component;
                obj._propertyValues[Name] = value;
                OnValueChanged(component, new PropertyChangedEventArgs(Name));
            }
        }

        public override bool ShouldSerializeValue(object component)
        {
            throw new NotImplementedException();
        }

        public override void AddValueChanged(object component, EventHandler handler)
        {
            // set a breakpoint here to see WPF attaching a value changed handler
            base.AddValueChanged(component, handler);
        }
    }
}

我将此自定义类型对象的列表绑定到datagrid itemsource。但是datagrid没有显示任何内容?

1 个答案:

答案 0 :(得分:0)

使用DataGrid的方法是将IEnumerable<T>作为其ItemsSource,其中T是具有属性的类或接口,您希望为其生成列。您应该使用DataGrid支持的类型。大多数值类型都受支持。您可以找到有关支持的类型和用法的列表HERE

因此,您的列表应包含行的对象,而不是单元格。 DataGrid将为列表的泛型参数类型的每个公共属性自动生成单元格。

然后,您可以使用MVVM绑定列表,如下所示:

<DataGrid ItemsSource="{Binding MyList}" AutoGenerateColumns="True"></DataGrid>

当然,DataGrid的DataContext(继承或显式)应包含带有项目的public IEnumerable<T> MyList { get; }

<强>更新

  

实际上,每个行对象依次具有单元格的对象列表。   每个单元对象都有多个属性。我想要显示1个   属性值作为列名称,每个单元格对象将有不同   基于对象类型的单元格样式。如何动态创建   数据网格有很多条件吗?

在WPF中,DataGrid对每一行都有相同的列集。因此,在您的情况下,即使您可以为每个单元格定义不同的列数据,也可以仅使用第一行(或您需要的那一行)来定义列。我宁愿建议创建Custom Attributes以声明列属性(数据类型,header..etc),因为它是一个声明性信息而不是动态(不应该从一行变为行或不时地改变) 。通过使用THIS线程中描述的触发器,样式信息仍然可以具有一些动态性。这更像是一种WPF方式。

无论如何,如果你想坚持你的架构,你应该在从DataGrid派生的类中实现列探索逻辑。在ItemsSourceChanged处理程序中,检查ItemsSource的类型。如果这是一个IEnumerable,你可以通过反射获得泛型参数(T),并检查该类型是否支持描述模式的列。类似的东西:

protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
{
    base.OnItemsSourceChanged(oldValue, newValue);

    if (AutoGenerateColumns && newValue != null && newValue.GetType().IsGenericType)
    {
        this.Columns.Clear();

        var type = newValue.GetType().GetGenericArguments().First();

        if (typeof(IMyColumnDescriptionStructure).IsAssignableFrom(type))
        {
            var rows = newValue as IEnumerable<IMyColumnDescriptionStructure>;
            var firstRow = rows.First() as IMyColumnDescriptionStructure;

            // TODO: explore your structure and add column definitions
            ///this.Columns.Add(...)
        }
    }
}