C # grid DataSource Polymorphism

I have a grid and I set DataSourceto List<IListItem>. I want the list to be bound to the base type and not use the properties defined in IListItem. So:

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

How can I snap to the grid so that if mine List<IListItem>contains users, I will see the user field? Edit: Note that any list I want to associate with a Datagrid will consist of one base type.

+3
source share
7 answers

Linking data to lists follows the following strategy:

  • Does the data source implement IListSource? if so, goto 2 with the resultGetList()
  • IList? , ;
  • ITypedList? ()
  • , , public Foo this[int index] ( Foo)? , typeof(Foo)
  • - ? , (list[0])

List<IListItem> "4" , IListItem - TypeDescriptor.GetProperties(typeof(IListItem)).

, :

  • TypeDescriptionProvider, IListItem - , , , IListItem
  • (List<User> ..) - IList -
  • ITypedList ( )
  • - ArrayList (.. ) - !

List<>... 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
            }
        }
    });
}
+5

, List <IListItem> , , , " ".

BindingListView, DataGridViews.

DataGridView Form1_Load().

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

, , , ConvertAll, .

+1

Grid. , , - , . :

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

:

protected string GetUserSpecificProperty(IListItem obj) {
    if (obj is User) {
        return ((User) obj).UserSpecificField
    } else if (obj is Location) {
        return ((Location obj).LocationSpecificField;
    } else { 
        return "";
    }
}
0

, Convert.ChangeType, , DataGrid . , , , , ( ) 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;
    }
0

autogeneratecolumns, ?

0

, IListItem, , , .

, , .

/ 'psuedo code';

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

If you want to use a ListView-based solution, the ObjectListView version for data binding allows you to do this. It reads the public properties of the DataSource and creates columns to display each property. You can combine it with a BindingListView.

It also looks better than the grid :)

0
source

Source: https://habr.com/ru/post/1709407/


All Articles