C# 将WPF DataGrid绑定到列表<;界面>;
你好,我有一个数据网格,我有不同的报告,我想显示。我将改变课程,使它们在这里变短,但想法是一样的 假设我有一个名为IReports的接口C# 将WPF DataGrid绑定到列表<;界面>;,c#,wpf,datagrid,C#,Wpf,Datagrid,你好,我有一个数据网格,我有不同的报告,我想显示。我将改变课程,使它们在这里变短,但想法是一样的 假设我有一个名为IReports的接口 public interface IReports { } 还有三个班级,叫做学生,班级,汽车 public class Students:IReports { public string Name { get; set; } } public class Classes : IReports { public string Class
public interface IReports
{
}
还有三个班级,叫做学生,班级,汽车
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; }
}
名单
private List<IReports> _reportsTable;
public List<IReports> ReportsTable
{
get { return _reportsTable; }
set { SetProperty(ref (_reportsTable), value); }
}
private List\u报告稳定;
公共列表报告稳定
{
获取{return\u reportsTable;}
set{SetProperty(ref(_reportsTable),value);}
}
数据网格
<DataGrid ItemsSource="{Binding ReportsList}"
Grid.Column="1"
Grid.Row="0"
AutoGenerateColumns="True"
Grid.RowSpan="6"/>
好的,这里重要的是它们都有不同的属性名称,有些属性名称更多,有些属性名称更少。如何绑定DataGrid以查看不同的属性?如果这有什么区别的话,这就是MVVM
更新:这将始终一次只使用一个类。但当有人更改组合框时,它将触发一个事件,该事件将填充IList
你可以滥用
IValueConverter
。为每列创建一个。在
ValueConverter
中,可以测试类型并返回正确的属性。我的意思的一个例子:
public class NameValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is Students)
{
return (value as Students).Name;
}
if (value is Classes)
{
return (value as Classes).ClassName;
}
if (value is Cars)
{
return (value as Cars).CarType;
}
return "";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
要使用它,请将其作为资源添加到DataGrid:
<DataGrid.Resources>
<local:NameValueConverter x:Key="NameValueConverter"></local:NameValueConverter>
</DataGrid.Resources>
不过,此解决方案仅适用于只读数据网格(编辑会引发NotImplementedException)。您可能会滥用
IValueConverter
。为每列创建一个。在
ValueConverter
中,可以测试类型并返回正确的属性。我的意思的一个例子:
public class NameValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is Students)
{
return (value as Students).Name;
}
if (value is Classes)
{
return (value as Classes).ClassName;
}
if (value is Cars)
{
return (value as Cars).CarType;
}
return "";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
要使用它,请将其作为资源添加到DataGrid:
<DataGrid.Resources>
<local:NameValueConverter x:Key="NameValueConverter"></local:NameValueConverter>
</DataGrid.Resources>
不过,此解决方案仅适用于只读数据网格(编辑会引发NotImplementedException)。据我所知,您希望数据网格显示实现接口的各种类的各种列。如果钩住DataGrid的LoadingRow事件,可以看到运行时处理的对象类型。可以使用反射从行的datacontext中获取属性,然后检查datagrid以查看该属性是否有列。如果没有,请添加它 如果列表中有不同的类型,并且一个类型没有另一个类型中的属性(比如汽车没有名称属性,学生和汽车都在列表中),则会出现问题。如果编辑对象上不存在的属性的列,将引发异常。为了解决这个问题,您需要一个将其应用于datagridcells的转换器和样式。为了好玩,我还添加了一个datatrigger,如果禁用,它会将单元格的背景更改为银色。一个问题是,如果您需要更改单元格的样式,那么您必须在代码中进行更改(或者根据您的样式更改代码中的样式) XAML: 转换器的CS:
public class ReadOnlyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null)
{
var prop = value.GetType().GetProperty(parameter as string);
if (prop != null)
return true;
}
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
完整地说,这是我用来设置屏幕截图数据的:
public List<IReports> ReportsTable { get; set; }
public MainWindow()
{
InitializeComponent();
ReportsTable = new List<IReports>() {
new Students() { Name = "Student 1" },
new Students() { Name = "Student 2" },
new Classes() { ClassName="CS 101", StudentName = "Student 3" },
new Cars() { CarType = "Truck", Mileage=12345, StudentName = "Student 4" }
};
this.DataContext = this;
}
public List ReportsTable{get;set;}
公共主窗口()
{
初始化组件();
ReportsTable=新列表(){
新生(){Name=“Student 1”},
新生(){Name=“Student 2”},
新类(){ClassName=“CS 101”,StudentName=“Student 3”},
新车(){CarType=“Truck”,里程数=12345,StudentName=“Student 4”}
};
this.DataContext=this;
}
截图:
public List<IReports> ReportsTable { get; set; }
public MainWindow()
{
InitializeComponent();
ReportsTable = new List<IReports>() {
new Students() { Name = "Student 1" },
new Students() { Name = "Student 2" },
new Classes() { ClassName="CS 101", StudentName = "Student 3" },
new Cars() { CarType = "Truck", Mileage=12345, StudentName = "Student 4" }
};
this.DataContext = this;
}
据我所知,您希望datagrid显示实现接口的各种类的各种列。如果钩住DataGrid的LoadingRow事件,可以看到运行时处理的对象类型。可以使用反射从行的datacontext中获取属性,然后检查datagrid以查看该属性是否有列。如果没有,请添加它 如果列表中有不同的类型,并且一个类型没有另一个类型中的属性(比如汽车没有名称属性,学生和汽车都在列表中),则会出现问题。如果编辑对象上不存在的属性的列,将引发异常。为了解决这个问题,您需要一个将其应用于datagridcells的转换器和样式。为了好玩,我还添加了一个datatrigger,如果禁用,它会将单元格的背景更改为银色。一个问题是,如果您需要更改单元格的样式,那么您必须在代码中进行更改(或者根据您的样式更改代码中的样式) XAML: 转换器的CS:
public class ReadOnlyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null)
{
var prop = value.GetType().GetProperty(parameter as string);
if (prop != null)
return true;
}
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
完整地说,这是我用来设置屏幕截图数据的:
public List<IReports> ReportsTable { get; set; }
public MainWindow()
{
InitializeComponent();
ReportsTable = new List<IReports>() {
new Students() { Name = "Student 1" },
new Students() { Name = "Student 2" },
new Classes() { ClassName="CS 101", StudentName = "Student 3" },
new Cars() { CarType = "Truck", Mileage=12345, StudentName = "Student 4" }
};
this.DataContext = this;
}
public List ReportsTable{get;set;}
公共主窗口()
{
初始化组件();
ReportsTable=新列表(){
新生(){Name=“Student 1”},
新生(){Name=“Student 2”},
新类(){ClassName=“CS 101”,StudentName=“Student 3”},
新车(){CarType=“Truck”,里程数=12345,StudentName=“Student 4”}
};
this.DataContext=this;
}
截图:
public List<IReports> ReportsTable { get; set; }
public MainWindow()
{
InitializeComponent();
ReportsTable = new List<IReports>() {
new Students() { Name = "Student 1" },
new Students() { Name = "Student 2" },
new Classes() { ClassName="CS 101", StudentName = "Student 3" },
new Cars() { CarType = "Truck", Mileage=12345, StudentName = "Student 4" }
};
this.DataContext = this;
}
与其使用转换器选项来显示给定的字符串值,不如在基本接口中添加一个getter。然后,每个类只返回它自己的,几乎就像每个对象都可以重写它的“ToString()”方法一样,因为您将创建一个列表,例如用于显示或拾取,该值无论如何都是只读的,使其成为一个getter
public interface IReports
{
string ShowValue {get;}
}
public class Students:IReports
{
public string Name { get; set; }
public string ShowValue { get { return Name; } }
}
public class Classes : IReports
{
public string ClassName { get; set; }
public string StudentName { get; set; }
public string ShowValue { get { return ClassName + " - " + StudentName ; } }
}
public class Cars : IReports
{
public int Mileage { get; set; }
public string CarType { get; set; }
public string StudentName { get; set; }
public string ShowValue { get { return CarType + "(" + Mileage + ") - " + StudentName; } }
}
然后在视图模型管理器中
public class YourMVVMClass
{
public YourMVVMClass()
{
SelectedRptRow = null;
ReportsTable = new List<IReports>()
{
new Students() { Name = "Student 1" },
new Students() { Name = "Student 2" },
new Classes() { ClassName="CS 101", StudentName = "Student 3" },
new Cars() { CarType = "Truck", Mileage=12345, StudentName = "Student 4" }
};
}
// This get/set for binding your data grid to
public List<IReports> ReportsTable { get; set; }
// This for the Selected Row the data grid binds to
public IReports SelectedRptRow { get; set; }
// This for a user double-clicking to select an entry from
private void Control_OnMouseDoubleClick(object sender, MouseButtonEventArgs e)
{
// Now, you can look directly at the SelectedRptRow
// as in the data-grid binding declaration.
if (SelectedRptRow is Classes)
MessageBox.Show("User selected a class item");
else if( SelectedRptRow is Cars)
MessageBox.Show("User selected a car item");
else if( SelectedRptRow is Students)
MessageBox.Show("User selected a student item");
else
MessageBox.Show("No entry selected");
}
}
因此,通过MVVM上的一个布尔属性,我可以显示和隐藏不同的控件。。。可能是管理员和标准用户选项
我还使用了转换器来处理日期,以实现其他格式化目的。与其使用转换器选项来显示给定的字符串值,不如在基本接口中添加一个getter。然后,每个类只返回它自己的,几乎就像每个对象都可以重写它的“ToString()”方法一样,因为您将创建一个列表,例如用于显示或拾取,该值无论如何都是只读的,使其成为一个getter
public interface IReports
{
string ShowValue {get;}
}
public class Students:IReports
{
public string Name { get; set; }
public string ShowValue { get { return Name; } }
}
public class Classes : IReports
{
public string ClassName { get; set; }
public string StudentName { get; set; }
public string ShowValue { get { return ClassName + " - " + StudentName ; } }
}
public class Cars : IReports
{
public int Mileage { get; set; }
public string CarType { get; set; }
public string StudentName { get; set; }
public string ShowValue { get { return CarType + "(" + Mileage + ") - " + StudentName; } }
}
然后在视图模型管理器中
public class YourMVVMClass
{
public YourMVVMClass()
{
SelectedRptRow = null;
ReportsTable = new List<IReports>()
{
new Students() { Name = "Student 1" },
new Students() { Name = "Student 2" },
new Classes() { ClassName="CS 101", StudentName = "Student 3" },
new Cars() { CarType = "Truck", Mileage=12345, StudentName = "Student 4" }
};
}
// This get/set for binding your data grid to
public List<IReports> ReportsTable { get; set; }
// This for the Selected Row the data grid binds to
public IReports SelectedRptRow { get; set; }
// This for a user double-clicking to select an entry from
private void Control_OnMouseDoubleClick(object sender, MouseButtonEventArgs e)
{
// Now, you can look directly at the SelectedRptRow
// as in the data-grid binding declaration.
if (SelectedRptRow is Classes)
MessageBox.Show("User selected a class item");
else if( SelectedRptRow is Cars)
MessageBox.Show("User selected a car item");
else if( SelectedRptRow is Students)
MessageBox.Show("User selected a student item");
else
MessageBox.Show("No entry selected");
}
}
所以,一个布尔
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" },
};
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; }
}
private IList<IReports> _reportsTable;
public IList<IReports> ReportsTable
{
get { return _reportsTable; }
set { SetProperty(ref _reportsTable, value as ReportsList ?? new ReportsList(value)); }
}