将WPF DataGrid绑定到列表<接口> [英] Binding WPF DataGrid to List<Interface>

查看:186
本文介绍了将WPF DataGrid绑定到列表<接口>的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

你好,我有一个DataGrid,我有不同的报告,我想显示。我要改变这些课程,所以他们在这里比较短,但想法是一样的。



让我说有一个叫做IReports的界面

  public interface IReports 
{
}

和三个类称为学生,类,汽车

  public class学生:IReports 
{
public string Name {get;组;
}

public class Classes:IReports
{
public string ClassName {get;组; }
public string StudentName {get;组;

}
public class Cars:IReports
{
public int Mileage {get;组; }
public string CarType {get;组; }
public string StudentName {get;组; }
}

列表

 私人列表< IReports> _reportsTable; 

public List< IReports> ReportsTable
{
get {return _reportsTable; }
set {SetProperty(ref(_reportsTable),value); }
}

DataGrid

 < DataGrid ItemsSource ={Binding ReportsList}
Grid.Column =1
Grid.Row =0
AutoGenerateColumns = True
Grid.RowSpan =6/>

好的,这里重要的是他们都有不同的属性名称,有些更少一些有更少的。如何绑定DataGrid来查看不同的属性?这是MVVM,如果这有任何区别。



更新:这将永远只会一次使用一个类。但是当有人更改组合框时,它将触发事件将填写 IList< IReports>



解决方案


这将是什么一直只能使用其中一个类,强>。但是当有人更改组合框时,它将触发将填充IList< IReports>的事件。


你不会在列表中混合不同的元素(即它只包含 学生 Cars )。所有其他答案都假定该列表包含混合内容,但如果这是真的,那么 DataGrid 根本不是这样的内容的正确的主持人。



如果上述假设是正确的,那么唯一的问题是如何用单个可绑定属性来表示不同的列表。从



而这个

  viewModel.ReportsTable = new List< Classes> 
{
new Classes {ClassName =A,StudentName =A},
new Classes {ClassName =A,StudentName =B},
new Class Class {ClassName =B,StudentName =C},
new Classes {ClassName =B,StudentName =D},
};

它显示





最后这个

  viewModel.ReportsTable = new List< Cars> 
{
新车{里程= 100,CarType =宝马,StudentName =A},
新车{里程= 200,CarType =宝马,StudentName =B },
新车{里程= 300,CarType =宝马,StudentName =C},
新车{里程= 400,CarType =宝马,StudentName =D} ,
};

结果





更新:上述要求修改模型以返回具体的列表< T> ; 实例。如果你想保持模型(即返回 List< IReports> ),那么你需要一个不同的解决方案,这次使用 ITypedList 。为了做到这一点,我们将使用 System.Collections.ObjectModel.Collection< T> 基类:

  public class ReportsList:集合< IReports>,ITypedList 
{
public ReportsList(IList< IReports>源):base(source){}
public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor [] listAccessors)
{
return TypeDescriptor.GetProperties(Count> 0?this [0] .GetType():typeof(IReports));
}
public string GetListName(PropertyDescriptor [] listAccessors){return null;
}

然后将bindable属性更改为

  private IList< IReports> _reportsTable; 
public IList< IReports> ReportsTable
{
get {return _reportsTable; }
set {SetProperty(ref _reportsTable,value as ReportsList?new ReportsList(value));
}

您已完成。


Hello I have a DataGrid and I have different reports that I want to show. I'm going to change the classes so they are shorter in here but Idea is the same.

Lets say that I Have an Interface called IReports

public interface IReports
{
}

and three classes called Students, Classes, Cars

public class Students:IReports
{
    public string Name { get; set; }
}  

public class Classes : IReports
{
    public string ClassName { get; set; }
    public string StudentName { get; set; }

}   
public class Cars : IReports
{
    public int Mileage { get; set; }
    public string CarType { get; set; }
    public string StudentName { get; set; }
}

The List

private List<IReports> _reportsTable;    

public List<IReports> ReportsTable
    {
        get { return _reportsTable; }
        set { SetProperty(ref (_reportsTable), value); }
    }

the DataGrid

<DataGrid ItemsSource="{Binding ReportsList}"
          Grid.Column="1"
          Grid.Row="0"
          AutoGenerateColumns="True"
          Grid.RowSpan="6"/>

Okay, so what is important here is they all have different property names and some have more some have less. How can I bind the DataGrid to look at the different properties? This is MVVM if that makes any difference.

Update: What this will always only use one of the classes at a time.but when someone changes a combobox it will fire an event that will fill the IList<IReports>.

解决方案

What this will always only use one of the classes at a time. but when someone changes a combobox it will fire an event that will fill the IList<IReports>.

The way I understand the above is that you never mix different elements inside the list (i.e. it contains only Classes, Students or Cars). All the other answers are assuming the list contains mixed content, but if that's the true, then DataGrid is simply not the right presenter for such content.

If the above assumption is correct, then the only problem is how to represent different lists with a single bindable property. As can be seen in Data Binding Overview, when dealing with collection, data binding does not really care if they are generic or not. The recognizable source types are the non generic IEnumerable, IList and IBindingList. However, the collection view implementation is using some rules to determine the element type of the collection, by seeking for generic type argument of implemented IEnumerable<T> interfaces by the actual data source class, by checking the first available item, or taking the information from ITypedList implementation etc. All the rules and their precedence can be seen in the Reference Source.

With all that in mind, one possible solution could be to change the ReportsTable property type to allow assigning List<Classes> or List<Students or List<Cars>. Any common class/interface will work (remember, data binding will check the actual type returned by GetType()) like object, IEnumerable, IList, IEnumerable<IReports> etc., so I'll choose the closest covariant type to List<IReports which is IReadOnlyList<IReports>:

private IReadOnlyList<IReports> _reportsTable;

public IReadOnlyList<IReports> ReportsTable
{
    get { return _reportsTable; }
    set { SetProperty(ref (_reportsTable), value); }
}

Now when you do this

viewModel.ReportsTable = new List<Students>
{
    new Students { Name = "A" },
    new Students { Name = "B" },
    new Students { Name = "C" },
    new Students { Name = "D" },
};

you get

while with this

viewModel.ReportsTable = new List<Classes>
{
    new Classes { ClassName = "A", StudentName = "A" },
    new Classes { ClassName = "A", StudentName ="B" },
    new Classes { ClassName = "B", StudentName = "C" },
    new Classes { ClassName = "B", StudentName = "D" },
};

it shows

and finally this

viewModel.ReportsTable = new List<Cars>
{
    new Cars { Mileage = 100, CarType = "BMW", StudentName = "A" },
    new Cars { Mileage = 200, CarType = "BMW", StudentName = "B" },
    new Cars { Mileage = 300, CarType = "BMW", StudentName = "C" },
    new Cars { Mileage = 400, CarType = "BMW", StudentName = "D" },
};

results in

UPDATE: The above requires modifying the model to return concrete List<T> instances. If you want to keep the model as it is (i.e. returning List<IReports>), then you'll need a different solution, this time utilizing the ITypedList. In order to do that, we'll create a simple list wrapper using the System.Collections.ObjectModel.Collection<T> base class:

public class ReportsList : Collection<IReports>, ITypedList
{
    public ReportsList(IList<IReports> source) : base(source) { }
    public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
    {
        return TypeDescriptor.GetProperties(Count > 0 ? this[0].GetType() : typeof(IReports));
    }
    public string GetListName(PropertyDescriptor[] listAccessors) { return null; }
}

then change the bindable property to

private IList<IReports> _reportsTable;
public IList<IReports> ReportsTable
{
    get { return _reportsTable; }
    set { SetProperty(ref _reportsTable, value as ReportsList ?? new ReportsList(value)); }
}

and you are done.

这篇关于将WPF DataGrid绑定到列表&lt;接口&gt;的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