I have a button that deletes rows from a DataGrid; one of the columns is the admin column. I need the program to check whether the row (or rows, since it can delete multiple rows) that is being deleted is the last one with Admin = true, since deleting the last administrator renders this table unaccessible.
The code for deleting rows is this:
connection.Open();
List<DataRow> theRows = new List<DataRow>();
for (int i = 0; i < funcDataGrid.SelectedItems.Count; ++i)
{
Object o = funcDataGrid.SelectedItems[i];
if (o != CollectionView.NewItemPlaceholder)
{
DataRowView r = (DataRowView)o;
theRows.Add(r.Row);
}
}
foreach (DataRow r in theRows)
{
string a = r.Field<string>("usernameFunc");
cmdSel = new MySqlCommand("DELETE FROM usersFunc WHERE usernameFunc='"
+ a + "'", connection);
cmdSel.ExecuteNonQuery();
int k = ds.Tables["funcionarios"].Rows.IndexOf(r);
ds.Tables[0].Rows[k].Delete();
}
connection.Close();
Keep in mind it's deleting from two joined tables, so I couldn't delete rows the usual way.
You could have a variable (static or not, depending on how you implemented your class) called ActiveAdmins. Upon loading of your Datagrid, you could easily query your DB and get the number of admins.
I'll assume you have a boolean column IsAdmin on your objects.
in your foreach loop, add something like:
if (r.Field<string>("IsAdmin"))
if (`ActiveAdmins` <= 1)
continue;
This way, whenever you're deleting, if it's the last admin stading, it'll just go to the next row, leaving your last admin in the table.
Related
I am trying to remove rows from datatable in which a certain cell is empty. I have tried using for loop but to no avail.
for (int i = dtbl.Rows.Count - 1; i >= 0; i--)
{
DataRow dr = dtbl.Rows[i];
if (dr["id"] == null)
dtbl.Rows.Remove(dr);
}
If the cell of ID column is empty, then that row should be deleted.
Any help is appreciated. Thanks in advance.
Change your test to this one.
for (int i = dtbl.Rows.Count - 1; i >= 0; i--)
{
DataRow dr = dtbl.Rows[i];
if (dr.IsNull("id"))
dtbl.Rows.Remove(dr);
}
See docs: DataRow.IsNull
or you can use a check against the special field DbValue.Null
if (dr["id"] == DbNull.Value)
Another approach is this one
for (int i = dtbl.Rows.Count - 1; i >= 0; i--)
{
DataRow dr = dtbl.Rows[i];
if (dr.IsNull("id"))
dr.Delete();
}
dtbl.AcceptChanges();
this last one just marks the row for deletion, but the row remains in the table until you call AcceptChanges. (So this approach is suitable for a foreach loop)
Calling DataRow.Delete is the preferred way to work if you plan to update the real database table at later time (for example when you want your user delete many rows from a bound grid and then make a single update to the database only if the user clicks on a Save button).
You could use linq to select those not null
dtbl = dtbl.AsEnumerable()
.Where(r => r.Field<string>("id") != null)
.CopyToDataTable();
Might need the type, specify that nullable type to compare row.Field<int?>("id").HasValue
Hellou to everyone.
I would love the assistance from you guys.
My problem is like this:
I have this datagridview which I fill using datasource (a query to a db table). I only created manually one column which contain a checkbox. When the binding is complete I can click the checkboxes to select the row and I can select multiples rows this way.
I want to put the selected rows into a datatable (so I can read it and do some sql queries with the information on it) BUT when I read the selected rows to put them inside a datatable I need to skip the checkbox column (the first one).
How can I achieve this?
Okay, for future reference, I'm gonna post here how I fixed it.
DataTable dt = new DataTable();
if (dgvPedidos.SelectedRows.Count > 0)
{
foreach (DataGridViewColumn column in dgvPedidos.Columns)
dt.Columns.Add();
int i = 0;
foreach(DataGridViewRow row in dgvPedidos.Rows)
{
if (Convert.ToBoolean(dgvPedidos.Rows[i].Cells["chkPedido"].Value) == true)
{
dt.Rows.Add();
dt.Rows[i][1] = row.Cells[1].Value.ToString();
dt.Rows[i][2] = row.Cells[2].Value.ToString();
dt.Rows[i][3] = row.Cells[3].Value.ToString();
dt.Rows[i][4] = row.Cells[4].Value.ToString();
dt.Rows[i][5] = row.Cells[5].Value.ToString();
dt.Rows[i][6] = row.Cells[6].Value.ToString();
dt.Rows[i][7] = row.Cells[7].Value.ToString();
dt.Rows[i][8] = row.Cells[8].Value.ToString();
dt.Rows[i][9] = row.Cells[9].Value.ToString();
i++;
}
}
dt.Columns.RemoveAt(0);
I have an existing datatable called _longDataTable containing data. Now, I want to duplicate each row and in each duplicate of the row, I want to set only the value in the SheetCode column according to a value from a different datatable called values, see code below. For example, the values datatable contains 1, 2 and 3, then I want each row of _longDataTable to be duplicated three times and in each of the duplicated rows, I want the Sheet Code column to have values 1, 2 and 3 respectively. My code now looks like below:
foreach (DataRow sheets in _longDataTable.Rows)
{
for(int k = 0; k < number_of_sheets; k++)
{
var newRowSheets = _longDataTable.NewRow();
newRowSheets.ItemArray = sheets.ItemArray;
newRowSheets["SheetCode"] = values.Rows[k]["Sheet Code"];
//add edited row to long datatable
_longDataTable.Rows.Add(newRowSheets);
}
}
However, I get the following error:
Collection was modified; enumeration operation might not execute.
Does anyone know where this error comes from and how to solve my problem?
you get enumeration error because you are iterating through a collection which is changing in the loop(new rows added to it),
as you said in the comment, you get out of memory exception because you are iterating on the _longDataTable, then you add rows to it, the iteration never reach to end and you will get out of memory exception.
I assume this can help you:
//assume _longDataTable has two columns : column1 and SheetCode
var _longDataTable = new DataTable();
var duplicatedData = new DataTable();
duplicatedData.Columns.Add("Column1");
duplicatedData.Columns.Add("SheetCode");
foreach (DataRow sheets in _longDataTable.Rows)
{
for (int k = 0; k < number_of_sheets; k++)
{
var newRowSheets = duplicatedData.NewRow();
newRowSheets.ItemArray = sheets.ItemArray;
newRowSheets["SheetCode"] = values.Rows[k]["Sheet Code"];
newRowSheets["Column1"] = "anything";
//add edited row to long datatable
duplicatedData.Rows.Add(newRowSheets);
}
}
_longDataTable.Merge(duplicatedData);
do not modify _longDataTable, add rows to the temp table (with the same schema) and after the iteration merge two data tables.
I have 2 DataGridViews: productsDataGridView and promotionsDataGridView.
The first one, productsDataGridView, which reads ALL values from a file with this method:
public static List<Products> LoadUserListFromFile(string filePath)
{
var loadProductsData = new List<Products>();
foreach (var line in File.ReadAllLines(filePath))
{
var columns = line.Split('\t');
loadProductsData.Add(new Products
{
InventoryID = "BG" + columns[0],
Brand = columns[1],
Category = columns[2],
Description = columns[3],
Promotions = Convert.ToInt32(columns[4]),
Quantity = Convert.ToInt32(columns[5]),
Price = Convert.ToDouble(columns[6])
});
}
return loadProductsData;
}
The first DataGridView (productsDataGridView) is filled correctly with ALL the values. Now in my productsDataGridView I have set-up a check-box column called "Promotion" (column Promotion reads integer values from the file): if it has value of 0 - the box is not checked, if greater than 1: is checked.
Now what I WANT to do is to FILTER/MOVE (I don't care which of both exactly) the values from productsDataGridView to promotionsDataGridView where we have a >0 value in the check-box column (promotions).
Example:
If productsDataGridView has 25 total products, from which 8 are promotional products (have value >0 in the check-box column), promotionsDataGridView should be filled with 8 values, which are copied/moved/filtered/whatever from DataGridView.
So far I can only copy the data from the first DataGridView to the second one with the following code:
public void Experimental2()
{
dataGridView1.DataSource = Products.LoadUserListFromFile(filePath);
//Bind datagridview to linq
var dg1 =
(from a in productsDataGridView.Rows.Cast<DataGridViewRow>()
select new { Column1 = a.Cells["Column1"].Value.ToString() }).ToList();
//loop dg1 and save it to datagridview2
foreach (var b in dg1)
{
dataGridView1.Rows.Add(b.Column1);
}
}
I made few pitiful attempts to insert an IF check, which would do the job for me (copy only IF columnt[4] > 0) but I am really new to DataGridView so I just couldn't write anything which would even compile at all...
Please, help me!
If both grids have same schema (and I assume they have) then we are going to find which rows are checked, get product bound to given row, create new result list and bind it to the next grid.
var results = new List<Products>(); //our new data source with only checked items
foreach (DataGridViewRow row in productsDataGridView.Rows)
{
var item = row.DataBoundItem as Products; //get product from row (only when grid is databound!)
if (item.Promotions > 0)
{
results.Add(item);
}
}
promotionsDataGridView.DataSource = results;
If you want to delete rows from first grid that are checked then create temporary list of rows, add to it checked rows and at the end iterate over them and remove one by one. Hope that help You out :)
I have a DataTable dt with 2 columns. First col (call it CustomerId) is unique and doesn't allow nulls. the second one allows nulls and is not unique.
From a method I get a CustomerId and then I would like to either insert a new record if this CustomerId doesn't exist or increment by 1 what's in the second column corresponding to that CustomerId if it exists.
I'm not sure how I should approach this. I wrote a select statement (which returns System.Data.DataRow) but I don't know how to test whether it returned an empty string.
Currently I have:
//I want to insert a new row
if (dt.Select("CustomerId ='" + customerId + "'") == null) //Always true :|
{
DataRow dr = dt.NewRow();
dr["CustomerId"] = customerId;
}
If the datatable is being populated by a database. I would recommend making the customerid a identity column. That way when you add a new row it will automatically create a new customerid which will be unique and 1 greater than the previous id (depending on how you setup your identity column)
I would check the row count which is returned from the select statement. Something like
I would also use string.Format...
So it would look like this
var selectStatement = string.Format("CustomerId = {0}", customerId);
var rows = dt.Select(selectStatement);
if (rows.Count < 1){
var dr = dt.NewRow();
dr["CustomerId"] = customerId;
}
This is my method to solve similar problem. You can modify it to fit your needs.
public static bool ImportRowIfNotExists(DataTable dataTable, DataRow dataRow, string keyColumnName)
{
string selectStatement = string.Format("{0} = '{1}'", keyColumnName, dataRow[keyColumnName]);
DataRow[] rows = dataTable.Select(selectStatement);
if (rows.Length == 0)
{
dataTable.ImportRow(dataRow);
return true;
}
else
{
return false;
}
}
The Select Method returns an array of DataRow objects. Just check if its length is zero (it's never null).
By the way, don't write such statements in the code directly as in this example. There's a technique for breaching your code's security called "SQL Injection", I encourage you to read the Wikipedia Article. In brief, an experienced user could write SQL script that gets executed by your database and potentially do harmful things if you're taking customerId from the user as a string. I'm not experienced in database programming, this is just "general knowledge"...