.Net Winforms DataGridView DataBinding - PullRequest
       4

.Net Winforms DataGridView DataBinding

0 голосов
/ 27 февраля 2019

Я поддерживаю приложение winforms, которое используется группой финансового планирования.На одном из главных экранов программы, называемом Сетка планирования, используется сетка данных, чтобы показать изменения в финансовом плане неделя за неделей для групп продуктов, которые продает компания.

В таблице данных отображаются финансовые показатели по неделям для выбранных групп продуктов.Существует одна колонка для каждой недели года (всего 52 колонки).Существует 15 финансовых показателей для каждой группы продуктов, поэтому выбор группы продуктов в пользовательском интерфейсе добавляет 15 последовательных строк данных в представление данных.

В настоящее время программа возвращает данные из бизнес-уровня в двухмерноммассив.Затем он строит строки программно, циклически проходя по массиву и используя каждую строку массива для создания строки в сетке данных.

Мой вопрос касается привязки данных.Я хотел бы изменить код для использования привязки данных для автоматического построения сетки данных.По сути, я хочу вернуть список бизнес-объектов (PlannedProductGroup) и установить этот список в качестве источника данных для сетки данных.Однако моя проблема в том, что я не знаю, как справиться с привязкой.Когда в прошлом я делал привязку сетки данных, каждый объект в источнике данных равняется одной строке в сетке.Однако на этот раз один объект будет создавать 15 строк в сетке, и я не уверен, возможно ли это с помощью привязки данных.

Возможно ли то, что я хочу сделать?Если да, кто-нибудь знает, как реализовать этот более сложный сценарий привязки данных?

Редактировать - Добавление фрагментов текущей реализации, которые я хочу реорганизовать

        'create rows
        Dim _planningDetailsArray(,) As Single = Load_Planning_Details()
        For Each productGroup As ProductGroup In _productGroups
            If Product_Group_Checked(productGroup.ID) Then

                'Measure.ProductGroupId
                productGroupId = Convert.ToInt32(_planningDetailsArray((productGroupCounter * Measure.Count) + Measure.ProductGroupId, 0))
                productGroupName = _productGroups.Find(Function(p) p.ID = productGroupId).Name
                myDataGridViewRow = New DataGridViewRow
                myDataGridViewRow.Cells.Add(NewDataGridViewIntegerCell(productGroupId))
                For weekCounter As Integer = 0 To 51
                    Dim priceMultipleId As Integer = CInt(_planningDetailsArray((productGroupCounter * Measure.Count) + Measure.PriceMultiple, weekCounter))
                    Dim priceMultipleName As String = _priceMultiples.Find(Function(p) p.ID = priceMultipleId).Name
                    Dim complexOffer = String.Empty
                    If CInt(_planningDetailsArray((productGroupCounter * Measure.Count) + Measure.Complex, weekCounter)) = 1 Then
                        complexOffer = " *"
                    End If
                    Dim retailPrice As String = Math.Abs(_planningDetailsArray((productGroupCounter * Measure.Count) + Measure.RetailPrice, weekCounter)).ToString("C2") & complexOffer
                    Dim cellText = $"{priceMultipleName} {retailPrice}"
                    _myDataGridViewCell = NewDataGridViewTextCell(cellText)
                    If _blackoutCollection.Contains((weekCounter + 1).ToString) Then
                        _myDataGridViewCell.Style.BackColor = Color.LightSlateGray
                    Else
                        _myDataGridViewCell.Style.BackColor = Color.LightGray
                    End If
                    myDataGridViewRow.Cells.Add(_myDataGridViewCell)
                Next
                myDataGridViewRow.Cells.Add(NewDataGridViewIntegerCell(Measure.ProductGroupId))
                myDataGridViewRow.HeaderCell.Value = productGroupName
                myDataGridViewRow.ReadOnly = True
                _dgvPlanningDetails.Rows.Add(myDataGridViewRow)

                'Measure.Retail_Price
                myDataGridViewRow = New DataGridViewRow
                myDataGridViewRow.Cells.Add(NewDataGridViewIntegerCell(productGroupId))
                For weekCounter As Integer = 0 To 51
                    myDataGridViewRow.Cells.Add(NewDataGridViewSingleCellDash(_planningDetailsArray((productGroupCounter * Measure.Count) + Measure.RetailPrice, weekCounter), False, True))
                Next
                myDataGridViewRow.Cells.Add(NewDataGridViewIntegerCell(Measure.RetailPrice))
                myDataGridViewRow.HeaderCell.Value = "   Retail Price"
                myDataGridViewRow.Visible = ToolStripCheckBox_Retail.Checked
                myDataGridViewRow.ReadOnly = _isReadOnly
                myDataGridViewRow.DefaultCellStyle.Format = "C2"
                _dgvPlanningDetails.Rows.Add(myDataGridViewRow)

                'Measure.Price_Multiple_ID
                myDataGridViewRow = New DataGridViewRow
                myDataGridViewRow.Cells.Add(NewDataGridViewIntegerCell(productGroupId))
                For weekCounter As Integer = 0 To 51
                    myDataGridViewRow.Cells.Add(NewDataGridViewComboBoxCell(_priceMultiples.AsEnumerable(), Convert.ToInt32(_planningDetailsArray((productGroupCounter * Measure.Count) + Measure.PriceMultiple, weekCounter))))
                Next
                myDataGridViewRow.Cells.Add(NewDataGridViewIntegerCell(Measure.PriceMultiple))
                myDataGridViewRow.HeaderCell.Value = "   Price Multiple"
                myDataGridViewRow.Visible = ToolStripCheckBox_Retail.Checked
                myDataGridViewRow.ReadOnly = _isReadOnly
                _dgvPlanningDetails.Rows.Add(myDataGridViewRow)            
            End If
        Next

