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

10 thoughts on “Adicionando um ComboBox no DataGridView

  • Muito bom Andre, são estes pequenos detalhes que deixam nossas aplicações atraentes.

  • Glauco Moro disse:

    Parabéns André, como sempre ótimos artigos!
    Já pensou em montar um curso do básico ao avançado em Winforms ou WPF?
    Eu compraria kkkkk.
    Abraços.

    • andrealveslima disse:

      Olá Glauco, muito obrigado pelo comentário!

      Já pensei em montar um curso desse tipo sim, inclusive eu planejava fazer nesse ano.. Porém, no ano passado eu me sobrecarreguei muito com trabalho (e com o nascimento do meu segundo filho) e quase acabei tendo um burnout, aí resolvi pegar mais leve em 2017 para dar uma recuperada no fôlego.. Ano que vem eu volto com força na criação de produtos, aí talvez saia algum curso sobre Windows Forms / WPF..

      Um forte abraço!
      André Lima

  • Excelente artigo como sempre André.
    Abraço!

  • Israel disse:

    Muito obrigado!
    Perfeita explicação.

  • André Pinheiro disse:

    Muito bom André, ajudou muito.

    Mas fiquei com uma dúvida.
    Se eu for adicionar uma linha nesse datagridview via fonte, como faço pra “setar” o valor desse comboBox? Tentei com SelectedValue, mas não existe a propriedade.

    • andrealveslima disse:

      Olá André, obrigado pelo comentário!

      Isso depende se você está alimentando o grid com uma DataSource (por exemplo, uma DataTable) ou se está populando o grid “na mão” (adicionando as linhas diretamente no grid)..

      Se você estiver populando o grid com uma DataSource, você deve alterar o valor diretamente na DataSource.. Por exemplo, você deve pegar a linha correspondente na DataTable e setar o valor na coluna correspondente à ComboBox..

      Já se você estiver populando o grid “na mão”, você seta o valor da célula específica.. Por exemplo:

      dataGridView1.Rows[0].Cells["NomeDaColuna"].Value = VALOR_DESEJADO;
      

      Abraço!
      André Lima

Deixe uma resposta

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