如何使用DataTemplates将“未命名”属性绑定到DataGrid?

时间:2016-02-10 10:35:01

标签: c# wpf mvvm datagrid

我所拥有的是一个没有“命名”属性的类:

public class ColumnsValues
{
  public int IdProperty { get; set; }
  public string ColumnName { get; set; }
  public string ValueOfColumn { get; set; }
  public TypeCode TypeOfColumn { get; set; }
}

并且数据存储在“ObservableCollection coll”中(例如,对于 Person ): enter image description here 关于大学的另一个例子: enter image description here

我的目标是使用DataGrid创建DataTemplates。为了实现我的目标,我使用DataTable,我可以在其中定义类型:

MyDataTable.Columns.Add("I am bool", typeof(bool));

以及DataTemplates的{​​{1}}示例:

DataGrid

是否有更好的方法(不是DataTable或改进此方法)将我的类ColumnsValues绑定到DataGrid而不会牺牲使用DataTemplates作为值类型?

我知道这个问题有很多机会被关闭,但我想尝试询问潜在的经历。任何帮助将不胜感激!

1 个答案:

答案 0 :(得分:1)

详细说明我的评论,这里有一些粗略的示例代码来说明这个想法:

class DynamicPerson : ICustomTypeDescriptor
{
    public ObservableCollection<ColumnsValues> Features { get; } = new ObservableCollection<ColumnsValues>();


    #region  ICustomTypeDescriptor  

    private CustomTypeDescriptor _customTypeDescriptor = new DynamicPersonTypeDescriptor();
    public String GetClassName() => _customTypeDescriptor.GetClassName();
    public AttributeCollection GetAttributes() => _customTypeDescriptor.GetAttributes();
    public String GetComponentName() => _customTypeDescriptor.GetComponentName();
    public TypeConverter GetConverter() => _customTypeDescriptor.GetConverter();
    public EventDescriptor GetDefaultEvent() => _customTypeDescriptor.GetDefaultEvent();
    public PropertyDescriptor GetDefaultProperty() => _customTypeDescriptor.GetDefaultProperty();
    public object GetEditor(Type editorBaseType) => _customTypeDescriptor.GetEditor(editorBaseType);
    public EventDescriptorCollection GetEvents(Attribute[] attributes) => _customTypeDescriptor.GetEvents(attributes);
    public EventDescriptorCollection GetEvents() => _customTypeDescriptor.GetEvents();
    public object GetPropertyOwner(PropertyDescriptor pd) => this;
    public PropertyDescriptorCollection GetProperties(Attribute[] attributes) => GetProperties();

    public PropertyDescriptorCollection GetProperties()
    {
        var collectionDescriptors = Features.Select(x => new DynamicPersonPropertyDescriptor(Features, x)).ToArray(); 
        return new PropertyDescriptorCollection(collectionDescriptors);          
    }

    #endregion


    class DynamicPersonTypeDescriptor : CustomTypeDescriptor
    { }

    class DynamicPersonPropertyDescriptor : PropertyDescriptor
    {
        TypeConverter typeConverter;

        Collection<ColumnsValues> features;
        ColumnsValues feature;

        public DynamicPersonPropertyDescriptor(Collection<ColumnsValues> features, ColumnsValues feature)
            : base(feature.ColumnName, new Attribute[] { new BindableAttribute(true) })
        {
            this.features = features;
            this.feature = feature;
            typeConverter = TypeDescriptor.GetConverter(feature.Type);
        }

        public override Type ComponentType => feature.Type; 
        public override bool IsReadOnly => false;           
        public override Type PropertyType => feature.Type;  
        public override bool CanResetValue(object component) => true;
        public override object GetValue(object component) => typeConverter.ConvertFrom(feature.ValueOfColumn);

        public override void ResetValue(object component)
        {
            feature.ValueOfColumn = null;
        }

        public override void SetValue(object component, object value)
        {
            feature.ValueOfColumn = Convert.ToString(value);
        }

        public override bool ShouldSerializeValue(object component) => true;
    }

    public class ColumnsValues
    {
        public int IdProperty { get; set; }
        public string ColumnName { get; set; }
        public string ValueOfColumn { get; set; }
        public TypeCode TypeOfColumn { get; set; }
        public Type Type => Type.GetType("System." + TypeOfColumn);
    }
 }

请注意,您原则上可以从抽象类DynamicPerson派生CustomTypeDescriptor,尽管这可能是糟糕的设计。

这将像这样使用

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        var staff = new List<DynamicPerson>();
        var person1 = new DynamicPerson();
        var feature1 = new DynamicPerson.ColumnsValues
        {
            ColumnName = "FirstName",
            IdProperty = 1,
            TypeOfColumn = TypeCode.String,
            ValueOfColumn = "Albert"
        };
        person1.Features.Add(feature1);
        var feature2 = new DynamicPerson.ColumnsValues
        {
            ColumnName = "LastName",
            IdProperty = 1,
            TypeOfColumn = TypeCode.String,
            ValueOfColumn = "Dunno"
        };
        person1.Features.Add(feature2);
        var feature3 = new DynamicPerson.ColumnsValues
        {
            ColumnName = "Alive",
            IdProperty = 1,
            TypeOfColumn = TypeCode.Boolean,
            ValueOfColumn = "True"
        };
        person1.Features.Add(feature3);

        staff.Add(person1);
        staff.Add(person1);
        DataContext = staff;
    }
}

XAML部分:

    <DataGrid Name="Persons" ItemsSource="{Binding}" AutoGenerateColumns="False">
        <DataGrid.Columns>
            <DataGridTextColumn Header="First name"  Binding="{Binding FirstName}"/>
            <DataGridTextColumn Header="Last name" Binding="{Binding LastName}"/>
            <DataGridCheckBoxColumn Header="Alive" Binding="{Binding Alive}"/>
        </DataGrid.Columns>
    </DataGrid>

如果您想使用AutoGenerateColumns="True"自动生成列,DynamicPerson还需要使用[TypeDescriptionProvider(typeof(DynamicPersonDescriptionProvider))]属性进行装饰。为此,您需要定义一个派生自DynamicPersonDescriptionProvider的附加类TypeDescriptionProvider并覆盖GetTypeDescriptor方法。