FindControl not working on GridView when inserting bound columns - c#

I have a gridview with several ItemTemplates. The first contains a checkbox the rest contain textboxes.
I then added dynamically some bound controls like this:
BoundField bdfPrivName = new BoundField();
clsUtilities.SetBoundFieldCenter(ref bdfPrivName, "PrivName", "Priv Name");
BoundField bdfDescription = new BoundField();
clsUtilities.SetBoundFieldLeft(ref bdfDescription, "PrivDesc", "Description");
BoundField bdfLive = new BoundField();
clsUtilities.SetBoundFieldCenter(ref bdfLive, "Live","Active?");
grdExisting.Columns.Add(bdfPrivName);
grdExisting.Columns.Add(bdfDescription);
grdExisting.Columns.Add(bdfLive);
I then use FindControl() to locate the checkbox and textboxes and perform my logic based the result
foreach (GridViewRow gvr in grdMissing.Rows)
{
mckbAny = (CheckBox)gvr.FindControl("ckbAdd");
mtxtApplyDate = (TextBox)gvr.FindControl("txtAddApplyDate");
mtxtDateToAdd = (TextBox)gvr.FindControl("txtAddDateToAdd");
mtxtDateToRemove = (TextBox)gvr.FindControl("txtAddDateToRemove");
}
etc.
This all worked fine. I then got a request to put the bound fields as the second, third and fourth columns, after the check box and before the textboxes. I found that this was easy to do by changing the Add’s to Inserts as follows:
grdExisting.Columns.Insert(1, bdfPrivName);
grdExisting.Columns.Insert(2, bdfDescription);
grdExisting.Columns.Insert(3, bdfLive);
It looked fine of the page, but the FindControl(), all of them fail to work.
Please suggest a solution or a workaround.
Thanks in advance.

It sounds like you have come across this bug:
https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=104994&wa=wsignin1.0
It appears ViewState is not stored (or restored) when a BoundField is inserted into a GridView. So when you do FindControl it doesn't exist.
You could try adding them as you did before and finding some way of re-arranging the columns (I think this is possible).

I am not sure how it was working for you before, as controls don't belong in row - they are inside cells. Anyways, the issue is that FindControl is not recursive, it will not search entire control tree - only immediate children of the control you run it on. You need to implement your own recursive findcontrol, for example like so:
public static Control FindControlRecursive(Control Root, string Id)
{
if (Root.ID == Id)
return Root;
foreach (Control c in Root.Controls)
{
Control fc = FindControlRecursive(c, Id);
if (fc != null)
return fc;
}
return null;
}

Related

Can I select checkboxes by ID?

I would like to select checkboxes
not by Design name but by parameters. More specifically, I would like to
check the checkboxes which has the parameters which are also in the data
retrieved from the database.
Example code :
(checkBox) C1.checked = true;
This is how I can set checked right now,
but I want to do something like...
string[] datas = db.getData();
foreach (string data in datas)
{
if (data.Equals("C1"))
{
C1.checked = true;
}
}
Of course I can do this for every checkboxes,
but there are over 50 checkboxes and I think it's stupid
to manually checking this but I couldn't find a way to select a particular checkbox based on the name.
Also, it would be really helpful if someone knows a way to group the textboxes,
so that I don't have to loop over every checkboxes every time. By that, I mean something like contains method within a group of checkboxes to find particular one.
It seems like your main goal is to find:
a way to group the textboxes, so that I don't have to loop over every
checkboxes every time.
You can create a Dictionary of string/Checkbox and select the checkbox that way.
Something like:
string key = "C1";
Dictionary<string, CheckBox> pairs = new Dictionary<string, CheckBox>();
pairs[key].Checked = true;
You Can do like below Using FindControl :
string[] datas = db.getData();
foreach (string data in datas)
{
CheckBox chk = this.Controls.Find(data, true).FirstOrDefault() as CheckBox;
if(chk !=null)
chk.Checked = true;
}

How to retrieve data from dynamically created textboxes and build strings

