Removing rows from a WPF datagrid - c#

I have a WPF DataGrid theDataGrid bound to a DataSet ds containing a table. I want to enable the user to remove lines by first selecting them in the grid and then pressing a button (positioned somwhere outside of the datagrid). I finally arrived at the following lines of code which do what I want, but which I consider rather ugly:
DataSet ds = new DataSet();
...
// fill ds somehow
...
private void ButtonClickHandler(object Sender, RoutedEventArgs e)
{
List<DataRow> theRows = new List<DataRow>();
for (int i = 0; i < theDataGrid.SelectedItems.Count; ++i)
{
// o is only introduced to be able to inspect it during debugging
Object o = theDataGrid.SelectedItems[i];
if (o != CollectionView.NewItemPlaceholder)
{
DataRowView r = (DataRowView)o;
theRows.Add(r.Row);
}
}
foreach(DataRow r in theRows)
{
int k = ds.Tables["producer"].Rows.IndexOf(r);
// don't remove() but delete() cause of update later on
ds.Tables[0].Rows[k].Delete();
}
}
Is there a better way to do this? E.g. one which needs only one loop and without having to check for the NewItemPlaceHolder explicitly, or possible a more efficient way to access the rows which are to be deleted?
(I already figured out that I must not remove anything from the ds in the first loop, since then theDataGrid.SelectedItems.Count changes everytime the loop is executed...)

In order to remove row selected on button click you can try:
private void ButtonClickHandler(object sender, RoutedEventArgs e)//Remove row selected
{
DataRowView dataRow = (DataRowView)dataGridCodes.SelectedItem; //dataRow holds the selection
dataRow.Delete();
}

I think it works by just one loop:
int count=theDataGrid.SelectedItems.Count;
int removedCount=0;
while (removedCount < count)
{
try{
Object o = theDataGrid.SelectedItems[0];
}
catch{ break;}
if (o == CollectionView.NewItemPlaceholder)
continue;
DataRowView r = (DataRowView)o;
r.Row.Delete();
removedCount++;
}

You can remove the double loop by iterating backwards :
private void ButtonClickHandler(object Sender, RoutedEventArgs e) {
for (int i = theDataGrid.SelectedItems.Count-1; i>=0; --i)
if (theDataGrid.SelectedItems[i] != CollectionView.NewItemPlaceholder)
ds.Tables[0].Rows[i].Delete();
}

Related

Conditional sorting for datagridview