Редактировать 2 - Добавление кода POC Я создал быстрый проект Proof of Concept, чтобы показать, что я хочу сделать.Каждый объект person должен состоять из двух строк в сетке данных, одной строки для списка FavoriteColors и одной строки для списка FavoriteFoods.

Форма общедоступного класса1

Public Sub New ()

    ' This call is required by the designer.
    InitializeComponent()

    ' Add any initialization after the InitializeComponent() call.

End Sub

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    Dim people = New List(Of Person)

    Dim person1 = New Person
    person1.Name = "Jim"
    person1.FavoriteColors = New List(Of String)(New String() {"Red", "Green", "Blue"})
    person1.FavoriteFoods = New List(Of String)(New String() {"Pizza", "Salad", "Burger"})

    Dim person2 = New Person
    person2.Name = "Bob"
    person2.FavoriteColors = New List(Of String)(New String() {"Yellow", "Black", "Pink"})
    person2.FavoriteFoods = New List(Of String)(New String() {"Hotdog", "French Fries", "Steak"})

    people.Add(person1)
    people.Add(person2)

    DataGridView1.DataSource = people
End Sub

Private Class Person
    Public Property Name As String
    Public Property FavoriteColors As List(Of String)
    Public Property FavoriteFoods As List(Of  String)
End Class

Конечный класс

Ответы [ 2 ]

0 голосов
/ 28 февраля 2019

Трудно представить, что вы описываете.Итак, я буду упрощен и сосредоточусь на примере Person.

Использование List<T> в качестве DataSource к DataGridView будет работать.Однако он будет отображать только «открытые» открытые «Свойства», которые НЕ являются КОЛЛЕКЦИЯМИ.

В примере класса Person в сетке будет отображаться только столбец «имя».И это имеет смысл ... сетка не будет знать, как сделать одно значение «Ячейки» равным совокупности значений.

Поэтому, если вы хотите отобразить каждый элемент в списке как «столбец» в сетке….Тогда вам нужно будет сделать это.Я не уверен, поможет ли привязка или какой-либо другой механизм, однако я уверен, что не составит труда создать метод, который с учетом List<T> вернет DataTable, настроенный так, как вы описываете.

Учитывая, что есть ДВА (2) списка для каждого Person (я предполагаю, что 15 в исходном примере), это означает, что для каждого Person. будет две строки. Если это правильно, то о единственномвам нужно беспокоиться о том ... СКОЛЬКО КОЛОНН вам понадобится, предоставив список Person, чтобы каждый человек мог иметь разное количество предметов (цветов, продуктов) в одном из списков.

Кажется очевидным, что количество столбцов, которое вам понадобится, будет количеством элементов из самого большого (Цвета, Продукты) Списка ВСЕХ Person в списке people.Я предполагаю, что в исходном случае это всегда будет 52, однако было бы разумно проверить это, так как иначе гарантирован сбой.

Чтобы помочь, нужен метод, чтобы получить количество столбцов для DataTable.Этот метод GetMaxColumns принимает значение List<Person> и возвращает счет из самых больших списков «Цвет» и «Еда».Это гарантирует, что мы останемся в пределах досягаемости независимо от размера любого цвета или списка продуктов.Это может выглядеть следующим образом…

