André Alves de Lima

Talking about Software Development and more…

Adicionando um ComboBox no DataGridView

Hoje eu vou mostrar para você um tema que pode parecer bem simples se você já tem alguma experiência com desenvolvimento de aplicações desktop na plataforma Microsoft, mas que acaba sendo uma grande dor de cabeça para quem está começando: como podemos adicionar um ComboBox no DataGridView?

Nesse caso, temos duas opções: ou criamos uma lista fixa direto no ComboBox, ou alimentamos o ComboBox com uma fonte de dados. Ambas as opções são bem tranquilas de serem implementadas, porém, existe uma pequena diferença caso precisemos ordenar os itens do nosso ComboBox.

Confira no vídeo um passo a passo mostrando como adicionar um ComboBox no DataGridView utilizando essas duas estratégias:

O projeto base

A construção do exemplo desse vídeo toma como base um projeto bem descomplicado, que na realidade é um simples formulário com um DataGridView e três colunas:

No code-behind do formulário nós carregamos manualmente o grid utilizando uma DataTable:

        // C#
        public Form1()
        {
            InitializeComponent();

            var dt = new DataTable();
            dt.Columns.Add("ID", typeof(int));
            dt.Columns.Add("Nome");
            dt.Columns.Add("Cidade", typeof(int));
            dt.Rows.Add(1, "André", 1);// "Limeira");
            dt.Rows.Add(2, "Fulano", 2);// "São Paulo");
            dt.Rows.Add(3, "Beltrano", 3);// "Rio de Janeiro");
            dt.Rows.Add(4, "Sicrano", 4);//"Brasília");
            dataGridView1.DataSource = dt;
        }
    ' VB.NET
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Dim Dt = New DataTable()
        Dt.Columns.Add("ID", GetType(Integer))
        Dt.Columns.Add("Nome")
        Dt.Columns.Add("Cidade")
        Dt.Rows.Add(1, "André", "Limeira")
        Dt.Rows.Add(2, "Fulano", "São Paulo")
        Dt.Rows.Add(3, "Beltrano", "Rio de Janeiro")
        Dt.Rows.Add(4, "Sicrano", "Brasília")
        DataGridView1.DataSource = Dt
    End Sub

Uma outra opção para carregarmos os dados do DataGridView seria trabalharmos com uma colação de objetos:

            // C#
            var dados = new BindingList<object>(new object[]
            {
                new { ID = 1, Nome = "Andre", Cidade = "Limeira" },
                new { ID = 2, Nome = "Fulano", Cidade = "São Paulo" },
                new { ID = 3, Nome = "Beltrano", Cidade = "Rio de Janeiro" },
                new { ID = 4, Nome = "Sicrano", Cidade = "Brasília" }
            });
            dataGridView1.DataSource = dados;
        ' VB.NET
        Dim Dados = New System.ComponentModel.BindingList(Of Object)(New Object() _
        {New With {
            .ID = 1,
            .Nome = "Andre",
            .Cidade = "Limeira"
        }, New With {
            .ID = 2,
            .Nome = "Fulano",
            .Cidade = "São Paulo"
        }, New With {
            .ID = 3,
            .Nome = "Beltrano",
            .Cidade = "Rio de Janeiro"
        }, New With {
            .ID = 4,
            .Nome = "Sicrano",
            .Cidade = "Brasília"
        }})
        DataGridView1.DataSource = Dados

O resultado final seria exatamente o mesmo. Porém, eu preferi trabalhar com DataTable, pois é a maneira mais utilizada para carregarmos dados em projetos Windows Forms.

A ideia é transformarmos a coluna “Cidade” em um ComboBox onde poderemos selecionar uma opção dentro de uma lista de cidades disponíveis.

Criando um ComboBox fixo

A primeira opção seria criarmos um ComboBox fixo. Para isso, vamos até a lista de colunas do nosso grid, clicando na opção “Edit Columns” da smart tag do controle:

Em seguida, transformamos a coluna “Cidade” em uma “DataGridViewComboBoxColumn“:

