C# 如何从WPF中的基类型集合为子类型集合自动生成DataGrid列?
所以我有一堆从基类派生的类。我有一些类(收集器),它们有一个返回这些类集合的方法。我还有一个C# 如何从WPF中的基类型集合为子类型集合自动生成DataGrid列?,c#,wpf,mvvm,datagrid,C#,Wpf,Mvvm,Datagrid,所以我有一堆从基类派生的类。我有一些类(收集器),它们有一个返回这些类集合的方法。我还有一个TabControl,其中每个选项卡都有包含DataGrid的自定义控件。这些自定义控件有一个ViewModel。在ViewModel中,我拥有由收集器返回的基类元素的集合。我想将DataGrids绑定到这些集合并自动生成列,但派生类有不同的属性,基类没有任何应该显示的属性 internal class ElementsInfoViewModel : INotifyPropertyChanged {
TabControl
,其中每个选项卡都有包含DataGrid
的自定义控件。这些自定义控件有一个ViewModel
。在ViewModel中,我拥有由收集器返回的基类元素的集合。我想将DataGrid
s绑定到这些集合并自动生成列,但派生类有不同的属性,基类没有任何应该显示的属性
internal class ElementsInfoViewModel : INotifyPropertyChanged
{
private readonly ElementScaner _elementScaner;
public ElementsInfoViewModel(ElementScaner elementScaner)
{
_elementScaner = elementScaner;
}
public ReadOnlyCollection<SystemElement> ShownElements => _elementScaner.Elements;
}
我不知道它是否有效,但您尝试过泛型吗? 我的意思是这样的:
internal class ElementsInfoViewModel<T> : INotifyPropertyChanged
where T : SystemElement
{
public ElementsInfoViewModel(ElementScaner elementScaner)
{
var list = new List<T>();
foreach (var el in elementScanner.Elements)
{
list.Add(el as T);
}
ShownElements = new ReadOnlyCollection<T>(list);
}
public ReadOnlyCollection<T> ShownElements { get; }
}
内部类元素SinfoViewModel:INotifyPropertyChanged
其中T:SystemElement
{
公共元素SinfoViewModel(元素扫描器元素扫描器)
{
var list=新列表();
foreach(elementScanner.Elements中的变量el)
{
列表。添加(el为T);
}
ShownElements=新的只读集合(列表);
}
公共只读集合显示元素{get;}
}
我是在浏览器中写的,所以我的代码可能是错误的,但我还是要接受这个想法。
如果可以的话,最好是使用通用版本的ElementScanner(类似于ElementScanner让我们知道它是否会工作我不知道这是否是您需要的,但我有另一个选择。 由于DataGrid没有一种方法来动态绑定列,因此您可以对其进行扩展并自行创建 例如:
class MyDataGrid : DataGrid
{
public MyDataGrid() : base()
{
}
protected override void OnItemsSourceChanged(System.Collections.IEnumerable oldValue, System.Collections.IEnumerable newValue)
{
base.OnItemsSourceChanged(oldValue, newValue);
if (newValue != null)
{
var enumerator = newValue.GetEnumerator();
if (enumerator.MoveNext())
{
Columns.Clear();
var firstElement = enumerator.Current;
var actualType = firstElement.GetType();
foreach (var prop in actualType.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(x => x.CanRead))
{
Columns.Add(new DataGridTextColumn
{
Header = prop.Name,
Binding = new Binding(prop.Name)
});
}
}
}
}
}
我写了一个简单的项目来测试它,它在我的环境中工作
第一对象类型(个人)
第二种对象类型(汽车)
称为SystemElement的元素(标题用作选项卡标题)
类系统元素
{
公共系统元素(字符串标题、IList元素)
{
头衔=头衔;
元素=新的只读集合(元素);
}
公共字符串标题{get;set;}
公共只读集合元素{get;}
}
ElementScanner,用于提取要显示的元素。在我的测试中,列表在构造函数中设置:
class ElementScanner
{
public ElementScanner()
{
var data = new List<SystemElement>();
data.Add(new SystemElement("People", new List<object>
{
new Person { Name = "John", Surname = "Doe", Age = 22},
new Person { Name = "Lenny", Surname = "Pegasus", Age = 30},
new Person { Name = "Duffy", Surname = "Duck", Age = 22}
}));
data.Add(new SystemElement("Cars", new List<object>
{
new Car { Name = "Mercedes", HP = 700 },
new Car { Name = "Red Bull", HP = 650 },
new Car { Name = "Ferrari", HP = 600 }
}));
Elements = new ReadOnlyCollection<SystemElement>(data);
}
public ReadOnlyCollection<SystemElement> Elements { get; }
}
class元素扫描器
{
公共元素扫描器()
{
var data=新列表();
添加(新系统元素(“人员”),新列表
{
新人{Name=“John”,姓氏=“Doe”,年龄=22},
新人{Name=“Lenny”,姓氏=“Pegasus”,年龄=30},
新人{Name=“Duffy”,姓氏=“Duck”,年龄=22}
}));
添加(新系统元素(“汽车”),新列表
{
新车{Name=“Mercedes”,HP=700},
新车{Name=“红牛”,HP=650},
新车{Name=“法拉利”,马力=600}
}));
元素=新的只读集合(数据);
}
公共只读集合元素{get;}
}
现在我们有了ViewModel。在我的示例中,我没有使用INotifyPropertyChanged,因为属性永远不会更改(这是一个测试项目)
类元素SinfoViewModel
{
公共元素SinfoViewModel()
{
var elementScanner=new elementScanner();
Elements=elementScanner.Elements;
}
公共只读集合元素{get;}
}
现在移到视图侧。首先,我们有一个UserControl,它显示每个选项卡的内容。因此,它将显示MyDataGrid类型的DataGrid:
<UserControl x:Class="Stack.ElementInfoView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Stack"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<local:MyDataGrid ItemsSource="{Binding Elements}" >
</local:MyDataGrid>
</Grid>
</UserControl>
最后,我们有一个主视图,它将所有内容合并到一起:
<Window x:Class="Stack.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Stack"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<local:ElementsInfoViewModel />
</Window.DataContext>
<Grid>
<TabControl ItemsSource="{Binding Elements}">
<TabControl.ItemContainerStyle>
<Style TargetType="TabItem">
<Setter Property="Header" Value="{Binding Title}" />
</Style>
</TabControl.ItemContainerStyle>
<TabControl.ContentTemplate>
<DataTemplate DataType="local:ElementsInfoViewModel">
<local:ElementInfoView></local:ElementInfoView>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
</Window>
这就是我们所拥有的(我想这就是你想要的):
首先,我想使用泛型,但这在我的情况下不起作用,因为我有一个ElementsInfoViewModel集合,所以我不能有一个
列表
嗯。。。听起来是个好主意。我会在尝试后给你回复。最重要的是,我喜欢我可以添加属性到一些属性,我不想显示,因为我真的!可以使用数据属性管理任何自定义项(例如,可以使用组合框列而不是文本)
class Person
{
public string Name { get; set; }
public string Surname { get; set; }
public int Age { get; set; }
}
class Car
{
public string Name { get; set; }
public int HP { get; set; }
}
class SystemElement
{
public SystemElement(string title, IList<object> elements)
{
Title = title;
Elements = new ReadOnlyCollection<object>(elements);
}
public string Title { get; set; }
public ReadOnlyCollection<object> Elements { get; }
}
class ElementScanner
{
public ElementScanner()
{
var data = new List<SystemElement>();
data.Add(new SystemElement("People", new List<object>
{
new Person { Name = "John", Surname = "Doe", Age = 22},
new Person { Name = "Lenny", Surname = "Pegasus", Age = 30},
new Person { Name = "Duffy", Surname = "Duck", Age = 22}
}));
data.Add(new SystemElement("Cars", new List<object>
{
new Car { Name = "Mercedes", HP = 700 },
new Car { Name = "Red Bull", HP = 650 },
new Car { Name = "Ferrari", HP = 600 }
}));
Elements = new ReadOnlyCollection<SystemElement>(data);
}
public ReadOnlyCollection<SystemElement> Elements { get; }
}
class ElementsInfoViewModel
{
public ElementsInfoViewModel()
{
var elementScanner = new ElementScanner();
Elements = elementScanner.Elements;
}
public ReadOnlyCollection<SystemElement> Elements { get; }
}
<UserControl x:Class="Stack.ElementInfoView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Stack"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<local:MyDataGrid ItemsSource="{Binding Elements}" >
</local:MyDataGrid>
</Grid>
</UserControl>
<Window x:Class="Stack.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Stack"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<local:ElementsInfoViewModel />
</Window.DataContext>
<Grid>
<TabControl ItemsSource="{Binding Elements}">
<TabControl.ItemContainerStyle>
<Style TargetType="TabItem">
<Setter Property="Header" Value="{Binding Title}" />
</Style>
</TabControl.ItemContainerStyle>
<TabControl.ContentTemplate>
<DataTemplate DataType="local:ElementsInfoViewModel">
<local:ElementInfoView></local:ElementInfoView>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
</Window>