Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/html/83.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#网格数据源多态性_C#_Winforms_Datagridview - Fatal编程技术网

C#网格数据源多态性

C#网格数据源多态性,c#,winforms,datagridview,C#,Winforms,Datagridview,我有一个网格,我正在将数据源设置为列表。我想要的是让列表绑定到底层类型,并显示这些属性,而不是IListItem中定义的属性。因此: public interface IListItem { string Id; string Name; } public class User : IListItem { string Id { get; set; }; string Name { get; set; }; string UserSpecificFiel

我有一个网格,我正在将
数据源
设置为
列表
。我想要的是让列表绑定到底层类型,并显示这些属性,而不是
IListItem
中定义的属性。因此:

public interface IListItem
{
    string Id;
    string Name;
}

public class User : IListItem
{
    string Id { get; set; };
    string Name { get; set; };
    string UserSpecificField { get; set; };
}

public class Location : IListItem
{
    string Id { get; set; };
    string Name { get; set; };
    string LocationSpecificField { get; set; };
}

如何绑定到网格,以便如果我的
列表
包含用户,我将看到特定于用户的字段?编辑:请注意,我要绑定到Datagrid的任何给定列表都将由单个基础类型组成。

为此,您需要使用网格模板列。在template字段中,您需要检查对象的类型,然后获得正确的属性-我建议在代码中创建一个方法来处理这个问题。因此:

<asp:TemplateField HeaderText="PolymorphicField">
    <ItemTemplate>
        <%#GetUserSpecificProperty(Container.DataItem)%>
    </ItemTemplate>
</asp:TemplateField>

我尝试了投影,并尝试使用Convert.ChangeType获取基础类型的列表,但DataGrid不会显示字段。我最终决定在每种类型中创建静态方法以返回标题,实例方法以返回显示字段(作为字符串列表)并将它们组合到一个DataTable中,然后绑定到该DataTable。相当干净,并且它保持了我想要的数据类型和显示之间的分离

下面是我用来创建表的代码:

    DataTable GetConflictTable()
    {
        Type type = _conflictEnumerator.Current[0].GetType();
        List<string> headers = null;
        foreach (var mi in type.GetMethods(BindingFlags.Static | BindingFlags.Public))
        {
            if (mi.Name == "GetHeaders")
            {
                headers = mi.Invoke(null, null) as List<string>;
                break;
            }
        }
        var table = new DataTable();
        if (headers != null)
        {
            foreach (var h in headers)
            {
                table.Columns.Add(h);
            }
            foreach (var c in _conflictEnumerator.Current)
            {
                table.Rows.Add(c.GetFieldsForDisplay());
            }
        }
        return table;
    }
DataTable GetConflictTable()
{
类型类型=_conflictEnumerator.Current[0]。GetType();
列表头=空;
foreach(type.GetMethods(BindingFlags.Static | BindingFlags.Public)中的var mi)
{
如果(mi.Name==“GetHeaders”)
{
headers=mi.Invoke(null,null)作为列表;
打破
}
}
var table=新数据表();
如果(标题!=null)
{
foreach(标题中的var h)
{
表.列.添加(h);
}
foreach(变量c在_conflictEnumerator.Current中)
{
table.Rows.Add(c.GetFieldsForDisplay());
}
}
返回表;
}

只要您确定列表中的所有成员都将是相同的派生类型,下面介绍如何使用“在我的机器上工作”的批准印章

首先,下载,这将允许您将通用列表绑定到DataGridView

对于本例,我只是使用DataGridView创建了一个简单表单,并随机调用代码来加载Form1_load()中的用户或位置列表


如果已知列表中的所有项都是特定的派生类型,只需调用ConvertAll将它们转换为该类型。

当您使用autogeneratecolumns时,它不会自动为您执行此操作?