Agora que a nossa coluna já é um ComboBox, nós teremos à nossa disposição a propriedade “Items“:

Para termos uma lista fixa, basta digitarmos os itens desejados, um em cada linha:

Execute o projeto e veja o resultado:

É importante notar que, ao transformarmos a coluna em ComboBox, todos os itens que estão sendo utilizados no grid devem estar disponíveis na lista do ComboBox. Caso contrário, receberemos um erro como este ao carregarmos o grid:

Ordenando o ComboBox

Ao trabalharmos com listas fixas no ComboBox, nós podemos facilmente ordenar os seus itens através da propriedade “Sorted“:

Criando um ComboBox dinâmico

Esse tipo de lista fixa pode até ser útil em alguns cenários (como quando queremos criar algum “Status” que tem uma quantidade bem delimitada de opções). Porém, em um cenário mais comum nós provavelmente precisaremos carregar a lista de opções de algum lugar (normalmente do banco de dados). Como é que podemos fazer isso?

Colunas do tipo ComboBox possuem uma propriedade chamada “DataSource“. Nós podemos carregar essa propriedade com uma DataTable ou coleção de objetos que deverão ser mostrados no ComboBox. Por exemplo, para carregarmos o ComboBox com uma DataTable, o código ficaria assim:

            // C#
            var dtCidades = new DataTable();
            dtCidades.Columns.Add("Cidade");
            dtCidades.Rows.Add("Limeira");
            dtCidades.Rows.Add("São Paulo");
            dtCidades.Rows.Add("Rio de Janeiro");
            dtCidades.Rows.Add("Brasília");
            dtCidades.Rows.Add("Fortaleza");
            ColumnCidade.ValueMember = "Cidade";
            ColumnCidade.DisplayMember = "Cidade";
            ColumnCidade.DataSource = dtCidades;
        ' VB.NET
        Dim DtCidades = New DataTable()
        DtCidades.Columns.Add("Cidade")
        DtCidades.Rows.Add("Limeira")
        DtCidades.Rows.Add("São Paulo")
        DtCidades.Rows.Add("Rio de Janeiro")
        DtCidades.Rows.Add("Brasília")
        DtCidades.Rows.Add("Fortaleza")
        ColumnCidade.ValueMember = "Cidade"
        ColumnCidade.DisplayMember = "Cidade"
        ColumnCidade.DataSource = DtCidades

Remova a lista de itens que criamos anteriormente na propriedade “Items” da coluna “Cidade” e execute o projeto. O resultado será idêntico ao que tivemos com a lista fixa.

Ordenando um ComboBox com DataSource

Ao utilizarmos uma fonte de dados para o nosso ComboBox, se tentarmos ordená-lo através da propriedade “Sorted“, receberemos um erro:

Nesse caso, nós teremos que ordenar os dados de uma outra maneira. A primeira opção é já trazer os dados ordenados do banco de dados (ou de onde quer que tivermos carregado os dados). Se isso não for possível, nós temos algumas outras opções.

A classe “DataTable” conta com uma propriedade chamada “DefaultView“. Essa propriedade é uma “DataView” onde podemos definir, entre outras coisas, a ordenação padrão da DataTable. Dessa forma, se configurarmos a propriedade “Sort” da “DefaultView“, o ComboBox virá ordenado da maneira que configuramos:

            // C#
            dtCidades.DefaultView.Sort = "Cidade ASC";
            ColumnCidade.DataSource = dtCidades;
        ' VB.NET
        DtCidades.DefaultView.Sort = "Cidade ASC"
        ColumnCidade.DataSource = DtCidades

Se por algum acaso essa opção não funcionar, nós podemos também criar manualmente uma outra DataView com base na nossa DataTable, configuramos o “Sort” dessa DataView e passamos essa DataView para o ComboBox (ao invés da DataTable):

            // C#
            var dtView = new DataView(dtCidades);
            dtView.Sort = "Cidade ASC";
            ColumnCidade.DataSource = dtView;
        ' VB.NET
        Dim DtView = New DataView(DtCidades)
        DtView.Sort = "Cidade ASC"
        ColumnCidade.DataSource = DtView

