I have a database that has a Users table and I present the data in a DataGridView. I would like to remove 4 columns, but the code I have (referenced from MSDN) seems to append the columns at the end. How can I totally REMOVE the columns?
So this is how the DGV looks without the columns removed
The Code I use to TRY and remove the columns
RadarServerEntities rse = new RadarServerEntities();
gvUsers.DataSource = rse.Users;
gvUsers.Columns.Remove("ID");
gvUsers.Columns.Remove("InsertDate");
gvUsers.Columns.Remove("Connections");
gvUsers.Columns.Remove("MachineID");
The result
I would like to get rid of the last 4 columns, so why isnt my code doing it?
Many Thanks :)
I tend to just hide the fields instead.
gvUsers.Columns["ID"].Visibility = false;
Et cetera.
you could also use gvUsers.Columns.RemoveAt(IndexOfColumn);
if you don't want to create the columns automatically when you bind your DataSource, you need to set gvUsers.AutoGenerateColumns = false;
RadarServerEntities rse = new RadarServerEntities();
gvUsers.AutoGenerateColumns = false;
gvUsers.DataSource = rse.Users;
To actually remove automatically generated column you have to turn off automatic generation after data binding.
So the code would be:
RadarServerEntities rse = new RadarServerEntities();
gvUsers.DataSource = rse.Users;
gvUsers.AutoGenerateColumns = false;
gvUsers.Columns.Remove("ID");
gvUsers.Columns.Remove("InsertDate");
gvUsers.Columns.Remove("Connections");
gvUsers.Columns.Remove("MachineID");
I haven't checked what exactly happens, but probably the moment DGV becomes visible, missing columns are re-autogenerated.
So with this solution you have columns generated at the moment of binding data, then you turn it off and remove columns that you don't need. Missing columns can't be re-generated.
DataGridViewColumn DataGridViewColumnSelected;
private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex !=-1 && e.RowIndex == -1)
{
DataGridViewColumnSelected = dataGridView1.Columns[e.ColumnIndex] as DataGridViewColumn;
}
}
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
bool bHandled = false;
switch (keyData)
{
case Keys.Delete:
if (DataGridViewColumnSelected != null)
{
this.dataGridView1.Columns.RemoveAt(DataGridViewColumnSelected.Index);
//dataGridView1.Columns[DataGridViewColumnSelected.Name].Visible = false; // case of just hiding the column
}
break;
}
return bHandled;
}
I think you have to Modify DataTable before passing it to DGV
as follow:
RadarServerEntities rse = new RadarServerEntities();
rse.Users.Columns.Remove("ID");
rse.Users.Columns.Remove("InsertDate");
rse.Users.Columns.Remove("Connections");
rse.Users.Columns.Remove("MachineID");
dgvUsers.DataSource=rse.Users;
this method worked for me
If what you want is to remove from a DataGridView is the column you have selected then this code is for you.
Place this code in the delete button and ready to delete the column you have selected.
int rowIndex = TuDataGrigView1.CurrentCell.RowIndex;
TuDataGrigView1.Rows.RemoveAt(rowIndex);
Related
I have a desktop app and I am using DataGridView on form. I froze the first row. But when I clicked the header of column in DataGridView The location of the first row is also changing. How to ignore the first row while I am sorting.
Within the SortCompare event handler for the SortCompare event of the DataGridView, try this:
private void dataGridView1_SortCompare(object sender, DataGridViewSortCompareEventArgs e)
{
if (e.RowIndex1 == 0)
{
e.Handled = true;
}
else
{
e.Handled = false;
}
}
Essentially, what you're asking is to be able to sort only a portion of the DataGridView. To my knowledge, there's no easy or standard way to do this, echoed by Filip's response to this question: Sorting selected rows in DataGridView. This doesn't mean it's impossible to achieve the behavior that you want.
Filip offers an implementation, and in this question, Justin offers code that you would have to modify slightly, but that may suit your needs. The question asks how to move a row up or down in a DataGridView, but I think you could modify his implementation to move the row you want to keep static all the way to the top after you sort. Depending on the volume of rows you're sorting, the first row might disappear and reappear. However, if your data set is relatively small, I can't imagine the brief flash of the first row being sorted to some random position and then added back to the top would cause any major problems.
private void dataGridView1_SortCompare(object sender, DataGridViewSortCompareEventArgs e)
{
int so = 1;
if (dataGridView1.SortOrder == SortOrder.Ascending)
{
so = -1;
}
if (e.RowIndex1 == 0 )
{
e.SortResult = so;
e.Handled = true;
}
if (e.RowIndex2 == 0)
{
e.SortResult = -so;
e.Handled = true;
}
}
This code puts the first line always on top of the sorted list, by setting the sort result of the first row accordingly.
I populated a DataGridView with a DataTable as DataSource. This DataSource has a column with comments in it. I hide this column as part of the requirements and added a new DataGridVewLinkColumn that when is clicked the user will be able to see that comment.
My problem is when I sort by clicking on any of the headers from the DataGridView, all the DataGridViewLinkColumn links disappear. I have set the SortMode to Automatic in this LinkColumn but seems that I need to do something else because still as soon as I click on the headers from the other columns in the Grid the links disappear.
Any one knows how can I make sure that when the DataGridView is sorted the link column gets sorted accordingly?
Many thanks
OK I figure it out. The problem is because I used a DataTable as DataSource it was binded to the grid and there is no way to add an extra column to a grid source that is already binded and expect that it will bind with the source.
To solve this problem I just modified the data table. Add the extra column in the data table with the strings that will be the links in the DataGridView and populate the DataGridView programatically as recommended in http://msdn.microsoft.com/en-us/library/bxt3k60s(v=vs.90).aspx
Column Sort Modes in the Windows Forms DataGridView Control
When a DataGridView control containing both bound and unbound columns is sorted, the values in the unbound columns cannot be maintained automatically. To maintain these values, you must implement virtual mode by setting the VirtualMode property to true and handling the CellValueNeeded and CellValuePushed events.
This is a bit complicated, so the simplest solution would be to add an extra column into your DataTable.
I'll leave an example below for future reference.
The points are:
VirtualMode should be true.
CellValueNeeded should be handled properly to show the specified cell values.
ColumnHeaderMouseClick should be handled properly to sort by the unbound columns, and to show sort glyphs.
Note:
The DataGridView in this example is read-only to make things simple.
This example form contains:
A typed DataSet, that has DataTable1 with columns of ID(string), Comment(string):
private DataSet1 dataSet1;
A BindingSource:
private BindingSource dataTable1BindingSource;
.DataMember = "DataTable1";
.DataSource = this.dataSet1;
A DataGridView:
private DataGridView dataTable1DataGridView;
.DataSource = this.dataTable1BindingSource;
.VirtualMode = true;
.CellValueNeeded += this.dataTable1DataGridView_CellValueNeeded;
.ColumnHeaderMouseClick += this.dataTable1DataGridView_ColumnHeaderMouseClick;
.ReadOnly = true;
.AllowUserToAddRows = false;
.AllowUserToDeleteRows = false;
Its columns:
private DataGridViewTextBoxColumn iDDataGridViewTextBoxColumn; // bound column
private DataGridViewTextBoxColumn commentDataGridViewTextBoxColumn; // bound column
private DataGridViewLinkColumn linkColumn; // unbound column
.SortMode = DataGridViewColumnSortMode.Automatic;
And here goes the code:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
// Hold the link texts, in a dictinary
// keyed by ID (= unique key in DataTable1), to be bound to each row.
private SortedDictionary<string, string> _linkTexts
= new SortedDictionary<string, string>();
private void Form1_Load(object sender, EventArgs e)
{
// Bound data sample
this.dataSet1.DataTable1.AddDataTable1Row("1", "Comment1");
this.dataSet1.DataTable1.AddDataTable1Row("2", "Comment2");
this.dataSet1.DataTable1.AddDataTable1Row("3", "Comment3");
// Unbound data sample
this._linkTexts.Add("1", "linkA");
this._linkTexts.Add("2", "linkC");
this._linkTexts.Add("3", "linkB");
}
// Handles ColumnHeaderMouseClick to do custom sort.
private void dataTable1DataGridView_ColumnHeaderMouseClick(
object sender, DataGridViewCellMouseEventArgs e)
{
// When the unbound column header is clicked,
if (e.ColumnIndex == this.linkColumn.Index)
{
// Create a new DataView sorted by the link text
// with toggling the sort order.
DataView newView;
switch (this.linkColumn.HeaderCell.SortGlyphDirection)
{
case SortOrder.None:
case SortOrder.Descending:
this.linkColumn.HeaderCell.SortGlyphDirection
= SortOrder.Ascending;
newView = this.dataSet1.DataTable1
.OrderBy(row => this._linkTexts[row.ID])
.AsDataView();
break;
default:
this.linkColumn.HeaderCell.SortGlyphDirection
= SortOrder.Descending;
newView = this.dataSet1.DataTable1
.OrderByDescending(row => this._linkTexts[row.ID])
.AsDataView();
break;
}
// Set it as DataSource.
this.dataTable1BindingSource.DataSource = newView;
// Clear sort glyphs on the other column headers.
foreach (DataGridViewColumn col
in this.dataTable1DataGridView.Columns)
{
if (col != this.linkColumn)
col.HeaderCell.SortGlyphDirection = SortOrder.None;
}
}
// The bound column header is clicked,
else
{
// Sorting has done automatically.
// Reset the sort glyph on the unbound column.
this.linkColumn.HeaderCell.SortGlyphDirection = SortOrder.None;
}
}
// Handles CellValueNeeded to show cell values in virtual mode.
private void dataTable1DataGridView_CellValueNeeded(
object sender, DataGridViewCellValueEventArgs e)
{
// Extract the bound row from the current data view.
DataSet1.DataTable1Row row
= (this.dataTable1BindingSource[e.RowIndex] as DataRowView)
.Row as DataSet1.DataTable1Row;
// For the unbound column,
if (e.ColumnIndex == this.linkColumn.Index)
{
if (row.IsIDNull())
e.Value = DBNull.Value;
else
// get the value from the dictionary.
e.Value = this._linkTexts[row.ID];
}
// For the bound columns,
else
{
// get the value from the data source.
string propName = this.dataTable1DataGridView
.Columns[e.ColumnIndex].DataPropertyName;
e.Value = row[propName];
}
}
}
Seems to be a common issue.
I'm trying to hide a column of my GridView. I have read that simply setting the column to 'visible = false' will not work as I'm auto-generating my data.
Currently my code stands as so:
protected void Page_Load(object sender, EventArgs e)
{
bind();
if (GridView1.Columns.Count > 0)
GridView1.Columns[0].Visible = false;
else
{
GridView1.HeaderRow.Cells[0].Visible = false;
foreach (GridViewRow gvr in GridView1.Rows)
{
gvr.Cells[1].Visible = false;
}
}
}
The 'if' statement will not trigger as as said I am auto-generating the data. With the above loop, I can hide the header text of the column but want to hide the whole column with the ability to still be able to access the hidden data.
How about just doing this later in the control's life cycle (when the Columns collection has been populated):
protected void GridView1_PreRender(object sender, EventArgs e)
{
if (GridView1.Columns.Count > 0)
GridView1.Columns[0].Visible = false;
else
{
GridView1.HeaderRow.Cells[0].Visible = false;
foreach (GridViewRow gvr in GridView1.Rows)
{
gvr.Cells[1].Visible = false;
}
}
}
Note: you would need to add OnPreRender="GridView1_PreRender" to your GridView markup.
Why not use the GridView.DataKeyNames and GridView.DataKeys properties to store the ID and then retrieve it with the rowIndex later? This also will keep the column from being autogenerated.
DataKeyNames
DataKeys
This is a limitation with auto-generating columns. You give up a lot of control over HOW the columns are displayed for the convenience of not pre-defining them.
Bite the bullet now and define your columns. If this is the ONLY customization you need to make, you may be all right, but 90+% of the time I end up defining columns to customize how they are displayed.
Otherwise you're going to end up with several of these "tweaks" that are prone to breaking under various circumstances (e.g. the order of the columns changes in the data source).
Ah... so the problem is the Columns is not predefined.
Perhaps you could use Linq to select all the columns you want to display:
gvTest.DataSource = (from d in dataSource
select new
{
ColumnA = d.ColumnA...
}
Alternatively, you could hide the columns with JavaScript;
I have a small app in c#, it has a DataGridView that gets filled using:
grid.DataSource = MyDatasource array;
MyClass hold the structure for the columns, it looks something like this:
class MyDatasource
{
private string column1;
private string column2;
public MyDatasource(string arg1, string arg2)
{
this.column1 = arg1;
this.column2 = arg2;
}
public string column1
{
get
{
return this.column1;
}
set
{
this.column1 = value;
}
}
public string column2
{
get
{
return this.column2;
}
set
{
this.column1 = value;
}
}
}
Everything works fine and the DataGridView gets populated with the correct data, but now I want to hide the column2. I tried adding [Browsable(false)] above the column declaration, that will hide it, but I also need to access the column value from code, and when I use [Browsable(false)] and try to read the content it acts like if the column doesn't exist. If I don't use it I can read the column without problem but it's visible in the DataGridView.
How could I hide the column but still be able to read its content from code?
In some cases, it might be a bad idea to first add the column to the DataGridView and then hide it.
I for example have a class that has an NHibernate proxy for an Image property for company logos. If I accessed that property (e.g. by calling its ToString method to show that in a DataGridView), it would download the image from the SQL server. If I had a list of Company objects and used that as the dataSource of the DataGridView like that, then (I suspect) it would download ALL the logos BEFORE I could hide the column.
To prevent this, I used the custom attribute
[System.ComponentModel.Browsable(false)]
on the image property, so that the DataGridView ignores the property (doesn't create the column and doesn't call the ToString methods).
public class Company
{
...
[System.ComponentModel.Browsable(false)]
virtual public MyImageClass Logo { get; set;}
You have to hide the column at the grid view control rather than at the data source. Hiding it at the data source it will not render to the grid view at all, therefore you won't be able to access the value in the grid view. Doing it the way you're suggesting, you would have to access the column value through the data source as opposed to the grid view.
To hide the column on the grid view control, you can use code like this:
dataGridView1.Columns[0].Visible = false;
To access the column from the data source, you could try something like this:
object colValue = ((DataTable)dataGridView.DataSource).Rows[dataSetIndex]["ColumnName"];
I have noticed that if utilised progrmmatically it renders incomplete (entire form simply doesn't "paint" anything) if used before panel1.Controls.Add(dataGridView); then dataGridView.Columns["ID"].Visible = false; will break the entire form and make it blank, so to get round that set this AFTER EG:
panel1.Controls.Add(dataGridView);
dataGridView.Columns["ID"].Visible = false;
//works
dataGridView.Columns["ID"].Visible = false;
panel1.Controls.Add(dataGridView);
//fails miserably
I"m not sure if its too late, but the problem is that, you cannot set the columns in design mode if you are binding at runtime. So if you are binding at runtime, go ahead and remove the columns from the design mode and do it pragmatically
ex..
if (dt.Rows.Count > 0)
{
dataGridViewProjects.DataSource = dt;
dataGridViewProjects.Columns["Title"].Width = 300;
dataGridViewProjects.Columns["ID"].Visible = false;
}
Set that particular column's Visible property = false
dataGridView[ColumnName or Index].Visible = false;
Edit
sorry missed the Columns Property
dataGridView.Columns[ColumnName or Index].Visible = false;
I had the same problem
Here is the Solution that might work for you. It worked for me
GridView1.DataBind();
if (GridView1.Columns.Count > 0)
GridView1.Columns[0].Visible = false;
else
{
GridView1.HeaderRow.Cells[0].Visible = false;
foreach (GridViewRow gvr in GridView1.Rows)
{
gvr.Cells[0].Visible = false;
}
}
Just set DataGridView.AutoGenerateColumns = false;
You need click on the arrow on top right corner (in datagridview) to add columns, and in DataPropertyName you need to put a name of your property in your class.
Then, after you defined your columns in datagridview, you can set datagridview.datasource = myClassViewModel.
MyDataGridView.RowHeadersVisible = False;
Before binding and rename each columns header and set columns width.
To help my failing memory when I search, because I will search ... that's for sure ;-)
If you want to use the BrowsableAttribute, then you can look for it at runtime on the model and hide the column accordingly:
private void Form_Load(object sender, EventArgs e)
{
//add this line after your DataGridView initialization
HideColumns<MyModel>(myDvg);
}
private void HideColumns<T>(DataGridView dvg)
{
var type = typeof(T);
foreach (var column in dvg.Columns.Cast<DataGridViewColumn>())
column.Visible = IsBrowsable(type.GetProperty(column.Name));
}
private bool IsBrowsable(PropertyInfo propertyInfo)
{
var attribute = propertyInfo.GetCustomAttributes(true).FirstOrDefault(att => att.GetType() == typeof(BrowsableAttribute));
return attribute == null || (attribute as BrowsableAttribute).Browsable;
}
Try follow:
DataGridView1.Columns["YourColumnName"].Visible = false;
I want an OpenFileDialog to come up when a user clicks on a cell, then display the result in the cell.
It all works, except that the DataGridView displays an extra row, for adding values to the list it's bound to. The row shows up if dataGridView.AllowUserToAddNewRows == true, which is what I want. What I don't want is for the application to crash when that row is edited programatically; instead, it should do exactly what it would do if the user had edited that row manually (add the new row to the underlying list, push another empty row onto the grid for adding values).
I read about SendKeys.Send(), which should make the DataGridView behave exactly as though the user had typed the value in; however, it does not work either. Here is what I am trying:
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
dataGridView1.CurrentCell = cell;
//simply doing a cell.Value = etc. will cause the program to crash
cell.ReadOnly = false;
dataGridView1.Columns[cell.ColumnIndex].ReadOnly = false;
dataGridView1.EditMode = DataGridViewEditMode.EditOnEnter;
dataGridView1.BeginEdit(true);
SendKeys.Send(openFileDialog1.FileName + "{Enter}");
dataGridView1.EndEdit();
cell.ReadOnly = true;
dataGridView1.Columns[cell.ColumnIndex].ReadOnly = true;
}
//I would expect the FileName would be in the cell now, and a new empty
//row tacked onto the end of the DataGridView, but it's not; the DataGridView
//is not changed at all.
I found a workaround on this page, though I don't know why it works
public MyForm()
{
InitializeComponent();
//Create a BindingSource, set its DataSource to my list,
//set the DataGrid's DataSource to the BindindingSource...
_bindingSource.AddingNew += OnAddingNewToBindingSource;
}
private void OnAddingNewToBindingSource(object sender, AddingNewEventArgs e)
{
if(dataGridView1.Rows.Count == _bindingSource.Count)
{
_bindingSource.RemoveAt(_bindingSource.Count - 1);
}
}
I'm getting very sick of spending so much time dealing with Visual Studio bugs...
I was having the same problem when trying to programattically edit cells with a binding source.
""Operation is not valid due to the current state of the object"
Which operation? What State? So helpful.
My code seem to work fine except when editing the last row in the grid.
Turns out the key is DataGridView.NotifiyCurrentCelldirty(true)
The correct sequence for programatically editing a cell, so it works the same as if the user did it.
(A new empty row appears when changing a cell in the last row) is something like this:
1) Make the cell to edit the current cell (do what ever you need to the current currentcell, first
like calling endEdit if it is in edit mode.)
2) Call DataGridview.BeginEdit(false)
3) Call DataGridView.NotifyCurrentCellDirty(true)
4) Modify the value.
5) Call DataGridView.EndEdit()
And you'll want to do something for the RowValidating and RowValidated events.
One of my routines for updating a cell value looks like this:
This is from a method in my class derived from DataGridView.
You could do the same thing from the containing form, calling
through a DataGridView instance, because the methods are public.
Here the calls are using an impliciit 'this.'
private void EnterTime()
{
if (CurrentRow == null) return;
SaveCurrentCell(); // Calls EndEdit() if CurrentCell.IsInEditMode
DataGridViewCell previous = CurrentCell;
CurrentCell = CurrentRow.Cells[CatchForm.TimeColumn];
BeginEdit(false);
NotifyCurrentCellDirty(true);
CurrentCell.Value = DateTime.Now;
EndEdit();
CurrentCell = previous;
}
I’m not sure why a separate call is needed.
Why doesn’t BeginEdit, or actually modifying the cell value, cause the right
things to happen?
And if you move the NotifyCurrentCellDirty call to after you actually modify the cell,
it doesn’t behave correctly either. All very annoying.
This is old, but I am running VS2010 and just come across this issue. I have a DataGridView bound to a List<T> using a BindingList<T>. I have a drag n' drop event on my DataGridView and it would throw this exception after deleting all rows from the DGV (except for the last blank one which one cannot delete) and then adding new rows to the DGV in the DragDrop handler via the BindingList<T>. This exception was not thrown if I simply added rows manually editing individual cells.
One solution I read said to handle the BindingList<T>.AddNew event, but I found that this event did not fire when calling BindingList<T>.Add() within the DragDrop event handler (I'm not sure why). I solved the issue by adding
if(bindingList.Count == 0)
bindingList.RemoveAt(0)
inside of the DragDrop event handler before adding new objects to bindingList. It seemed that adding an object to the bindingList failed when the only "object" in the bindingList was the one associated to the final blank row. The point of a BindingList<T> is to allow the developer to work with it instead of the DGV directly, but it seems doing so can cause problems in border cases.
The relationship between DGV rows and BindingList<T> rows seems to be a bit of a gray area. I have not spent much time investigating this, but it is not clear to me what is the state of the "object" in the BindingList<T> associated to the final (empty) row of the DGV. However, it does seem like the "object" at the end is only instantiated "correctly" when you interact with the final row directly (not via a DataSource).
Try this:
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
int row = e.RowIndex;
int clmn = e.ColumnIndex;
if(e.RowIndex == dataGridView1.Rows.Count- 1)
dataGridView1.Rows.Add();
dataGridView1.Rows[row].Cells[clmn].Value = openFileDialog1.FileName;
}
EDIT
I didn't notice that you are binding your datagridview :(
Ok, to solve it: use binding source, set its DataSource property to your list, then set the data source of the data grid view to this binding source. Now, the code should look like so:
public partial class frmTestDataGridView : Form
{
BindingSource bindingSource1 = new BindingSource();
List<string> datasource = new List<string>();
public frmTestDataGridView()
{
InitializeComponent();
datasource.Add("item1");
datasource.Add("item2");
datasource.Add("item3");
bindingSource1.DataSource = datasource;
dataGridView1.DataSource = bindingSource1;
}
private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e)
{
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
int row = e.RowIndex;
int clmn = e.ColumnIndex;
if (e.RowIndex == dataGridView1.Rows.Count - 1)
{
bindingSource1.Add("");
}
dataGridView1.Rows[row].Cells[clmn].Value = openFileDialog1.FileName;
}
}
}
Remember to use Row.BeginEdit() and Row.EndEdit() if you get this error while editing a value in a row, using DataGrid or GridEX from Janus (in my case). The sample code that Darrel Lee posted here (https://stackoverflow.com/a/9143590/1278771) remind me to use these instructions that I forgot to use and this solved the problem for me.