Jasin
Jasin

Reputation: 425

WPF Datagrid DataGridComboBoxColumn Autogenerate at runtime

I have a datagrid in my vb.net 4.0 wpf project. I have seen many examples in XAML on how to bind a DataGridComboBoxColumn however I need to do this in code as I have auto-generating columns. I switch the datagridbinding source to multiple data sets.

Inside some of these custom classes are a couple lists. I can get text and checkboxes to auto-generate correctly. When it comes across my array (I have tried many different kinds) I just see a textboxcolumn with the words (Collection) in it.

For instance - One screen I am grabbing system information on WMI calls. One of the calls returns all IP addresses on the server (We can have up to 8 IP addresses) I do not want a column per IP address. I would like to include a list of those into the datagrid so you can drop down and see them.

Any suggestions on if this is possible or if I am doing something wrong?

Thank you

Sample Code

Imports System.Collections.ObjectModel

Class MainWindow

Dim ServerInfoArray As ObservableCollection(Of ServerInfo) = New ObservableCollection(Of ServerInfo)

Private ReadOnly _ipAddresses As ObservableCollection(Of String) = New ObservableCollection(Of String)


Private Sub GetInfo(ByVal list As List(Of String))
    For Each server As String In list

        Dim tempip As List(Of String) = New List(Of String)
        Dim sinfo As ServerInfo = New ServerInfo

        tempip.Add("192.129.123.23")
        tempip.Add("23.213.223.21")

        sinfo.IPArray = tempip
        sinfo.Servername = server

        ServerInfoArray.Add(sinfo)

    Next
End Sub

Private Sub Button1_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles Button1.Click

    Dim serverlist As List(Of String) = New List(Of String)
    serverlist.Add("Test")
    serverlist.Add("Random")
    serverlist.Add("Local")
    GetInfo(serverlist)

End Sub

Private Sub Window_Loaded(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded
    Dim Col_Serial As DataGridTextColumn = New DataGridTextColumn()
    Col_Serial.Binding = New Binding("Servername")
    Col_Serial.Header = "Servername"
    Col_Serial.Width = 40

    Dim Col_IPArray = New DataGridComboBoxColumn()
    Col_IPArray.Header = "IP Addresses"
    Col_IPArray.IsReadOnly = True
    Col_IPArray.ItemsSource = Me._ipAddresses
    Col_IPArray.SelectedItemBinding = New Binding("IPArray")

    DataGrid1.Columns.Add(Col_Serial)
    DataGrid1.Columns.Add(Col_IPArray)
    DataGrid1.ItemsSource = ServerInfoArray
End Sub
End Class

Class ServerInfo

Dim _Servername As String
Dim _IPArray As List(Of String)

Public Property Servername() As String
    Get
        Return _Servername
    End Get
    Set(ByVal value As String)
        _Servername = value
    End Set
End Property

Public Property IPArray As List(Of String)
    Get
        Return _IPArray
    End Get
    Set(ByVal value As List(Of String))
        _IPArray = value
    End Set
End Property

Public Sub New()
    _Servername = Nothing
    _IPArray = New List(Of String)
End Sub

End Class

Upvotes: 1

Views: 2674

Answers (1)

blins
blins

Reputation: 2535

Not sure if I am understanding perfectly but if you can live with AutoGenerateColumns = False then you can manipulate the columns, their properties and bindings from code like below. So you could use whatever logic you need to in order setup the columns from code. I tried to show how you can source the combobox column items from a different object than the items in the DataGrid as well.

This is meant to be a simple example so I just did everything in the code-behind but from an MVVM standpoint, it depends on your preferred approach but possibility is that you could accomplish the same logic from a controller class that spins up your view models, etc...

Hope it helps!

XAML...

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
    <StackPanel>
        <Button x:Name="btnRefreshIPList">Refresh list of IPs</Button>
        <DataGrid x:Name="dataGrid1"></DataGrid>
    </StackPanel>
</Window>

Code behind...

Imports System.Collections.ObjectModel

Class MainWindow

    'The list of IPs for column's ItemSource property
    Private ReadOnly _ipAddresses As ObservableCollection(Of String)

    'The items for binding to the DataGrid's ItemsSource
    Private _items As List(Of MyObjectWithIPAddress)

    Public Sub New()

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

        ' Add any initialization after the InitializeComponent() call.
        _ipAddresses = New ObservableCollection(Of String)
        _items = New List(Of MyObjectWithIPAddress)

    End Sub

    Private Sub Window_Loaded(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded

        Me.dataGrid1.AutoGenerateColumns = False

        dataGrid1.Columns.Clear()

        'Example of text column (Text bound to Name property)
        Dim dgTxtCol = New DataGridTextColumn()
        dgTxtCol.Header = "Name"
        dgTxtCol.Binding = New Binding("Name")
        dataGrid1.Columns.Add(dgTxtCol)

        'Example of combobox column (SelectedItem bound to IPAddress)
        Dim dgCmbCol = New DataGridComboBoxColumn()
        dgCmbCol.Header = "IP Address"
        dgCmbCol.ItemsSource = Me._ipAddresses
        dgCmbCol.SelectedItemBinding = New Binding("IPAddress")
        dataGrid1.Columns.Add(dgCmbCol)

        'Add items to DataGrid
        _items.Add(New MyObjectWithIPAddress("foo1"))
        _items.Add(New MyObjectWithIPAddress("foo2"))

        Me.dataGrid1.ItemsSource = Me._items

    End Sub

    ''' <summary>
    ''' To emulate fetching the object that has the IP list
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function GetIpList() As MyObjectWithListOfIPs
        Dim inst = New MyObjectWithListOfIPs
        inst.IPList = New List(Of String)(New String() {"10.0.0.1", "10.0.0.2", "10.0.0.3"})
        Return inst
    End Function

    ''' <summary>
    ''' Updates the ObservableCollection instance based on business object
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub RefreshIpAddresses()
        _ipAddresses.Clear()
        For Each ip As String In GetIpList().IPList
            _ipAddresses.Add(ip)
        Next
    End Sub

    Private Sub btnRefreshIPList_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles btnRefreshIPList.Click
        RefreshIpAddresses()
    End Sub
End Class

''' <summary>
''' Object with response data (e.g., list of IPs)
''' </summary>
''' <remarks></remarks>
Class MyObjectWithListOfIPs

    Private _ipList As List(Of String)
    Public Property IPList() As List(Of String)
        Get
            Return _ipList
        End Get
        Set(ByVal value As List(Of String))
            _ipList = value
        End Set
    End Property

End Class

''' <summary>
''' Disperate object that "has an" address
''' </summary>
''' <remarks></remarks>
Class MyObjectWithIPAddress

    Public Sub New(name As String)
        Me._name = name
    End Sub

    Private _name As String
    Public Property Name() As String
        Get
            Return _name
        End Get
        Set(ByVal value As String)
            _name = value
        End Set
    End Property

    Private _ipAddress As String
    Public Property IPAddress() As String
        Get
            Return _ipAddress
        End Get
        Set(ByVal value As String)
            _ipAddress = value
        End Set
    End Property

End Class

Upvotes: 1

Related Questions