So I have some code, that creates a row of 4 textboxes. The data from those 4 textboxes will be combined to make a SQL query and then executed. The button to add a row can be hit an infinite amount of times. I have my IDs generated so that on the first click textbox one is txtBox1Row1, then on the second click it is txtBox1Row2 etc, etc.
Now I need a way to retrieve the data from each row and build SQL queries from them. Just to reiterate, I only need the data from the 4 textboxes in each row per loop (I assume thats how this will need to be done).
So how do I go about doing this?
Thanks a lot, the help is always appreciated. I was planning on doing it like this:
foreach (Control c in pnlArea.Controls)
{
if (c.ID.Contains("ddlArea"))
{
area.Add(((DropDownList)c).SelectedValue);
}
if (c.ID.Contains("txtArea"))
{
areaOther.Add(((TextBox)c).Text);
}
if (c.ID.Contains("ddHazard"))
{
hazard.Add(((DropDownList)c).SelectedValue);
}
if (c.ID.Contains("txtHazard"))
{
hazardOther.Add(((TextBox)c).Text);
}
if (c.ID.Contains("txtHazardDesc"))
{
hazardDesc.Add(((TextBox)c).Text);
}
if (c.ID.Contains("txtActionDesc"))
{
actionDesc.Add(((TextBox)c).Text);
}
if (c.ID.Contains("calDueDate"))
{
dueDate.Add(((Calendar)c).SelectedDate);
}
}
Per Nkosi, you'd want to encapsulate your textbox 'area' in a form.
What I would recommend is looping through each textbox in the form and storing in a dictionary.
Here's what that might look like:
Dictionary<string, string> textBoxVals = new Dictionary<string, string>();
Control form = this.FindControl("form1") as Control;
TextBox tb;
foreach (Control c in form.Controls)
{
if (c.GetType() == typeof(TextBox))
{
tb = c as TextBox;
textBoxVals.Add(tb.ID, tb.Text);
}
}
Then you'd be able to loop through the dictionary to build your SQL string. The dictionary would contain both the dynamic name of the control and the value of the textbox.
Based on the code you posted it looks like maybe there are some other controls that might also be associated with each line. If that's the case, you can create another if statement in the foreach loop to handle different control types, so that you're saving the proper parameters of them.
I hope that helps.

How do I obtain selected rows in a DataGridView from different pages

I have a windows forms DataGridView, where I have data and a checkbox for each row.
I will select check box for a particular row and all the selected rows will be populated in another page.
if (grdEmp.Rows.Count > 0)
{
var selectedEmpIDs= from DataGridViewRow coll in grdEmp.Rows
where Convert.ToBoolean(coll.Cells["Select"].Value) == true
select coll;
if (selectedEmpIDs.Count() > 0)
{
foreach (DataGridViewRow row in selectedEmpIDs)
{
selectedEmp+= row.Cells["EmpId"].Value + ",";
}
}
}
This works good only for one page.
When I navigate to another page, and click the selected rows, the previous one goes off.
How do I resolve it.
Thanks
cmrhema
Note :Sorry for the confusion, When I meant it works good for a page, I meant paging.
I think I need to add more inputs,
There are 10 pages in the gridview.
I select the first record from each page of the gridview, one after another by clicking next page( Page next button).
But only the record that was selected the last is getting displayed and others and ignored off.
What could be the prblm
You can use a List or Dictionary or any other collection type globally, using Program.cs or using a static class. And store the selected rows into the list before you leave the page.
Rather than using a comma delimited string string for your list of ids you can instead use a List.
Your code will then become something like this:
if (grdEmp.Rows.Count > 0)
{
var selectedEmpIDs= from DataGridViewRow coll in grdEmp.Rows
where Convert.ToBoolean(coll.Cells["Select"].Value) == true s
select coll;
if (selectedEmpIDs.Count() > 0)
{
foreach (DataGridViewRow row in selectedEmpIDs)
{
if (!listOfIds.Contains((int)row.Cells["EmpId"].Value))
{
listOfIds.Add(((int)row.Cells["EmpId"].Value));
}
}
}
}
You will need methods to remove items from this list so adding event handlers for the checkbox selected event will probably work better.
The List object itself can simple live as a class level object of the form that containst your DataGridView.
This gets a little bit more complicated if you are managing your paging across forms, but the same principles of maintaining a list of selected ids applies.