I have a datagridview which I have a textbox to search for account name, i use string.contains and set the matching cell to the first displayed index, but i need the user to be able to see other cells that are matching too
private void SearchAccountTxtBox_TextChanged(object sender, EventArgs e)
{
for (int i = 0; i < AppraisersDGV.Rows.Count; i++)
{
if (AppraisersDGV.Rows[i].Cells[1].Value.ToString().Contains(SearchAccountTxtBox.Text.Trim().ToUpper(),)|| AppraisersDGV.Rows[i].Cells[0].Value.ToString().Contains(SearchAccountTxtBox.Text.Trim()))
{
AppraisersDGV.FirstDisplayedScrollingRowIndex = i;
break;
}
}
}
I searched on SO but so far the only kind of sorting i saw was either ascending or descending
Are you using datatable when populating data into Datagridview?
if that so, then try this one.
Dataview dvp = datatable
dvp.rowfilter = String.format("Column_name Like '%{0}%', SearchAccountTxtBox.Text)
Datagridview.datasource = dvp
I hope this one helps.
I haven't done winforms in a while, but something like this should work:
private void SearchAccountTxtBox_TextChanged(object sender, EventArgs e)
{
string searchValue = SearchAccountTxtBox.Text.Trim();
foreach (DataGridViewRow row in AppraisersDGV.Rows)
{
row.Visible = row.Cells[1].Value.ToString().Contains(searchValue.ToUpper() || row.Cells[0].Value.ToString().Contains(searchValue);
}
}
It hides all the rows which don't match the search term.

Fix the last row of a DataGridView even if someone clicked the column header to sort the list

Table1 Fig.
Name | Marks
Pritam | 80
Aruna | 85
Uttaran | 90
Total | 255
DataTable dtStudentInfo = table1;
dataGridView1.DataSource = dtStudentInfo;
After Clicking on the column header name datagridview sort in ascending order of students' name. But I want the Total row always stays at the last of list.
I want to know if there is any way by which I can remove the last row from the list which will be sorted. If this is not possible then suggest any way by which i can get the result. NOTE: I don't want any external button to sort the list.
I've solved the problem by the following way:
DataGridViewRow dgRowTotalCount;
private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e)
{
if (e.RowIndex == -1)
{
dgRowTotalCount = (DataGridViewRow)dataGridView1.Rows[((DataGridView)sender).Rows.Count - 1].Clone();
for (Int32 index = 0; index < ((DataGridView)sender).Rows[((DataGridView)sender).Rows.Count - 1].Cells.Count; index++)
{
dgRowTotalCount.Cells[index].Value = ((DataGridView)sender).Rows[((DataGridView)sender).Rows.Count - 1].Cells[index].Value;
}
((DataGridView)sender).Rows.RemoveAt(((DataGridView)sender).Rows.Count - 1);
}
}
private void dataGridView1_Sorted(object sender, EventArgs e)
{
DataTable dtDGVCopy = new DataTable();
foreach (DataGridViewColumn col in dataGridView1.Columns)
{
dtDGVCopy.Columns.Add(col.Name);
}
foreach (DataGridViewRow row in dataGridView1.Rows)
{
DataRow dRow = dtDGVCopy.NewRow();
foreach (DataGridViewCell cell in row.Cells)
{
dRow[cell.ColumnIndex] = cell.Value;
}
dtDGVCopy.Rows.Add(dRow);
}
dtDGVCopy.Rows.Add();
for (Int32 i = 0; i < dgRowTotalCount.Cells.Count - 1; i++)
{
dtDGVCopy.Rows[dtDGVCopy.Rows.Count-1][i] = dgRowTotalCount.Cells[i].Value;
}
dataGridView1.DataSource = null;
dataGridView1.DataSource = dtDGVCopy;
}
But its not smooth as it was before. If there is any way by which i can make it's performance as it was before that would be great.
I know this is an old question, but here is a solution I've come up with. Use the SortCompare event on the grid to override the sorting for a specific row:
private void dgvData_SortCompare(object sender, DataGridViewSortCompareEventArgs e)
{
if (dgvData.Rows[e.RowIndex1].Cells[0].Value.ToString() == "Total" ||
dgvData.Rows[e.RowIndex2].Cells[0].Value.ToString() == "Total")
{
if (dgvData.SortOrder == SortOrder.Ascending)
{
e.SortResult = -1;
}
else
{
e.SortResult = 1;
}
e.Handled = true;
}
}
Now any row that has "Total" in the first column will always sort to the end of the grid.
(If you allow columns to be reordered then you'll need to figure out how to check the correct column)

Equivalent WinForm DataGridView code to WPF DataGrid

I have a data in DataGrid with n Rows, and the DataGrid is ReadOnly. Now my goal is to when I select any row or rows and then press EDIT Button then all the selected rows only becomes ReadOnly = false. so that I want to edit some data in selected row(s). After this when I press the update button then only the selected rows are updated using EntityFramework.
I done this task in WinForm DataGridView. Now I want the same thing in WPF DataGrid.
private void editCust_Click(object sender, EventArgs e)
{
if (dataGridView1.SelectedRows.Count > 0)
{
foreach (DataGridViewRow item in dataGridView1.SelectedRows)
{
for (int i = 1; i <= 3; i++)
{
item.Cells[i].ReadOnly = false;
}
}
}
else
{
MessageBox.Show("No Row Is Selected To Edit.");
}
}
private void updateCust_Click(object sender, EventArgs e)
{
if (dataGridView1.SelectedRows.Count > 0)
{
foreach (DataGridViewRow item in dataGridView1.SelectedRows)
{
cutable.Customer_Name = (string)item.Cells[1].Value;
cutable.Counter = int.Parse(Convert.ToString(item.Cells[2].Value));
cutable.Buying_Cost = float.Parse(Convert.ToString(item.Cells[3].Value));
for (int i = 1; i <= 3; i++)
{
item.Cells[i].ReadOnly = true;
}
}
db.SaveChanges();
MessageBox.Show("Record Is Update.");
}
else
{
MessageBox.Show("No Row Is Selected To Update.");
}
}
First convert DataGridView to DataTable then to DataGrid
For DataGridView to DataTable
Use this:
DataTable myDataTable=(DataTable)(myDataGridView.DataSource);
For more reference go through DataGridView to DataTable
Now for DataTable to DataGrid
Use this:
myDataGrid.ItemsSource=myDataTable.DefaultView;
myDataGrid.AutoGenerateColumns = true;
myDataGrid.CanUserAddRows = false;
For more reference go through DataTable to DataGrid
Comment for any query
UPDATE:
Try to create to your own code and logic, do not depend on direct solutions given by someone.
Well a similar issue I found in StackOverflow[Solved].
Refer to this question how to edit select row in datagrid in wpf
Hope it helps!

Populating listview through runtime in C#

I am creating a listview and the items and subitems are being fetched from the database. However, when I'm running my solution the listview only displays the id. I want to dynamically add columns and rows to make it like a datagrid. here's my code.
private void frmMain_Load(object sender, EventArgs e)
{
DataTable table = new DataTable();
table = logic.ViewInfo();
listView1.View = View.Details;
ListViewItem myList;
foreach (DataRow row in table.Rows)
{
myList = new ListViewItem();
for (int i = 0; i < row.ItemArray.Length; i++)
{
if (i == 0)
myList.Text = row.ItemArray[i].ToString();
else
myList.SubItems.Add(row.ItemArray[i].ToString());
}
listView1.Items.Add(myList);
}
}
The reason you see only an ID is that you don't have proper columns.
You can add them in designer or in a code.
As you need it to be added "dynamically", you need to add as many columns, as values you have in an ItemArray.
Try this code. It is working for me.
There is no need in using Text property. Each SubItem stands for a cell.
private void frmMain_Load(object sender, EventArgs e)
{
DataTable table = logic.ViewInfo(); // Combine declaration and assignment
listView1.View = View.Details; // Better place it in designer
listView1.Columns.Clear();
foreach (var column in dataTable.Columns)
{
listView1.Columns.Add(column.ColumnName); // or Caption, or anything else
}
foreach (DataRow row in table.Rows)
{
ListViewItem myList = new ListViewItem(); // Move declaration into inner scope. You are reinitializing reference type variable each type. In my opinion, it is not good.
for (int i = 0; i < row.ItemArray.Length; i++)
{
// You don't need Text property
myList.SubItems.Add(row.ItemArray[i].ToString());
}
listView1.Items.Add(myList);
}
}

How to clear a data grid view

I am trying to populate a DataGridView based on the selected item in a ComboBox, I have this part working.
However, I need to be able to clear the grid before adding the new data from a new item rather than it just adding on to the end.
How do I clear a DataGridView before adding items to it?
Firstly, null the data source:
this.dataGridView.DataSource = null;
Then clear the rows:
this.dataGridView.Rows.Clear();
Then set the data source to the new list:
this.dataGridView.DataSource = this.GetNewValues();
If it's bound to a datasource -
dataGridView.DataSource=null;
dataGridView.Rows.Clear();
Worked for me.
You can clear DataGridView in this manner
dataGridView1.Rows.Clear();
dataGridView1.Refresh();
If it is databound then try this
dataGridView1.Rows.Clear() // If dgv is bound to datatable
dataGridView1.DataBind();
IF you want to clear not only Data, but also ComboBoxes, Checkboxes, try
dataGridView.Columns.Clear();
DataGrid.DataSource = null;
DataGrid.DataBind();
You can assign the datasource as null of your data grid and then rebind it.
dg.DataSource = null;
dg.DataBind();
You could take this next instruction and would do the work with lack of perfomance. If you want to see the effect of that, put one of the 2 next instructions (Technically similars) where you need to clear the DataGridView into a try{} catch(...){} finally block and wait what occurs.
while (dataGridView1.Rows.Count > 1)
{
dataGridView1.Rows.RemoveAt(0);
}
foreach (object _Cols in dataGridView1.Columns)
{
dataGridView1.Columns.RemoveAt(0);
}
You improve this task but its not enough, there is a problem to reset a DataGridView, because of the colums that remains in the DataGridView object. Finally I suggest, the best way i've implemented in my home practice is to handle this gridView as a file with rows, columns: a record collection based on the match between rows and columns. If you can improve, then take your own choice a) or b): foreach or while.
//(a): With foreach
foreach (object _Cols in dataGridView1.Columns)
{
dataGridView1.Columns.RemoveAt(0);
}
foreach(object _row in dataGridView1.Rows){
dataGridView1.Rows.RemoveAt(0);
}
//(b): With foreach
while (dataGridView1.Rows.Count > 1)
{
dataGridView1.Rows.RemoveAt(0);
}
while (dataGridView1.Columns.Count > 0)
{
dataGridView1.Columns.RemoveAt(0);
}
Well, as a recomendation Never in your life delete the columns first, the order is before the rows after the cols, because logically the columns where created first and then the rows.It would be a penalty in terms of correct analisys.
foreach (object _Cols in dataGridView1.Columns)
{
dataGridView1.Columns.RemoveAt(0);
}
foreach (object _row in dataGridView1.Rows)
{
dataGridView1.Rows.RemoveAt(0);
}
while (dataGridView1.Rows.Count > 1)
{
dataGridView1.Rows.RemoveAt(0);
}
while (dataGridView1.Columns.Count > 0)
{
dataGridView1.Columns.RemoveAt(0);
}
Then, Put it inside a function or method.
private void ClearDataGridViewLoopWhile()
{
while (dataGridView1.Rows.Count > 1)
{
dataGridView1.Rows.RemoveAt(0);
}
while (dataGridView1.Columns.Count > 0)
{
dataGridView1.Columns.RemoveAt(0);
}
}
private void ClearDataGridViewForEach()
{
foreach (object _Cols in dataGridView1.Columns)
{
dataGridView1.Columns.RemoveAt(0);
}
foreach (object _row in dataGridView1.Rows)
{
dataGridView1.Rows.RemoveAt(0);
}
}
Finally, call your new function ClearDataGridViewLoopWhile(); or ClearDataGridViewForEach(); where you need to use it, but its recomended when you are making queries and changing over severall tables that will load with diferents header names in the grieView. But if you want preserve headers here there is a solution given.
dataGridView1.Rows.Clear();
dataGridView1.Refresh();
If you want to clear all the headers as well as the data, for example if you are switching between 2 totally different databases with different fields, therefore different columns and column headers, I found the following to work. Otherwise when you switch you have the columns/ fields from both databases showing in the grid.
dataTable.Dispose();//get rid of existing datatable
dataTable = new DataTable();//create new datatable
datagrid.DataSource = dataTable;//clears out the datagrid with empty datatable
//datagrid.Refresh(); This does not seem to be neccesary
dataadapter.Fill(dataTable); //assumming you set the adapter with new data
datagrid.DataSource = dataTable;
private void ClearGrid()
{
if(this.InvokeRequired) this.Invoke(new Action(this.ClearGrid));
this.dataGridView.DataSource = null;
this.dataGridView.Rows.Clear();
this.dataGridView.Refresh();
}
refresh the datagridview and refresh the datatable
dataGridView1.Refresh();
datatable.Clear();
datatable.Clear();
dataGridView1.DataSource = datatable;
For having a Datagrid you must have a method which is formatting your Datagrid. If you want clear the Datagrid you just recall the method.
Here is my method:
public string[] dgv_Headers = new string[] { "Id","Hotel", "Lunch", "Dinner", "Excursions", "Guide", "Bus" }; // This defined at Public partial class
private void SetDgvHeader()
{
dgv.Rows.Clear();
dgv.ColumnCount = 7;
dgv.RowHeadersVisible = false;
int Nbr = int.Parse(daysBox.Text); // in my method it's the textbox where i keep the number of rows I have to use
dgv.Rows.Add(Nbr);
for(int i =0; i<Nbr;++i)
dgv.Rows[i].Height = 20;
for (int i = 0; i < dgv_Headers.Length; ++i)
{
if(i==0)
dgv.Columns[i].Visible = false; // I need an invisible cells if you don't need you can skip it
else
dgv.Columns[i].Width = 78;
dgv.Columns[i].HeaderText = dgv_Headers[i];
}
dgv.Height = (Nbr* dgv.Rows[0].Height) + 35;
dgv.AllowUserToAddRows = false;
}
dgv is the name of DataGridView
The solution is:
dataGridView1.Rows.RemoveAt(0);
Clears the grid and preserves the columns.
YourGrid.Items.Clear();
YourGrid.Items.Refresh();
This is working to me
'int numRows = dgbDatos.Rows.Count;
for (int i = 0; i < numRows; i++)
{
try
{
int max = dgbDatos.Rows.Count - 1;
dgbDatos.Rows.Remove(dgbDatos.Rows[max]);
btnAgregar.Enabled = true;
}
catch (Exception exe)
{
MessageBox.Show("No se puede eliminar " + exe, "",
MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}`
Solution is:
while (dataGridView1.RowCount > 1)
{
dataGridView1.Rows.RemoveAt(0);
}
You could take this next instruction and would do the work with lack of perfomance. If you want to see the effect of that, put one of the next instruction where you need to clear the DataGridView into a try{} catch(...){} finally block and wait what occurs.
while (dataGridView1.Rows.Count > 1)
{
dataGridView1.Rows.RemoveAt(0);
}
while (dataGridView1.Columns.Count > 0)
{
dataGridView1.Columns.RemoveAt(0);
}

Categories

Resources