Связывание объектов DataGridView C#

У меня есть DataGridView и список объектов, которые я хотел бы показать.

Объекты это:

public class Entity
{
    public int ID { get; set; }
}    

public class Travel: Entity
{
    public Service Service { get; set; }
    public City Source { get; set; }
    public City Destiny { get; set; }
    public decimal Price { get; set; }
}

public class Service: Entity
{
    public string Name { get; set; }
}

public class City: Entity
{
    public string Name { get; set; } // Max 50 chars
}

В своей форме я связываю список объектов путешествия следующим образом:

List<Travel> travels = logic.GetAllTravels();
DgvRecorridos.DataSource = travels;

И я получаю следующее:

Я хотел бы получить название службы, исходный город и город судьбы.

Заранее спасибо.

4 ответа

Вместо того, чтобы делать следующие коды ниже:

List<Travel> travels = logic.GetAllTravels();  
DgvRecorridos.DataSource = travels;  

Сделай это:

List<Travel> travels = logic.GetAllTravels();  
BindingSource bs = new BindingSource();  
bs.DataSource = travels;  
DgvRecorridos.AutoGenerateColumn = false;  
DgvRecorridos.DataSource = bs;  

Затем добавьте столбцы вручную:

DataGridViewColumn col1 = new DataGridViewTextBoxColumn();  
col1.DataPropertyName = "Service.Name";  
col1.HeaderText = "Service Name";  
dataGridView1.Columns.Add(col1);  

DataGridViewColumn col2 = new DataGridViewTextBoxColumn();  
col2.DataPropertyName = "City.Name";  
col2.HeaderText = "City Name";  
dataGridView1.Columns.Add(col2);  

DataGridViewColumn col3 = new DataGridViewTextBoxColumn();  
col3.DataPropertyName = "City.Name";  
col3.HeaderText = "Destiny Name";  
dataGridView1.Columns.Add(col3);  

DataGridViewColumn col4 = new DataGridViewTextBoxColumn();  
col4.DataPropertyName = "Price";  
col4.HeaderText = "Price";  
dataGridView1.Columns.Add(col4);  

Затем добавьте обработчик события форматирования ячейки для DataGridView:

private void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)  
{  
    if (dataGridView1.Rows[e.RowIndex].DataBoundItem != null &&   
        dataGridView1.Columns[e.ColumnIndex].DataPropertyName.Contains("."))  
    {  
        e.Value = BindProperty(dataGridView1.Rows[e.RowIndex].DataBoundItem,
            dataGridView1.Columns[e.ColumnIndex].DataPropertyName);  
    }  
}  

private string BindProperty(object property, string propertyName)  
{  
    string retValue = "";  

    if (propertyName.Contains("."))  
    {  
        PropertyInfo[] arrayProperties;  
        string leftPropertyName;  

        leftPropertyName = propertyName.Substring(0, propertyName.IndexOf("."));  
        arrayProperties = property.GetType().GetProperties();  

        foreach (PropertyInfo propertyInfo in arrayProperties)  
        {  
            if (propertyInfo.Name == leftPropertyName)  
            {  
                retValue = BindProperty(propertyInfo.GetValue(property, null),   
                propertyName.Substring(propertyName.IndexOf(".") + 1));  
                break;  
            }  
        }  
    }  
    else  
    {  
        Type propertyType;  
        PropertyInfo propertyInfo;  

        propertyType = property.GetType();  
        propertyInfo = propertyType.GetProperty(propertyName);  
        retValue = propertyInfo.GetValue(property, null).ToString();  
    }  

    return retValue;  
}  

Для полного руководства по форматированию ячеек, просмотрите здесь в блоге Антонио Белло, это - то, где я получил идею. ^_^ Я также задавал здесь на SO тот же вопрос два дня назад, и получил такие же ответы, как и вы, и я знаю, что это не то, что вы хотите сделать тоже. Надеюсь, это поможет вам.

List<Travel> travels = logic.GetAllTravels();
var _bind = from a in travels
            select new
            {
                Servicename = a.Service.Name,
                SourceName = a.Source.Name,
                DestinyName = a.Destiny.Name,
                Price = a.Price
            };
DgvRecorridos.DataSource = _bind;

или же

List<Travel> travels = logic.GetAllTravels();
var _bind = travels.Select(a => new 
            { 
                Servicename = a.Service.Name,
                SourceName = a.Source.Name,
                DestinyName = a.Destiny.Name,
                Price = a.Price
            });
DgvRecorridos.DataSource = _bind;

Весь этот код в методах CellFormatting и BindProperty кажется чрезмерным. Я имею в виду, что что-то должно в основном делать это, но я думаю, что это уже сделано. Я реализую INotifyPropertyChanged в объекте, который я хочу привязать к строке сетки, и помещаю эти объекты в BindingList. BindingList напрямую используется в качестве источника данных для сетки.

Этот подход означает немного больше ввода в базовый класс сущностей, который вы отображаете в строку сетки, но я думаю, что это намного экономит в других местах. Для реализации INotifyPropertyChanged в вашем классе:

public class Entity: INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public Entity()
        {
            Selected = false;
        }

        private bool _selected;
        public bool Selected
        {
            get
            {
                return _selected;
            }
            set
            {
                if (_selected != value)
                {
                    _selected = value;
                    OnPropertyChanged(nameof(Selected));
                }
            }
        }

        protected virtual void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

Затем поместите столбец в свою сетку и присвойте ему значение DataPropertyName "Выбрано", чтобы оно соответствовало имени свойства в Entity, которое должно появиться в этом столбце. Очевидно, добавьте любые свойства, которые вы хотите в вашей сетке, соответствующие свойствам объекта. Ключ должен быть уверен, что реализован установщик свойств с помощью вызова PropertyChanged.

Это дает вам двустороннюю связь между сеткой и списком объектов.

Мое личное мнение: даже это слишком много кода. Я постоянно пишу такие вещи, которые делают очевидное: взять свойство по имени и сопоставить его с чем-то другим, которое знает это имя (например, столбец сетки в этом примере). Это просто за пределами моего понимания, почему эти вещи не просто автоматически подключаются. Список и сетка должны иметь достаточно смысла, чтобы самостоятельно составить эту базовую схему. ноль строк кода. Хорошо, одна строка кода. Grid.Datasource = Список. Так вот как я это делаю. Я хотел бы знать меньше строк кода, способ сделать это.

Ваш дизайн такой странный. У меня есть другой подход, чтобы переопределить ToString() Метод ваших классов (Служба и Город), как это:

public class Service: Entity
{
  public string Name { get; set; }
  public override string ToString(){
     return Name;
  }
}

public class City: Entity
{
  public string Name { get; set; } // Max 50 chars
  public override string ToString(){
     return Name;
  }
}

И это работает хорошо. Опять же, ваш дизайн немного странно ^_^

Другие вопросы по тегам