How can I hide empty columns in a gridview without knowing which will be empty?

I am working with a gridview that pulls data from a SQL database based on selections in dropdown lists. The source table has six columns for attributes of the selection, but depending on what is chosen, there could be anywhere from one to six of those that are empty (all null values). When the column is empty, I would like to have it hidden so the page is less clunky and confusing.
I've searched around for an answer for the past couple days, but what I have found so far is either related to hiding columns that you know are empty which I will not know or removing them in the SQL code which I think doesn't work if the column is called for in the gridview code and doesn't exist in the query.
I'm very new to ASP.NET, so I'm sorry if some of my terminology is off! Let me know if you have any questions about what I'm asking.
Thanks in advance for your help!
Instead of hiding empty columns, why don't you add the columns you want in code behind?
When you retrieve the data to be displayed, you know which columns are present. You can add them and databind them in code behind.
To get you started with this, here is some code from a helpful article on how to do this:
BoundField nameColumn = new BoundField();
nameColumn.DataField = "Name";
nameColumn.HeaderText = "Person Name";
GridView1.Columns.Add(nameColumn);
You could try:
Set up the gridview will all columns hidden (Visible="false")
In the gridview's RowDataBound event, check each of the column values and if it has data, set the column to show (Visible="true")
The RowDataBound method could look something like this:
void YourGridview_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
// if this is a SqlDataSource you can use DataRowView,
// otherwise use whatever type is used in the data source
DataRowView rowView = (DataRowView)e.Row.DataItem;
if (!String.IsNullOrEmpty(rowView["ColumnA"]))
YourGridview.Columns[0].Visible = true;
// repeat for your other columns
}
}
Best way to do that for web:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.UI.WebControls;
namespace MyProject
{
public static class ExtensionGridView
{
public static GridView RemoveEmptyColumns(this GridView gv)
{
// Make sure there are at least header row
if (gv.HeaderRow != null)
{
int columnIndex = 0;
// For each column
foreach (DataControlFieldCell clm in gv.HeaderRow.Cells)
{
bool notAvailable = true;
// For each row
foreach (GridViewRow row in gv.Rows)
{
string columnData = row.Cells[columnIndex].Text;
if (!(string.IsNullOrEmpty(columnData) || columnData ==" "))
{
notAvailable = false;
break;
}
}
if (notAvailable)
{
// Hide the target header cell
gv.HeaderRow.Cells[columnIndex].Visible = false;
// Hide the target cell of each row
foreach (GridViewRow row in gv.Rows)
row.Cells[columnIndex].Visible = false;
}
columnIndex++;
}
}
return gv;
}
}
}
Instead of this code:
if (!String.IsNullOrEmpty(rowView["ColumnA"]))
YourGridview.Columns[0].Visible = true;
Use this one:
if (!String.IsNullOrEmpty(Convert.Tostring(rowView["ColumnA"])) )
YourGridview.Columns[0].Visible = true;
on the onItemBound event of the grid, you can check the value of the variable to be set in the column cell and set its visibility accordingly

Can't get values from rows/cells in GridView

I'm trying get values from a GridView using the following code:
foreach (GridViewRow row in this.dgvEstudios.Rows)
{
var xy = row.Cells[1].Text;
}
Always get a an empty string ("") as the value returned from .Text, why does this happen? I have set EnableViewState to true
If there are controls in each cell you will need to get the value like this:
Label lblEmailAddress = GridView.Rows[e.CommandArgument].FindControl("lblEmailAddress");
string Email = lblEmailAddress.Text;
in that case it it the control in the cell that has the value not the cell iteslf.
The cell might have controls inside it (e.g. LiteralControl or an HyperLink). This is what you should be looking for.
row.Cells[1].Controls collection, you should look for.
it could depend on many things.. Where is this code fired in relation to when the GridView is populated (Databind() called)?
Without any context, its hard to say what else it could be.

Categories