Private Function GetMaxColumns(people As List(Of Person)) As Int32
    Dim max = 0
    For Each person In people
        If (person.FavoriteColors.Count > max) Then
            max = person.FavoriteColors.Count
        End If
        If (person.FavoriteFoods.Count > max) Then
            max = person.FavoriteFoods.Count
        End If
    Next
    Return max
End Function

Далее метод GetDataTable принимает List<Person> и возвращает DataTable с правильным количеством столбцов для данного List<Person>.Именно здесь наименование столбцов может быть удобным.

Private Function GetDataTable(people As List(Of Person)) As DataTable
    Dim dt = New DataTable()
    Dim maxColumns = GetMaxColumns(people)
    For index = 0 To maxColumns
        dt.Columns.Add()
    Next
    Return dt
End Function

Метод FillDataTable принимает List<Person>, а DataTable затем заполняет DataTable из List<Person>, как описано выше.Для каждого человека будет две строки, однако вторая строка под столбцом имени будет пустой, поскольку она будет иметь то же имя, что и предыдущая строка.

В приведенном ниже коде цикл начинается с каждой Person в списке.Имя добавляется в строку, а затем проходит по списку «Цвет» для добавления каждого значения цвета.Затем добавляется вторая строка, которая содержит значения в списке «Еда».Имя не добавляется во второй ряд.

Private Sub FillDataTable(people As List(Of Person), dt As DataTable)
    Dim dr As DataRow
    Dim curCol = 0
    For Each person In people
        dr = dt.NewRow()
        curCol = 0
        dr(curCol) = person.Name.ToString()
        curCol += 1
        For Each favColor In person.FavoriteColors
            dr(curCol) = favColor
            curCol += 1
        Next
        dt.Rows.Add(dr)
        dr = dt.NewRow()
        curCol = 1
        For Each favFood In person.FavoriteFoods
            dr(curCol) = favFood
            curCol += 1
        Next
        dt.Rows.Add(dr)
    Next
End Sub

Наконец, все это может выглядеть примерно так, как показано ниже ...

 Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    Dim people = New List(Of Person)
    Dim person = New Person
    person.Name = "Jim"
    person.FavoriteColors = New List(Of String)(New String() {"Red", "Green", "Blue"})
    person.FavoriteFoods = New List(Of String)(New String() {"Pizza", "Salad", "Burger"})
    people.Add(person)

    person = New Person
    person.Name = "Bob"
    person.FavoriteColors = New List(Of String)(New String() {"Yellow", "Black", "Pink"})
    person.FavoriteFoods = New List(Of String)(New String() {"Hotdog", "French Fries", "Steak"})
    people.Add(person)

    person = New Person
    person.Name = "John"
    person.FavoriteColors = New List(Of String)(New String() {"Purple", "Olive Grey", "Polka Dot"})
    person.FavoriteFoods = New List(Of String)(New String() {"Ice Cream", "Fish", "Crutons"})
    people.Add(person)

    Dim GridTable = GetDataTable(people)
    FillDataTable(people, GridTable)
    DataGridView1.DataSource = GridTable
End Sub

Надеюсь, это поможет.

0 голосов
/ 27 февраля 2019

Это зависит от структуры ваших данных в базе данных.Например, хранятся ли данные отношений в 52 столбцах, по одному на каждую неделю?Выполняет ли запрос PIVOT для этих данных для генерации 52 столбцов?Есть ли один запрос, заполняющий существующий двумерный массив?Несколько запросов?

В любом случае, если вы можете выполнить рефакторинг кода и поместить результаты запроса в DataTable, вы просто привязываете этот DataTable к DataGridView следующим образом:

myDataGridView.DataSource = myDataTable

Просто убедитесь, чтосвойство AutoGenerateColumns объекта DataGridView имеет значение True.И это все, что нужно сделать, связать данные.Однако вы должны понимать, что такое простое связывание будет отображать ВСЕ поля в DataTable.Вам придется вручную установить столбцы, которые вы не хотите отображать, в Visible = False в вашем коде.

И наоборот, вы можете создать объект передачи данных (DTO), который, по сути, является просто классом с только свойствами,Затем вы создадите экземпляр одного объекта DTO для каждой строки в результатах запроса и добавите все эти DTO в список (из T).Затем свяжите этот List (Of T) с DataGridView:

Dim myResults As List(Of PlanningDetailDTO) = myFunction.ReadTheData()
myDataGridView.DataSource = myResults

Но я бы не хотел создавать класс DTO с 52+ полями в нем.Преимущество метода DataTable заключается в том, что он создает столбцы в DataTable на основе результатов запроса.

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