ValueMember e DisplayMember

Você deve ter percebido que utilizamos as propriedades “ValueMember” e “DisplayMember” na hora de configurarmos a fonte de dados do nosso ComboBox. Qual é o efeito dessas propriedades?

Essas propriedades servem para definirmos colunas específicas para fazermos a ligação entre o item selecionado no ComboBox e a linha do grid. No nosso exemplo, nós estamos armazenando o nome da cidade no grid e estamos alimentando o ComboBox com uma DataTable que só tem uma coluna: “Cidade“. Dessa forma, ambas as propriedades “DisplayMember” e “ValueMember” foram configuradas para “Cidade” (que é o nome da coluna na fonte de dados do ComboBox).

Mas, e se tivéssemos uma coluna “ID” para a cidade (tanto no grid quanto na fonte de dados do ComboBox)? Como é que faríamos para armazenar o “ID” mostrando os nomes das cidades no ComboBox? Simples! Nesse caso nós teríamos que configurar o “ValueMember” como sendo o “ID” e o “DisplayMember” como sendo a coluna onde temos os nomes das cidades (no nosso caso, a coluna se chama “Cidade“).

Veja só como é que ficaria o código:

            // C#
            var dtCidades = new DataTable();
            dtCidades.Columns.Add("ID", typeof(int));
            dtCidades.Columns.Add("Cidade");
            dtCidades.Rows.Add(1, "Limeira");
            dtCidades.Rows.Add(2, "São Paulo");
            dtCidades.Rows.Add(3, "Rio de Janeiro");
            dtCidades.Rows.Add(4, "Brasília");
            dtCidades.Rows.Add(5, "Fortaleza");
            ColumnCidade.ValueMember = "ID";
            ColumnCidade.DisplayMember = "Cidade";
            dtCidades.DefaultView.Sort = "Cidade ASC";
            ColumnCidade.DataSource = dtCidades;

            var dt = new DataTable();
            dt.Columns.Add("ID", typeof(int));
            dt.Columns.Add("Nome");
            dt.Columns.Add("Cidade", typeof(int));
            dt.Rows.Add(1, "André", 1);
            dt.Rows.Add(2, "Fulano", 2);
            dt.Rows.Add(3, "Beltrano", 3);
            dt.Rows.Add(4, "Sicrano", 4);
            dataGridView1.DataSource = dt;
        ' VB.NET
        Dim DtCidades = New DataTable()
        DtCidades.Columns.Add("ID", GetType(Integer))
        DtCidades.Columns.Add("Cidade")
        DtCidades.Rows.Add(1, "Limeira")
        DtCidades.Rows.Add(2, "São Paulo")
        DtCidades.Rows.Add(3, "Rio de Janeiro")
        DtCidades.Rows.Add(4, "Brasília")
        DtCidades.Rows.Add(5, "Fortaleza")
        ColumnCidade.ValueMember = "ID"
        ColumnCidade.DisplayMember = "Cidade"
        DtCidades.DefaultView.Sort = "Cidade ASC"
        ColumnCidade.DataSource = DtCidades

        Dim Dt = New DataTable()
        Dt.Columns.Add("ID", GetType(Integer))
        Dt.Columns.Add("Nome")
        Dt.Columns.Add("Cidade", GetType(Integer))
        Dt.Rows.Add(1, "André", 1)
        Dt.Rows.Add(2, "Fulano", 2)
        Dt.Rows.Add(3, "Beltrano", 3)
        Dt.Rows.Add(4, "Sicrano", 4)
        DataGridView1.DataSource = Dt

Criando a coluna “na mão”

Estou adicionando esta seção alguns dias depois da publicação original porque ela foi enviada como sugestão de um leitor do site. O Marcos Roberto da Fonseca (da empresa ZIPERSoft) me mandou o código que ele utiliza no sistema da empresa dele, onde ele faz a criação das colunas “na mão” (ao invés de utilizar o designer). Achei interessante e, como ele me autorizou compartilhar esse código, aqui vai:

