Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 将WPF DataGrid绑定到列表<;界面>;_C#_Wpf_Datagrid - Fatal编程技术网

C# 将WPF DataGrid绑定到列表<;界面>;

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

你好,我有一个数据网格,我有不同的报告,我想显示。我将改变课程,使它们在这里变短,但想法是一样的

假设我有一个名为IReports的接口

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)); }
}