Home > Software design >  Completely exclude columns in DataGridView
Completely exclude columns in DataGridView

Time:09-29

I have a DataTable I want to use as a DataSource of a DataGridView, but with a twist: for reasons (below) I need to exclude a column from DataGridView (not just hide it, truly exclude), ideally preventing it from being generated alltogether. Theoretically, I can call Columns.RemoveAt at an appropriate moment (which is the DataBindingComplete event handler - docs), but that's too late for me (for reasons).

An obvious solution is to set AutoGenerateColumns = false and do it manually. Without having looked into the details of this, I fear I'd need to reinvent the wheel in this scenario (to keep the data bindings working etc).

My reasons for this whole esoteric are, there is huge legacy app, originally written in VB6, and there any byte-array column is just ignored by the MS Hierarchical Grid. I'm trying to emulate this behavior in a custom control derived from DataGridView, and most stuff works.

UPDATE/ANSWER Just set dt.Columns[0].ColumnMapping = MappingType.Hidden; (courtesy of https://stackoverflow.com/a/31282356/5263865)

CodePudding user response:

In modern programming, there is a tendency to separate your data (=Model) from how the data is shown to the operator (=View). An adapter class (=ViewModel) is needed to connect the Model to the View. Abbreviated this gives MVVM. If you are not familiar with this concept of separation, consider to do some background reading.

Your Data is in a DataTable. You didn't mention what kind of items are in the DataTable. To ease the discussion I'll assume that the DataTable contains a sequence of Products.

class Product
{
    ...
}

You have methods to put Products in the DataTable and to Access them. Something like:

interface IProductRepository
{
    IEnumerable<Product> AllProducts {get;}
    Product GetProductById(int productId);

    void AddProductAtEnd(Product product);
    void ReplaceProduct(Product product, int index);
    ...
}

etc. The exact methods are not important for the answer. What I try to explain is that when using this interface you hide that the Products are stored in a DataTable. This give you the freedom to change where your Products are stored: in a DataBase? A List? or maybe a file, or even the internet.

I use a generic term repository (warehouse) for something where you can store items, and later retrieve them, replace them with other items or remove them from the repository. This can be a DataTable, or a database, or maybe a file, a Dictionary, whatever. The nice thing is that I've hidden that the Products are in a DataTable.

The DataGridView

When accessing the data in a DataGridView, people tend to fiddle directly with the DataGridViewCells and DataGridViewRows.

Well, don't!

Use DataBinding instead.

In almost all forms that have DataGridViews I have the following properties:

BindingList<Product> DisplayedProducts
{
    get => (BindingList<Product>)this.DataGridView1.DataSource;
    set => this.DataGridView1.DataSource = value;
}

Product CurrentProduct => this.DataGridView1.CurrentRow as Product;

IEnumerable<Product> SelectedProducts => this.DataGridView1.SelectedRows
    .Select(row => row.DataboundItem)
    .Cast<Product>();

Back to your question

for reasons (below) I need to exclude a column from DataGridView (not just hide it, truly exclude), ideally preventing it from being generated

If I read your question literally: you don't want to generate the DataGridViewCells that are in columns that are excluded.

This does not influence the Product that each row represents, it only influences the display of these Products. For example: even though each Product has an Id, you might want not to Display this Id.

The most easy thing for this is to use visual studios designer for this. Instead of defining the columns with the DataBinder, just add the columns one by one, and use the properties of each column for the name of the column, the name of the property that it has to show, the format that is used to show the value.

Code will look like this:

DataGridView dataGridView1 = new DataGridView();

// Column to show Product.Id
DataGridViewColumn columnProductId = new DataGridViewColumn();
columnProductId.HeaderText = "ID";
columnProductId.DataPropertyName = nameof(Product.Id);

// Column to show Product.Name
DataGridViewColumn columnProductName = new DataGridViewColumn();
columnProductName.HeaderText = "Name";
columnProductName.DataPropertyName = nameof(Product.Name);

// etc. for all columns that you want to show

Note: in DataPropertyName you store the name of the Property that must be shown in this column. I use the keyword nameof, so if later the name of the property changes, this won't be a problem.

Of course, if you want some special formatting, for example for numbers or dates, you need to set the proper properties as well. This can also be done in visual studio designer.

Once that you have defined your columns, add them to the DataGridView.

To Display the Products is a two-liner:

IDataTableProducts ProductRepository {get;} // initialize in constructor

void ShowProducts()
{
    IEnumerable<Product> productsToDisplay = this.ProductRepository.AllProducts;
    this.DisplayedProducts = new BindingList<Product>(productsToDisplay.ToList());
}

CodePudding user response:

I stumbled upon an answer: https://stackoverflow.com/a/31282356/5263865

Setting the column.ColumnMapping = MappingType.Hidden does exactly what I needed: the column isn't autogenerated anymore.

DataTable data;
data.Columns[0].ColumnMapping = MappingType.Hidden;
  • Related