// C#
// Criação das colunas
var colunaCodigo = new DataGridViewTextBoxColumn();
var colunaTipoPreco = new DataGridViewComboBoxColumn();

// Setando o nome nas colunas
colunaCodigo.Name = "colunaCodigo";
colunaTipoPreco.Name = "colunaTipoPreco";

// Configurando o texto do cabeçalho
colunaCodigo.HeaderText= "Código";
colunaTipoPreco.HeaderText= "Tipo de preço";

// Populando a coluna ComboBox com itens de um ArrayList
var lista = new ArrayList();
lista.Add("VISTA");
lista.Add("PRAZO");
lista.Add("ATACADO");
lista.Add("ALTERNATIVO");
lista.Add("CUSTO");

colunaTipoPreco.Items.AddRange(lista.ToArray());

// Adicionando as colunas no grid
seuGrid.Columns.Insert(0, colunaCodigo);
seuGrid.Columns.Insert(1, colunaTipoPreco);

// Configurando a largura das colunas
// Opção 1 - tamanho fixo
colunaCodigo.Width = 120;
colunaTipoPreco.Width = 190;

// Opção 2 - ajuste automático dependendo do conteúdo
seuGrid.AutoResizeColumns();
seuGrid.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;

// Configurando colunas como "somente leitura"
colunaCodigo.ReadOnly = true;

// Configurando a altura da linha do grid
seuGrid.RowTemplate.Height = 50;
' VB.NET
' Criação das colunas
Dim ColunaCodigo = New DataGridViewTextBoxColumn()
Dim ColunaTipoPreco = New DataGridViewComboBoxColumn()

' Setando o nome nas colunas
ColunaCodigo.Name = "ColunaCodigo"
ColunaTipoPreco.Name = "ColunaTipoPreco"

' Configurando o texto do cabeçalho
ColunaCodigo.HeaderText = "Código"
ColunaTipoPreco.HeaderText = "Tipo de preço"

' Populando a coluna ComboBox com itens de um ArrayList
Dim Lista = New ArrayList()
Lista.Add("VISTA")
Lista.Add("PRAZO")
Lista.Add("ATACADO")
Lista.Add("ALTERNATIVO")
Lista.Add("CUSTO")

ColunaTipoPreco.Items.AddRange(Lista.ToArray())

' Adicionando as colunas no grid
SeuGrid.Columns.Insert(0, ColunaCodigo)
SeuGrid.Columns.Insert(1, ColunaTipoPreco)

' Configurando a largura das colunas
' Opção 1 - tamanho fixo
ColunaCodigo.Width = 120
ColunaTipoPreco.Width = 190

' Opção 2 - ajuste automático dependendo do conteúdo
SeuGrid.AutoResizeColumns()
SeuGrid.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells

' Configurando colunas como "somente leitura"
ColunaCodigo.ReadOnly = True

' Configurando a altura da linha do grid
SeuGrid.RowTemplate.Height = 50

Baixe o projeto de exemplo

Para baixar o projeto de exemplo desse artigo, assine a minha newsletter. Ao fazer isso, além de ter acesso ao projeto, você receberá um e-mail toda semana sobre o artigo publicado e ficará sabendo também em primeira mão sobre o artigo da próxima semana, além de receber dicas “bônus” que eu só compartilho por e-mail. Inscreva-se utilizando o formulário no final do artigo.

Até a próxima!

Photo by Peter Shanks used under Creative Commons
https://pixabay.com/en/startup-start-up-notebooks-creative-593327/

Song Rocket Power Kevin MacLeod (incompetech.com)
Licensed under Creative Commons: By Attribution 3.0 License
http://creativecommons.org/licenses/by/3.0/

Newsletter do André Lima

* indicates required



Powered by MailChimp

6 thoughts on “Adicionando um ComboBox no DataGridView

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *