Searching values - c#

I want to search for a string value in column ProductName in dataGridView1 then I want to delete or hide any other value in dataGridView1
Search value is typed in textBox1
I tried this
string searchValue = textBox1.Text;
dataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
try
{
foreach (DataGridViewRow row in dataGridView1.Rows)
{
if (row.Cells[2].Value.ToString().Equals(searchValue))
{
row.Selected = true;
break;
}
}
}
catch (Exception exc)
{
MessageBox.Show(exc.Message);
}
but I get object reference not set to an instance of an object

In order to be able to manipulate your data(deleting rows, etc), you are going to have to bind your dataGridView to a DataTable or a DataSet and then manipulate your records through the DataSet/DataTable. This would let you reflect the changes in you dataGridView.

Lets try, If I understand your problem.
string searchValue = textBox1.Text;
dataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
try
{
foreach (DataGridViewRow row in dataGridView1.Rows)
{
if (row.Cells[2].Value.ToString().Equals(searchValue))
{
row.Selected = true;
}
else
{
dataGridView1.Rows.RemoveAt(row.Index); //Remove
}
}
}
catch (Exception exc)
{
MessageBox.Show(exc.Message);
}

Related

DataGridView C# search gives null

I wanted to create a simple program that looks trough each row of a dataGridView on a specific cell.
I got it to work but if the string that I search for isn't found it gives back null. Which screws it up. Here is the code:
string targetSearch = textBox2.Text;
dataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
foreach(DataGridViewRow row in dataGridView1.Rows)
{
if (row.Cells[0].Value.ToString().Equals(targetSearch))
{
row.Selected = true;
break;
}
}
These solutions works for me.
Sample UI
Validate if the value is null.
private void btnSearch_Click(object sender, EventArgs e)
{
string targetSearch = txtSearch.Text.Trim();
dataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
foreach(DataGridViewRow row in dataGridView1.Rows) {
if (row.cells[0].value == null)
continue;
if(row.cells[0].value.Tostring().Trim().Equals(targetSearch)) {
row.Selected = true;
break;
}
}
}
Handle the NullReference Exception
private void btnSearch_Click(object sender, EventArgs e)
{
string targetSearch = txtSearch.Text.Trim();
dataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
try
{
foreach (DataGridViewRow row in dataGridView1.Rows)
{
if (row.Cells[0].Value.ToString().Equals(targetSearch))
{
row.Selected = true;
break;
}
}
}
catch (NullReferenceException ex) { }
}
You are converting null value to string, which gives System.NullReferenceException.
string targetSearch = textBox2.Text;
dataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
foreach(DataGridViewRow row in dataGridView1.Rows)
{
if (row.Cells[0].Value == null) continue; //add this
if (row.Cells[0].Value.ToString().Equals(targetSearch))
{
row.Selected = true;
break;
}
}
There are two things you can try:
Either Set "AllowUserstoAddRows" to False
or
Put the foreach loop in a Try catch block in order to handle the NullReference exception

Filtering a datagridview that does not use a DB, but loads contents from text [duplicate]