与列表的数据绑定遵循以下策略:

  • 数据源是否实现了IListSource?如果是,则转到2,结果为
    GetList()
  • 数据源是否实现了IList?如果没有,抛出一个错误;期望列表
  • 数据源是否实现了
    ITypedList
    ?如果是这样,则将其用于元数据(退出)
  • 数据源是否有非对象索引器,
    public Foo this[int index]
    (对于某些
    Foo
    )?如果是,请使用
    typeof(Foo)
    作为元数据
  • 名单上有什么吗?如果是,请使用第一项(
    列表[0]
    )作为元数据
  • 没有可用的元数据
  • List
    属于上面的“4”,因为它有一个类型为
    IListItem
    的类型索引器,因此它将通过
    TypeDescriptor.GetProperties(typeof(IListItem))
    获取元数据

    现在,您有三个选择:

    • 编写一个
      TypeDescriptionProvider
      ,返回
      IListItem
      的属性-我不确定这是否可行,因为您不可能只知道给出的具体类型是什么
      IListItem
    • 使用正确键入的列表(
      list
      etc)-这是一种使用非对象索引器获取
      IList
      的简单方法
    • 编写一个
      ITypedList
      包装器(大量工作)
    • 使用类似于
      ArrayList
      (即没有公共的非对象索引器)的东西-非常黑客
    我更喜欢使用正确类型的
    列表
    。。。这里有一个
    AutoCast
    方法,可以为您执行此操作,而不必知道类型(使用示例)

    请注意,这仅适用于同质数据(即所有对象都相同),并且它需要列表中至少一个对象来推断类型

    // infers the correct list type from the contents
    static IList AutoCast(this IList list) {
        if (list == null) throw new ArgumentNullException("list");
        if (list.Count == 0) throw new InvalidOperationException(
              "Cannot AutoCast an empty list");
        Type type = list[0].GetType();
        IList result = (IList) Activator.CreateInstance(typeof(List<>)
              .MakeGenericType(type), list.Count);
        foreach (object obj in list) result.Add(obj);
        return result;
    }
    // usage
    [STAThread]
    static void Main() {
        Application.EnableVisualStyles();
        List<IListItem> data = new List<IListItem> {
            new User { Id = "1", Name = "abc", UserSpecificField = "def"},
            new User { Id = "2", Name = "ghi", UserSpecificField = "jkl"},
        };
        ShowData(data, "Before change - no UserSpecifiedField");
        ShowData(data.AutoCast(), "After change - has UserSpecifiedField");
    }
    static void ShowData(object dataSource, string caption) {
        Application.Run(new Form {
            Text = caption,
            Controls = {
                new DataGridView {
                    Dock = DockStyle.Fill,
                    DataSource = dataSource,
                    AllowUserToAddRows = false,
                    AllowUserToDeleteRows = false
                }
            }
        });
    }
    
    //从内容推断正确的列表类型
    静态IList AutoCast(此IList列表){
    如果(list==null)抛出新的ArgumentNullException(“list”);
    如果(list.Count==0)抛出新的InvalidOperationException(
    “无法自动广播空列表”);
    类型类型=列表[0]。GetType();
    IList result=(IList)Activator.CreateInstance(typeof(List)
    .MakeGenericType(类型),list.Count);
    foreach(列表中的对象obj)result.Add(obj);
    返回结果;
    }
    //用法
    [状态线程]
    静态void Main(){
    Application.EnableVisualStyles();
    列表数据=新列表{
    新用户{Id=“1”,Name=“abc”,UserSpecificField=“def”},
    新用户{Id=“2”,Name=“ghi”,UserSpecificField=“jkl”},
    };
    ShowData(数据,“更改前-无用户指定字段”);
    ShowData(data.AutoCast(),“更改后-具有UserSpecifiedField”);
    }
    静态void ShowData(对象数据源、字符串标题){
    Application.Run(新表单{
    文本=标题,
    控件={
    新DataGridView{
    Dock=DockStyle.Fill,
    数据源=数据源,
    AllowUserToAddress=false,
    AllowUserToDeleteRows=false
    }
    }
    });
    }
    
    我的建议是在网格中为额外属性动态创建列,并在IListItem中创建一个函数,提供可用列的列表,或者使用对象检查来识别colu
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Windows.Forms;
    using Equin.ApplicationFramework;
    
    namespace DGVTest
    {
        public interface IListItem
        {
            string Id { get; }
            string Name { get; }
        }
    
        public class User : IListItem
        {
            public string UserSpecificField { get; set; }
            public string Id { get; set; }
            public string Name { get; set; }
        }
    
        public class Location : IListItem
        {
            public string LocationSpecificField { get; set; }
            public string Id { get; set; }
            public string Name { get; set; }
        }
    
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
    
            private void InitColumns(bool useUsers)
            {
                if (dataGridView1.ColumnCount > 0)
                {
                    return;
                }
    
                DataGridViewCellStyle gridViewCellStyle = new DataGridViewCellStyle();
    
                DataGridViewTextBoxColumn IDColumn = new DataGridViewTextBoxColumn();
                DataGridViewTextBoxColumn NameColumn = new DataGridViewTextBoxColumn();
                DataGridViewTextBoxColumn DerivedSpecificColumn = new DataGridViewTextBoxColumn();
    
                IDColumn.DataPropertyName = "ID";
                IDColumn.HeaderText = "ID";
                IDColumn.Name = "IDColumn";
    
                NameColumn.DataPropertyName = "Name";
                NameColumn.HeaderText = "Name";
                NameColumn.Name = "NameColumn";
    
                DerivedSpecificColumn.DataPropertyName = useUsers ? "UserSpecificField" : "LocationSpecificField";
                DerivedSpecificColumn.HeaderText = "Derived Specific";
                DerivedSpecificColumn.Name = "DerivedSpecificColumn";
    
                dataGridView1.Columns.AddRange(
                    new DataGridViewColumn[]
                        {
                            IDColumn,
                            NameColumn,
                            DerivedSpecificColumn
                        });
    
                gridViewCellStyle.SelectionBackColor = Color.LightGray;
                gridViewCellStyle.SelectionForeColor = Color.Black;
                dataGridView1.RowsDefaultCellStyle = gridViewCellStyle;
            }
    
            public static void BindGenericList<T>(DataGridView gridView, List<T> list)
            {
                gridView.DataSource = new BindingListView<T>(list);
            }
    
            private void Form1_Load(object sender, EventArgs e)
            {
                dataGridView1.AutoGenerateColumns = false;
    
                Random rand = new Random();
    
                bool useUsers = rand.Next(0, 2) == 0;
    
                InitColumns(useUsers);
    
                if(useUsers)
                {
                    TestUsers();
                }
                else
                {
                    TestLocations();
                }
    
            }
    
            private void TestUsers()
            {
                List<IListItem> items =
                    new List<IListItem>
                        {
                            new User {Id = "1", Name = "User1", UserSpecificField = "Test User 1"},
                            new User {Id = "2", Name = "User2", UserSpecificField = "Test User 2"},
                            new User {Id = "3", Name = "User3", UserSpecificField = "Test User 3"},
                            new User {Id = "4", Name = "User4", UserSpecificField = "Test User 4"}
                        };
    
    
                BindGenericList(dataGridView1, items.ConvertAll(item => (User)item));
            }
    
            private void TestLocations()
            {
                List<IListItem> items =
                    new List<IListItem>
                        {
                            new Location {Id = "1", Name = "Location1", LocationSpecificField = "Test Location 1"},
                            new Location {Id = "2", Name = "Location2", LocationSpecificField = "Test Location 2"},
                            new Location {Id = "3", Name = "Location3", LocationSpecificField = "Test Location 3"},
                            new Location {Id = "4", Name = "Location4", LocationSpecificField = "Test Location 4"}
                        };
    
    
                BindGenericList(dataGridView1, items.ConvertAll(item => (Location)item));
            }
        }
    }
    
    DerivedSpecificColumn.DataPropertyName = useUsers ? "UserSpecificField" : "LocationSpecificField"; // obviously need to bind to the derived field
    
    public static void BindGenericList<T>(DataGridView gridView, List<T> list)
    {
        gridView.DataSource = new BindingListView<T>(list);
    }
    
    dataGridView1.AutoGenerateColumns = false; // Be specific about which columns to show
    
    BindGenericList(dataGridView1, items.ConvertAll(item => (User)item));
    BindGenericList(dataGridView1, items.ConvertAll(item => (Location)item));
    
    // infers the correct list type from the contents
    static IList AutoCast(this IList list) {
        if (list == null) throw new ArgumentNullException("list");
        if (list.Count == 0) throw new InvalidOperationException(
              "Cannot AutoCast an empty list");
        Type type = list[0].GetType();
        IList result = (IList) Activator.CreateInstance(typeof(List<>)
              .MakeGenericType(type), list.Count);
        foreach (object obj in list) result.Add(obj);
        return result;
    }
    // usage
    [STAThread]
    static void Main() {
        Application.EnableVisualStyles();
        List<IListItem> data = new List<IListItem> {
            new User { Id = "1", Name = "abc", UserSpecificField = "def"},
            new User { Id = "2", Name = "ghi", UserSpecificField = "jkl"},
        };
        ShowData(data, "Before change - no UserSpecifiedField");
        ShowData(data.AutoCast(), "After change - has UserSpecifiedField");
    }
    static void ShowData(object dataSource, string caption) {
        Application.Run(new Form {
            Text = caption,
            Controls = {
                new DataGridView {
                    Dock = DockStyle.Fill,
                    DataSource = dataSource,
                    AllowUserToAddRows = false,
                    AllowUserToDeleteRows = false
                }
            }
        });
    }
    
    public interface IListItem
    {
        IList<string> ExtraProperties;
    
    
        ... your old code.
    }
    
    public class User : IListItem
    {
       .. your old code
        public IList<string> ExtraProperties { return new List { "UserSpecificField" } }
    }
    
    foreach(string columnName in firstListItem.ExtraProperties)
    {
         dataGridView.Columns.Add(new DataGridViewTextBoxColumn { DataPropertyName = columnName, HeaderText = columnName );
    }