I am having this problem with my datagridview where the first time it runs the delete button is on the last column, but everytime when i click on delete or adds, and call this printdataview() function.. the button is on the first column.
This function is called initially on Form1(), then calls everytime, I delete a record or add a record. I am using xml for storing data and add and remove records according, this printdataview() just simply refreshes the data on it.. and somehow it messes up, even the column length is messed the first time the datagridview was initialized and after.
Thanks Appreciate the feedback.
private void PrintDataView()
{
// clears the old data and repopulate it.
C_DB.DataSource = null;
XmlReader xmlFile;
xmlFile = XmlReader.Create(filename, new XmlReaderSettings());
DataSet ds = new DataSet();
ds.ReadXml(xmlFile);
if (ds.Tables.Count > 0 && ds.Tables[0].Rows.Count > 0){
DataView dv = new DataView(ds.Tables[0]);
// first adds all rows after sorting today's list into datagridview
string Search = DateTime.Today.ToShortDateString();
dv.RowFilter = "DateTime LIKE '%" + Search + "%'";
dv.Sort = "DateTime ASC";
C_DB.DataSource = dv;
// then add the delete button if there is more than one row
if (dv.Count > 0 && C_DB.ColumnCount != 7 && C_DB.RowCount > 0)
{
// add button
DataGridViewButtonColumn btn = new DataGridViewButtonColumn();
C_DB.Columns.Add(btn);
btn.HeaderText = "Delete Row";
btn.Text = "Delete";
btn.Name = "btn";
btn.UseColumnTextForButtonValue = true;
}
// This scrolls to bottom
if (C_DB.RowCount > 10)
{
C_DB.FirstDisplayedScrollingRowIndex = C_DB.RowCount - 1;
}
}
else
{
C_ErrorMessage.Text = "No Data Found";
}
C_DB.Refresh();
xmlFile.Close();
}
Nvm, I found out what's wrong.
Instead of
// clears the old data and repopulate it.
C_DB.DataSource = null;
// I changed it to
C_DB.Columns.Clear();
Apparently nulling the datasource doesn't empty the structure from previously.
This also fixed my column widths.. which I set later
Thanks.
Note this is not a complete solution.
The problem you have that the Delete button appears at the end of the columns list the first time only and then changes position to first, is because when you added the button by executing the method PrintDataView(), you did not specify its position in the DataGridView. Use something like this:
//Your code
C_DB.Columns.Add(btn);
btn.HeaderText = "Delete Row";
btn.Text = "Delete";
btn.Name = "btn";
btn.UseColumnTextForButtonValue = true;
//Set the desired button position here
C_DB.Columns[btn.Name].DisplayIndex = 5; //Whatever value you want starting from zero
For more info. see: Full example on MS Site
Now, you can adjust each column width individually or set a property to max the width of all columns based on the contents as explained here:
DGV Column Size
Related
I'm populating a DGV with data from a database and everything works fine, with the exception of when 0 rows are returned to the DataTable that populates the DGV. And I'm not sure how exactly to handle that situation, which I do need to allow for due to the program requirements.
Here's the Form code that calls the data binding method:
public void PopulateDgvCategories()
{
string companyFilter = cboSelectCompany.Text;
string categoryFilter = cboSelectCategory.Text;
db categoriesData = new db();
if (categoryFilter == "All Categories")
{
string catsQuery = "SELECT id, category, old_value, old_desc, new_value, new_desc, reference1, reference2 " +
"FROM masterfiles.xref WHERE company_name = #company ORDER BY category, old_value";
this.dtCategories = categoriesData.GetDgvData(catsQuery, companyFilter, categoryFilter);
}
else
{
string catsQuery = "SELECT id, category, old_value, old_desc, new_value, new_desc, reference1, reference2 " +
"FROM masterfiles.xref WHERE company_name = #company and category = #category ORDER BY old_value";
this.dtCategories = categoriesData.GetDgvData(catsQuery, companyFilter, categoryFilter);
}
// Need to check this.dtCategories.Rows.Count
// If 0, then need to figure out how to handle that
dgvCategories.DataSource = this.dtCategories;
dgvCategories.Columns[0].Visible = false;
dgvCategories.Rows[0].Cells[0].Selected = false;
}
And here's how I'm retrieving the data and filling the DataTable:
public DataTable GetDgvData(string selectQuery, string companyFilter, string categoryFilter)
{
using (NpgsqlConnection conn = new NpgsqlConnection(connString))
using (NpgsqlCommand cmd = new NpgsqlCommand(selectQuery, conn))
{
cmd.Parameters.Add(new NpgsqlParameter("company", companyFilter));
if (categoryFilter != "All Categories") cmd.Parameters.Add(new NpgsqlParameter("category", categoryFilter));
DataSet ds = new DataSet();
conn.Open();
using (NpgsqlDataAdapter da = new NpgsqlDataAdapter(cmd))
{
da.Fill(ds);
}
conn.Close();
return ds.Tables[0];
}
}
It is possible for the categoryFilter variable to cause the query to return 0 rows.
What is the best way to handle that? I'm thinking manually create the DGV with just the header row (column names) and display a MessageBox saying no rows were returned. Is that possible/feasible and would it be the best way to handle that?
There is absolutely no problem binding a datatable with zero rows - it will have a set of columns of the correct types regardless and will autocreate columns if the AutoGenerateColumns property is true
The problems come simply because you're trying to access a datagridview cell at 0,0 without making sure it exists, so you could put:
dgvCategories.DataSource = this.dtCategories;
dgvCategories.Columns[0].Visible = false;
it(dtCategories.Rows.Count > 0)
dgvCategories.Rows[0].Cells[0].Selected = false;
But you should just take this line out completely - it doesn't do anything anyway; it doesn't hide the blue selection highlight in the top left and a freshly bound datagridview's top left cell is already Selected=false so you're not achieving anything other than an exception with this line of code
If you want to hide the blue selection highlight when a DGV isn't focused, set the cell's Style.SelectionBackColor to be the same as its Style.BackColor, but do set it to something visible when the user focuses the grid or they will struggle to understand why they can't make that cell highlighted.
If you want to remove the ghost row that allows you to add new values, set AllowUserToAddRows to false
If you want to let the user know there were no rows from a recent search, place a label on top of the DGV with a "Your search 'whatever' returned no results" and set its visibility depending on whether there are rows:
lblNoResults.Text = $"Your search '{categoryFilter}' returned no rows";
lblNoResults.Visible = dtCategories.Rows.Count == 0;
Finally, you don't need to use a DataSet when all you want is a DataTable. A datadapter can fill a datatable; there is no need to go to the extra wasted effort of using a dataset - it's like using a List<string> and accessing it's 0th element when all you want is a string. You also don't need to open/close the connection - dataadapter knows how to do this itself. It is intelligent enough to not close a connection opened elsewhere (in case there is a transaction in progress):
DataTable dt = new DataTale();
using (NpgsqlDataAdapter da = new NpgsqlDataAdapter(cmd))
{
da.Fill(dt);
}
return dt;
The datagrid I have made won't let me clear the values. When I attempt to do a datagrid.columns.clear(); then it scrunches the rows together and doesn't display anything more than lines and it still doesn't clear.
I have tried datagrid.items.clear(); as I have used Datagrid.items.add(); to add items to my datagrid. I have tried datagrid.items.refresh(); and setting the datacontext to null. None of this is working.
public void FillDataGridWithCustInfo()
{
CustomerDataGrid.Items.Clear();
CustomerDataGrid.ItemsSource = null;
CustomerDataGrid.Items.Refresh();
int ArrayNum = 0;
Int32 length = CustomerFirstName.Count;
while (length > ArrayNum)
{
CustomerInformation TempCust = new CustomerInformation();
TempCust.FirstName = CustomerFirstName[ArrayNum];
TempCust.MiddleInital = CustomerMiddleInitial[ArrayNum];
TempCust.LastName = CustomerLastName[ArrayNum];
TempCust.TaxClass = CustomerTaxClass[ArrayNum];
TempCust.Email = CustomerEmail[ArrayNum];
CustomerDataGrid.Items.Add(TempCust);
ArrayNum = ArrayNum + 1;
}
}
I want it to clear the datagrid before filling it when I close out of this particular window, but it is currently taking all the data in the datagrid and just adding to it so I am getting duplicates of all the information.
I am not sure is datagrid was use by desktop application , in web application we have a similar object call as gridview which show data as table format. If you want to force clear all row , you can assign an empty or dummy data table , something like below shall do the trick..
GridView1.DataSource = new DataTable();
GridView1.DataBind();
If you want to maintain proper column header and just want to remove row that contain data , make sure you proper structure your datatable..something like this.
DataTable dt = new DataTable();
dt.Columns.Add("Column1", Type.GetType("System.String"));
dt.Columns.Add("Column2", Type.GetType("System.String"));
GridView1.DataSource = dt;
GridView1.DataBind();
I'm trying to bind a DGV to a particular table but don't want to have it display the data that is in the table (only using it for insert queries) but I cannot figure out how to actually accomplish this in C#. Here is the code I have (but again, I don't want any data to be shown, just the column names, so just ignore the select query, it's there to explain what I want fetched).
private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
{
if (tabControl1.SelectedTab == tabControl1.TabPages["tabPage1"])
{
// set the data source and bind to childrens table
this.dbcmd = this.dbconn.CreateCommand();
this.dbcmd.CommandText = "SELECT first_name, last_name, birthday, email FROM children";
this.dt = new DataTable();
this.da = new SQLiteDataAdapter(this.dbcmd); // don't want this either I think
this.da.Fill(this.dt); // this I know I do not want
this.bindingSource = new BindingSource();
this.bindingSource.DataSource = this.dt;
dataGridViewChildren.DataSource = this.bindingSource;
}
}
Does this make any sense? I'm sorry if I didn't provide enough information, I will try to provide more if more is needed.
Appreciate any help,
Thanks!
You can set the DataTable's DefaultView's Row Filter property to only show user added rows.
I did this by adding a column to the DataTable that sets all of your queried rows to false. Then any time a row is added to the DataTable from there on it gets set with true. I set the new column to not be displayed.
dt.Columns.Add(new DataColumn("UserAdded", typeof(bool)));
foreach (var row in dt.Rows.Cast<DataRow>())
row["UserAdded"] = false;
bindingSource.DataSource = dt;
dataGridViewChildren.DataSource = bindingSource;
dataGridViewChildren.Columns.Cast<DataGridViewColumn>().Last().Visible = false;
dt.DefaultView.RowFilter = "UserAdded = true";
dt.TableNewRow += (s, e) =>
{
e.Row["UserAdded"] = true;
};
When the insert button is clicked you can get the rows the user added with...
var rows = dataGridViewChildren.Rows.Cast<DataGridViewRow>()
.Where(r => !r.IsNewRow);
I want to add a row in datagridview under my last row which shows summary of the records.So I Fill my dataset and then add a row in it, afterwards I Bind it to a datagridview and then when I try to assign my new row with value it gives me error that cannot convert date time to string as The fourth column datatype is datetime.SO my question is can we change column type of a specific row cell ,If no then how can I achieve what I want to do?
string SelectGroupQuery = "Select * From GroupMembers Where GID=#Id ";
using (SqlConnection conGroup = new SqlConnection(ConnectionString.ToString()))
{
using (SqlCommand commandGroup = new SqlCommand(SelectGroupQuery, conGroup))
{
commandGroup.CommandType = CommandType.Text;
commandGroup.Parameters.Add(new SqlParameter("Id", Id));
SqlDataAdapter da = new SqlDataAdapter(commandGroup);
DataSet ds = new DataSet();
da.Fill(ds);
d1 = new DataGridView();
this.Controls.Add(d1);
d1.Location = new Point(50,y);
d1.Size = new Size(600, 300);
dr = ds.Tables[0].NewRow();
ds.Tables[0].Rows.Add(dr);
d1.DataSource = ds.Tables[0];
d1.Columns[4].ValueType = typeof(string);
d1.Rows[d1.Rows.Count-2].Cells[4].Value = "Total Amount";
y = y + 400;
}
}
Changing the type of a particular cell could be done for some of the column types. See this answer. But I wouldn't recommend it.
It is tricky to use a row in datagridview for displaying summary as it brings in some problems (more when bound to database). You could create your summary row using textboxes outside the datagridview and add the summary data to them. Check this codeproject link for working example.
Instead of assigning the value to the cell, you could fake it by handling the CellFormatting event. If e.ColumnIndex = 4 and e.RowIndex is the last row in the grid, set the formatted value to the label you want and set the property of the EventArgs to tell it you formatted the value.
If you aren't making the whole DataGridView read-only, you probably want to also handle the CellBeginEdit event and cancel the edit if it's the summary row.
I am trying to remove all the rows to reload them (I need to remove all rows) For some reason its not removing them from my datagridview (tho its not adding any extra either) nothing is happening. I have tried many different methods, I know I have done this in the past. (maybe because its end of the day)
Here is my code trying a whole bunch of removes
private void loadMSXGridView()
{
BindingSource bs = new BindingSource();
dgv.DataSource = null;
dgv.Refresh();
bs.DataSource = GetTable();
dgv.DataSource = bs;
dgv.Columns[0].Width = 391;
dgv.Columns[1].Width = 30;
}
private DataTable GetTable()
{
DataTable t = new DataTable();
t.Rows.Clear();
t.AcceptChanges();
t.Columns.Add("Accounting Line", typeof(string));
t.Columns.Add("#", typeof(string));
foreach (AMAPnr.RemarkElement re in AMAPnr._RemarkElements)
{
if (re.ElementID == "RM" && re.FreeFlow.StartsWith("*MS"))
{
DataGridViewCell gridCellText;
DataGridViewCell gridCellElement;
gridCellText = new DataGridViewTextBoxCell();
gridCellText.Value = re.FreeFlow;
gridCellElement = new DataGridViewTextBoxCell();
gridCellElement.Value = re.ElementNo.ToString();
t.Rows.Add(gridCellText.Value, gridCellElement.Value);
}
}
return t;
}
My delete button calls loadMSXGridView, I only need to refresh everything because the items in my datagridview are associated to an element number, which won't remain the same
Initially, you are data-binding:
dgv.DataSource = GetTable();
you should then continue data-binding; either tell the table to clear itself (and repopulate the data), or just assign a different data-table to dgv.DataSource.
In my limited data binding experience it is not easy to edit a data source. You should handle your data edits using one of the row binding or other events. However if you want to edit a datasource you basically have to clone the structure, then import the rows. So in this sense you should actually go back to your datasource and select exactly what you want.
However..
If you want to filter / edit your data here is how you can do it:
DataSet ds1 = [Your Data Source]
DataSet ds2 = new DataSet();
ds2 = ds1.Clone();
for (int i = 0; i < ds1.Tables[0].Rows.Count; i++)
{
if (ds1.Tables[0].Rows[i].ItemArray[0].ToString() != "SomeValue")
{
ds2.Tables[0].ImportRow(ds1.Tables[0].Rows[i]);
}
}
ds1 = ds2;
If you simply just want to add a different datatable as the datasource, then do:
datagridview.DataSource = datatable;
datagridview.Invalidate();
That should work, as I have just done the exact same thing on a project i'm working on at the minute :)
My fault, everything worked after running a throughough debug I found
The remarks elements were not being deleted, thus it was getting deleted by adding the same items back in. I remove the items from the RemarkElement section and it works, thanks for your help everyone!
I would suggest to set the DataSource of your DataGridView to a BindingSource and change the DataSource of the BindingSource instead:
var table1 = GetDataTable();
var bindingSource1 = new BindingSource(table1, null);
dataGridView1.DataSource = bindingSource1;
// after reload
var table2 = GetDataTable();
bindingSource1.DataSource = table2;
that solves most problems, since you don't have to worry about how your data is bound.