This question already has answers here:
Unable To set row visible false of a datagridview
(7 answers)
Closed 5 years ago.
I'm trying to make my datagridview searchable, but I can't seem to get the hang of it. I've tried googling my way to the solution, whereas I found the following:
foreach (DataGridViewRow row in dataGridView1.Rows)
{
if (row.Cells[0].Value.ToString().ToLower().Contains(searchTextBox.Text.ToLower()) && row.Cells[0] != null)
{
row.Visible = true;
}
else
{
row.Visible = false;
}
}
The above code is linked to a button on a form, not a lot to show there. When I enter a string into the textbox, and click the button, I get the following exception:
Row associated with the currency manager's position cannot be made invisible.
I have no idea how to deal with this exception, I've tried various changes, but nothing has helped.
Just to give you an idea, this is my form's code (I only have 1 form):
namespace IntegratorReader
{
public partial class start : Form
{
DataTable dt = new DataTable();
public start()
{
InitializeComponent();
}
private void bOpenFileDialog_Click(object sender, EventArgs e)
{
OpenFileDialog theDialog = new OpenFileDialog();
theDialog.Title = "Open Text File";
theDialog.Filter = "TXT files|*.txt";
theDialog.InitialDirectory = #"C:\";
if (theDialog.ShowDialog() == DialogResult.OK)
{
try
{
if ((theDialog.OpenFile()) != null)
{
string fullPath = theDialog.FileName;
string fileName = theDialog.SafeFileName;
MessageBox.Show(fullPath);
fillGridView(fullPath);
}
}
catch (Exception ex)
{
MessageBox.Show("Error: Could not read file from disk. Original error: " + ex.Message);
}
}
}
private void fillGridView (string path)
{
var regex = new Regex("\\\"(.*?)\\\"");
System.IO.StreamReader file = new System.IO.StreamReader(path);
string[] columnnames = file.ReadLine().Split(',');
foreach (string c in columnnames)
{
dt.Columns.Add(c);
}
string newline;
while ((newline = file.ReadLine()) != null)
{
newline = regex.Replace(newline, m => m.Value.Replace(',', ' '));
DataRow dr = dt.NewRow();
string[] values = newline.Split(',');
for (int i = 0; i < values.Length; i++)
{
string v = values[i];
v = v.Replace("\"", "");
dr[i] = v;
}
dt.Rows.Add(dr);
}
file.Close();
dataGridView1.DataSource = dt;
}
private void searchButton_Click(object sender, EventArgs e)
{
try
{
foreach (DataGridViewRow row in dataGridView1.Rows)
{
if (row.Cells[0].Value.ToString().ToLower().Contains(searchTextBox.Text.ToLower()) && row.Cells[0] != null)
{
row.Visible = true;
}
else
{
row.Visible = false;
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
}
Short example:
I open a .txt file with a lot of rows, whereas I take the first row and base the columns off of that. I then proceed to read the lines and put them into their columns based off of the "," splitter. I realise that this could've been done with a CSV reader of some sort, but I need this to work. This is a work related thing, so I can't give you the exact .txt file, but it generally looks like this, just with more rows and more columns:
"customerID","CustomerName"
"1","Bob"
"2","Dennis"
"3","Richard"
"4","Fernando"
You get the general idea...
Now, my question is, am I completely in the wrong about how to search in a datagridview, or is it simply a smaller issue that I'm overseeing?
Is there a better way to search in a datagridview, if yes, how?
For reference, this is how the form looks:
Question is very much duplicate of this:
There two issues with your code, 1- It does not perform check for empty row(last row) and it does not suspend currencyManager while hiding a row.
To fix, replace your searchButton_Click code with following:
private void searchButton_Click(object sender, EventArgs e)
{
try
{
foreach (DataGridViewRow row in dataGridView1.Rows)
{
CurrencyManager currencyManager1 = (CurrencyManager)BindingContext[dataGridView1.DataSource];
currencyManager1.SuspendBinding();
if (row.IsNewRow)
continue;
if (row.Cells[0].Value.ToString().ToLower().Contains(searchTextBox.Text.ToLower()) )
{
row.Visible = true;
}
else
{
row.Visible = false;
}
currencyManager1.ResumeBinding();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
**Answer Update **
Another option is to set the current cell to null before deleting OR hiding a row as the error tells that "
Row is associated with the currency Manager
".
if(dataGridView1.CurrentCell!=null)
dataGridView1.CurrentCell = null;
row.Visible = true;//row.Visible = false

How to avoid duplicate rows when inserting data in a DataGridView?

I am developing an application that reads RFID's in the serial port. I use a timer to keep scaning until the user clicks a button.
When the scanner find a new RFID, I insert the info in a DataGridView, if the RFID is already in the DataGridView I paint the row with green color and if a RFID in the DataGridView is not anymore in the scanner range I paint it with white color.
My problem is that with small lapses of time the RFID is inserted more than once.
I thought that it was for the threads in the timer, so I put a lock inside, but if I try to scan in lapses smallers than 200 ms the problem still happening.
Is there a way to improve the code?
void tmrRepeatedScan_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
try
{
lock (thisLock)
{
var detectedTags = serialPort.Scan(true, false, true, false);
foreach (var tag in detectedTags)
{
bool tagFound = false;
string TID = Regex.Replace(Conversions.ByteToHexadecimal(tag.GetTagId()), "(.{2})(?!$)", "$0-");
string EPC = Regex.Replace(Conversions.ByteToHexadecimal(tag.GetEpc()), "(.{2})(?!$)", "$0-");
if (!tagsReaded.Contains(TID))
{
tagsReaded.Add(TID);
}
foreach (DataGridViewRow row in dgvTags.Rows)
{
if (row.Cells[0].Value.ToString().Equals(TID))
{
row.DefaultCellStyle.BackColor = Color.LightGreen;
tagFound = true;
break;
}
}
if (!tagFound)
{
dgvTags.BeginInvoke(new InvokeDelegate(() => AddRow(TID, EPC)));
}
}
}
foreach (DataGridViewRow row in dgvTags.Rows)
{
if (!tagsReaded.Contains(row.Cells[0].Value.ToString()))
{
row.DefaultCellStyle.BackColor = Color.White;
}
}
}
catch (Exception ex)
{
foreach (DataGridViewRow row in dgvTags.Rows)
{
row.DefaultCellStyle.BackColor = Color.White;
}
}
}
Sorry if my english is not very good.
You can switch it back to BeginInvoke but you need to include a check to see if your tag exists in tagsReaded before doing AddRow. Try this:
void tmrRepeatedScan_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
try
{
lock (thisLock)
{
var detectedTags = serialPort.Scan(true, false, true, false);
foreach (var tag in detectedTags)
{
bool tagFound = false;
string TID = Regex.Replace(Conversions.ByteToHexadecimal(tag.GetTagId()), "(.{2})(?!$)", "$0-");
string EPC = Regex.Replace(Conversions.ByteToHexadecimal(tag.GetEpc()), "(.{2})(?!$)", "$0-");
foreach (DataGridViewRow row in dgvTags.Rows)
{
if (row.Cells[0].Value.ToString().Equals(TID))
{
row.DefaultCellStyle.BackColor = Color.LightGreen;
tagFound = true;
break;
}
}
bool isNewTag = !tagsReaded.Contains(TID);
if (!tagFound && isNewTag )
{
dgvTags.BeginInvoke(new InvokeDelegate(() => AddRow(TID, EPC)));
}
if (newTag)
{
tagsReaded.Add(TID);
}
}
}
foreach (DataGridViewRow row in dgvTags.Rows)
{
if (!tagsReaded.Contains(row.Cells[0].Value.ToString()))
{
row.DefaultCellStyle.BackColor = Color.White;
}
}
}
catch (Exception ex)
{
foreach (DataGridViewRow row in dgvTags.Rows)
{
row.DefaultCellStyle.BackColor = Color.White;
}
}
}

Check if row modified in gridview

I have a GridView and I'm performing a bulk update for only one column with textbox. But before update I need to check the values of that textbox for entire gridview and if the values aren't changed I need to provide an alert stating "Make change to field to update".
Can anyone suggest me some options?
protected void btnUpdate_Click(object sender, EventArgs e)
{
try
{
foreach (GridViewRow row in gvDetails.Rows)
{
string strID = ((Label)row.FindControl("lblID")).Text;
string strGroup = ((Label)row.FindControl("lblGrp")).Text;
string strValue = ((TextBox)row.FindControl("txtValue")).Text;
{
//my update query
}
}
}
catch (Exception ex)
{
}
}
A much simpler method will be to add an asp:HiddenField to your ItemTemplate and compare the value for each row.
<asp:HiddenField ID="" runat="server" Value='<%# Eval("blah") %>'></asp:HiddenField>
Now all you need is to compare that value with the textbox value in each row in code-behind like this.
protected void btnUpdate_Click(object sender, EventArgs e)
{
try
{
var isAnyRowUpdated = false;
foreach (GridViewRow row in gvDetails.Rows)
{
string strID = ((Label)row.FindControl("lblID")).Text;
string strGroup = ((Label)row.FindControl("lblGrp")).Text;
string strValue = ((TextBox)row.FindControl("txtValue")).Text;
string strOldValue = ((HiddenField)row.FindControl("hdnOldValue")).Value;
if (strValue != strOldValue)
{
isAnyRowUpdated = true;
//update procedure here.
}
}
//now check if the flag is still false
//that means no rows are changed
if(!isAnyRowUpdated)
{
//alert no rows are updated
}
}
catch (Exception ex)
{
}
}
I hope the code is self explanatory.
You can try to use DataGridView.RowValidating Event and check if IsCurrentRowDirty Property is changed
IsCurrentRowDirty Property Gets a value indicating whether the current
row has uncommitted changes.
EDIT:-
The above works in Winforms; in Asp.net there is no such method, you have to load the data in a object and then you have to validate.
You can check Updating Only Changed Rows Using GridView Control
The basic thing to do here to load your original data into a DataTable and then compare the GridView rows, one by one in a loop for that particular Column, which in your case is the TextBox column. For comparing the DataTable with the GridView, you can try something like this :
foreach (GridViewRow row in Grd.Rows)
{
TextBox txtAmt = (TextBox)row.FindControl("txtAmount");
string Id = Grd.DataKeys[row.RowIndex].Value.ToString();
for (int i = 0; i < dt.Rows.Count; i++)
{
if (Id == dt.Rows[i]["ID"].ToString())
{
//do your logic here.
}
}
}
Hope this helps.
As per the conversation, I have provided some pseudo logic. You can implement your own code using this.
protected void btnUpdate_Click(object sender, EventArgs e)
{
try
{
DataTable dt = LoadData(); //Load the data from DB
EnumerableRowCollection<DataRow> enumerableDt = dt.AsEnumerable();
foreach (GridViewRow row in gvDetails.Rows)
{
string strID = ((Label)row.FindControl("lblID")).Text;
string strGroup = ((Label)row.FindControl("lblGrp")).Text;
string strValue = ((TextBox)row.FindControl("txtValue")).Text;
DataRow dr = enumerableDt.Where(x => x.Field<string>("ID") == strID).FirstOrDefault(); //Change your condition accordingly
if (dr["Value"].ToString().ToUpper() != strValue.Trim().ToUpper()) //Change your condition here
{
//Do your updated data logic here
}
else
{
//Do your not updated data logic here
}
}
}
catch (Exception ex)
{
}
}
Check them against the datakey value for the corresponding item.
<MasterTableView DataKeyNames="Response, ...
foreach (GridDataItem item in FTReport.MasterTableView.Items)
{
string ResponseValue = tbResponse.Text;
if (item.GetDataKeyValue("Response").ToString() != ResponseValue)
{
do something;
}
}
use onrowdatabound property of gridview. Inside it, use:
protected void GridLocation_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow &&
(e.Row.RowState & DataControlRowState.Edit) == DataControlRowState.Edit)
{
// here you can check your textbox values
}
}

Search for value in DataGridView in a column

I want the user to be able to search for a number in a column in the DataGridView (dgv). The dgv can hold many records. Each record has a Project Number. So I want the user to be able to search for a project number in column Project Number. The columns I have are: ProjectID(not visible); Image(no headertext); Project Number; Project Name; Company; Contact.
Here is my code:
private void btnSearch_Click(object sender, EventArgs e)
{
string searchValue = textBox1.Text;
int rowIndex = -1;
dgvProjects.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
try
{
foreach (DataGridViewRow row in dgvProjects.Rows)
{
if (row.Cells[row.Index].Value.ToString().Equals(searchValue))
{
rowIndex = row.Index;
dgvProjects.Rows[row.Index].Selected = true;
break;
}
}
}
catch (Exception exc)
{
MessageBox.Show(exc.Message);
}
}
Problem #1: What it does so far: The user types the project number in TextBox1. When he/she clicks the button, the code searches for this string in the rows, and when found the project number, that row gets selected. It works fine, but only once. When I want to search for an other project number, nothing happens.
Problem #2: I think this can be done in a better way, by searching the values for column Project Name only. But how should I do this properly?
The code I used to search comes from this answer
Why you are using row.Cells[row.Index]. You need to specify index of column you want to search (Problem #2). For example, you need to change row.Cells[row.Index] to row.Cells[2] where 2 is index of your column:
private void btnSearch_Click(object sender, EventArgs e)
{
string searchValue = textBox1.Text;
dgvProjects.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
try
{
foreach (DataGridViewRow row in dgvProjects.Rows)
{
if (row.Cells[2].Value.ToString().Equals(searchValue))
{
row.Selected = true;
break;
}
}
}
catch (Exception exc)
{
MessageBox.Show(exc.Message);
}
}
Why don't you build a DataTable first then assign it to the DataGridView as DataSource:
DataTable table4DataSource=new DataTable();
table4DataSource.Columns.Add("col00");
table4DataSource.Columns.Add("col01");
table4DataSource.Columns.Add("col02");
...
(add your rows, manually, in a circle or via a DataReader from a database table)
(assign the datasource)
dtGrdViewGrid.DataSource = table4DataSource;
and then use:
(dtGrdViewGrid.DataSource as DataTable).DefaultView.RowFilter = "col00 = '" + textBoxSearch.Text+ "'";
dtGrdViewGrid.Refresh();
You can even put this piece of code within your textbox_textchange event and your filtered values will be showing as you write.
It's better also to separate your logic in another method, or maybe in another class.
This method will help you retreive the DataGridViewCell object in which the text was found.
/// <summary>
/// Check if a given text exists in the given DataGridView at a given column index
/// </summary>
/// <param name="searchText"></param>
/// <param name="dataGridView"></param>
/// <param name="columnIndex"></param>
/// <returns>The cell in which the searchText was found</returns>
private DataGridViewCell GetCellWhereTextExistsInGridView(string searchText, DataGridView dataGridView, int columnIndex)
{
DataGridViewCell cellWhereTextIsMet = null;
// For every row in the grid (obviously)
foreach (DataGridViewRow row in dataGridView.Rows)
{
// I did not test this case, but cell.Value is an object, and objects can be null
// So check if the cell is null before using .ToString()
if (row.Cells[columnIndex].Value != null && searchText == row.Cells[columnIndex].Value.ToString())
{
// the searchText is equals to the text in this cell.
cellWhereTextIsMet = row.Cells[columnIndex];
break;
}
}
return cellWhereTextIsMet;
}
private void button_click(object sender, EventArgs e)
{
DataGridViewCell cell = GetCellWhereTextExistsInGridView(textBox1.Text, myGridView, 2);
if (cell != null)
{
// Value exists in the grid
// you can do extra stuff on the cell
cell.Style = new DataGridViewCellStyle { ForeColor = Color.Red };
}
else
{
// Value does not exist in the grid
}
}
// This is the exact code for search facility in datagridview.
private void buttonSearch_Click(object sender, EventArgs e)
{
string searchValue=textBoxSearch.Text;
int rowIndex = 1; //this one is depending on the position of cell or column
//string first_row_data=dataGridView1.Rows[0].Cells[0].Value.ToString() ;
dataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
try
{
bool valueResulet = true;
foreach (DataGridViewRow row in dataGridView1.Rows)
{
if (row.Cells[rowIndex].Value.ToString().Equals(searchValue))
{
rowIndex = row.Index;
dataGridView1.Rows[rowIndex].Selected = true;
rowIndex++;
valueResulet = false;
}
}
if (valueResulet != false)
{
MessageBox.Show("Record is not avalable for this Name"+textBoxSearch.Text,"Not Found");
return;
}
}
catch (Exception exc)
{
MessageBox.Show(exc.Message);
}
}
Filter the data directly from DataTable or Dataset:
"MyTable".DefaultView.RowFilter = "<DataTable Field> LIKE '%" + textBox1.Text + "%'";
this.dataGridView1.DataSource = "MyTable".DefaultView;
Use this code on event KeyUp of Textbox, replace "MyTable" for you table name or dataset, replace for the field where you want make the search.
"MyTable".DefaultView.RowFilter = " LIKE '%" + textBox1.Text + "%'";
this.dataGridView1.DataSource = "MyTable".DefaultView;
How about the relation to the database connections and the Datatable? And how should i set the DefaultView correct?
I use this code to get the data out:
con = new System.Data.SqlServerCe.SqlCeConnection();
con.ConnectionString = "Data Source=C:\\Users\\mhadj\\Documents\\Visual Studio 2015\\Projects\\data_base_test_2\\Sample.sdf";
con.Open();
DataTable dt = new DataTable();
adapt = new System.Data.SqlServerCe.SqlCeDataAdapter("select * from tbl_Record", con);
adapt.Fill(dt);
dataGridView1.DataSource = dt;
con.Close();
private void btnSearch_Click(object sender, EventArgs e)
{
try
{
string searchValue = txtSearch.Text;
string colName = dataGridView1.Columns[1].Name;//Column Number of Search
((DataTable)dataGridView1.DataSource).DefaultView.RowFilter = string.Format(colName+" like '%{0}%'", searchValue.Trim().Replace("'", "''"));
}
catch (Exception exc)
{
MessageBox.Show(exc.Message);
}
}
private void txtSearch_TextChanged(object sender, EventArgs e)
{
btnSearch_Click(null,null);
}
private void txtSearch_TextChanged(object sender, EventArgs e)
{
string searchValue = txtSearch.Text;
for (var i = 0; i <= dgvList.RowCount; i++)
{
for (var j = 0; j <= dgvList.ColumnCount; j++)
{
if ((dgvList.Item(j, i).FormattedValue.ToString.ToLower).ToString.Contains(searchValue.ToString.ToLower))
{
Console.Writeline("found");
dgvList.Item(j, i).Selected = true;
return;
}
}
}
}
This method will search all rows and cells in the DataGridView, If result is true then select the row.
I'm can solve it simply:
public static int SearchDGV(DataGridView dgv, string SearchValue, string ColName)
{
foreach (DataGridViewRow Row in dgv.Rows)
{
if (Row.Cells[ColName].Value.ToString().Equals(SearchValue))
return Row.Index;
}
return -1;
}
private void textBox3_TextChanged(object sender, EventArgs e)
{
DataView dv = ds.Tables["todo"].DefaultView;
dv.RowFilter = "topic LIKE '" + textBox3.Text + "%'";
dataGridView1.DataSource = dv;
}

Categories

Resources