Accesing controls of a FormView, getting Null references - asp.net

I have a FormView and I need to access some Divs and other controls that are inside it. My apsx code looks similar to this:
<asp:FormView ID="Edit_FV" runat="server" DataKeyNames="IDproceso" DefaultMode="Edit" DataSourceID="SqlDS_Procesos">
<EditItemTemplate>
<div id="second_info" runat="server">
<div id="second_info_left" runat="server">
<div id="alcance" class="report_field" runat="server">
<p class="container-title">
Alcance:</p>
<asp:TextBox ID="TextBox14" runat="server" TextMode="multiline" Width="400px" Height="120px" Text='<%# Bind("alcance") %>' />
</div>
</div>
<div id="second_info_right" runat="server">
<div class="valores-container" id="tipo_ahorro" runat="server">
<asp:CheckBox ID="ahorro_state" runat="server" Checked='<%# Bind("tipo_ahorro") %>' />
</div>
</div>
</EditItemTemplate>
</asp:FormView>
Now, say I want to access the CheckBox with id = ahorro_state, I tried with Edit_FV.FindControl("ahorro_state") and got a Null reference. I also tried with Edit_FV.FindControl("MainContent_Edit_FV_ahorro_state") because this is how the ID actually gets named in the final HTML document, but I got a Null reference too. The same happened when I tried accessing any of the divs (with IDs second_info,tipo_ahorro, etc..). I feel I'm doing a dumb mistake but I looked around a bit and haven't found and answer.
Any ideas how to solve this?
EDIT: Added Code where I'm calling FindControl.
I tried both calling DataBind() from the Page_Load():
protected void Page_Load(object sender, EventArgs e)
{
DataBind();
if (Edit_FV.CurrentMode == FormViewMode.Edit)
{
Control c = Edit_FV.FindControl("ahorro_state");//c is null here.
}
}
And also tried setting the OnDataBound attribute of Edit_FV: OnDataBound="onBound"
protected void onBound(object sender, EventArgs e)
{
if (Edit_FV.CurrentMode == FormViewMode.Edit)
{
ControlCollection a = Edit_FV.Controls;
Control c = Edit_FV.FindControl("ahorro_state");//c is null here
}
}

Although the default mode is set "Edit", the form view won't switch to that mode until the control is DataBound. Try calling DataBind() first, then use FindControl using the ID of your element (not the ClientID, as you tried in your second example).
See FormView.FindControl(): object reference error for examples of where to put your FindControl logic.
EDIT:
There is also the possibility that your data source is not returning any data. This will result in the EditItemTemplate being empty which might explain your null reference errors. Try checking for a Edit_FV.DataItemCount > 0 before switching into Edit mode.

I have had similar problems with 'FindControl'. I found a piece of code that has helped me a) Find controls recursively, and b) the debug statement has been very help to see why I am not finding the control in question. To help me find the controls I have to give them ID values when I am looking for them if they don't have one by default:
public static class General_ControlExtensions
{
//From: http://www.devtoolshed.com/content/find-control-templatefield-programmatically
/// <summary>
/// recursively finds a child control of the specified parent.
/// USAGE:
/// Control controlToFind = DetailsView1.fn_ReturnControl_givenControlID("txtName");
/// </summary>
/// <param name="rootControl"></param>
/// <param name="ID"></param>
/// <returns></returns>
public static Control fn_ReturnControl_givenControlID(this Control rootControl, string ID)
{
if (rootControl.ID == ID)
{
return rootControl;
}
foreach (Control control in rootControl.Controls)
{
Debug.WriteLine("FindByID - child.id: " + control.ID);
Control foundControl = fn_ReturnControl_givenControlID(control, ID);
if (foundControl != null)
{
return foundControl;
}
}
return null;
}
Here is an example of its usage:
using System.Diagnostics; // for debug
TextBox txt_LastName = (TextBox)fv_NewHire_DetailsForm.fn_ReturnControl_givenControlID("INSERT_txt_LastName");
In addition, I have found it helpful for this type of problem to preface the controls in the 'insertitemtemplate' with 'INSERT_", and controls in the 'edititemtemplate' with 'EDIT_" to quickly tell them apart in the debug output.

Related

Why binding event is not called after submit button clicked for user control (added code)

I have a web page that uses single user control and a asp:repeater that uses the same user control and both are created in Page_Load. Both bahave differently in a submit button clicking event. The binding event for each of the repeater (depdentBasicInfo)'s user control is called after submit button clicking before page_load. But the binding event is not for the single user control (spouseBasicInfo). Why? Also the user control create a runtime control (assuming a TextBox). I found there is no way to retrieve the runtime control's Text property after clicking the submit as the control becomes null after PostBack. How to retrieve the property easily? Could any one help me? Thanks.
<!--USER CONTROL -->
<asp:Panel runat="server" ID="PnlSpouseInformation" Visible="true">
<h3 id="ApplicantLabel" runat="server"></h3>
<div class="dependentInformation">
<asp:PlaceHolder ID="phDependentInformation" runat="server"></asp:PlaceHolder>
</div>
</asp:Panel>
<!-- code behind -->
public partial class userInfo : System.Web.UI.UserControl
{
public string identity;
public string applicantTitle
{
set { ApplicantLabel.InnerText = value; }
}
protected void Page_Load(object sender, EventArgs e)
{
}
public void Bind()
{
WebControl textBox = new TextBox
{
Text = identity,
ID = "textbox"
};
phDependentInformation.Controls.Add(textBox);
}
}
<html>
<head runat="server">
<title>Test user control binding</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<uc1:userInfo runat="server" ID="SpouseBasicInfo" Visible="false" />
<asp:Repeater runat="server" ID="RptDependents" OnItemCreated="RptDependents_ItemCreated">
<ItemTemplate>
<uc1:userInfo runat="server" ID="DependentBasicInfo" />
</ItemTemplate>
</asp:Repeater>
<asp:LinkButton runat="server" ID="submit" OnClick="OnClickSubmit"><span>Submit</span></asp:LinkButton>
</div>
</form>
</body>
public partial class _default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
SpouseBasicInfo.identity = "spouse";
SpouseBasicInfo.Bind();
SpouseBasicInfo.Visible = true;
List<String> list = new List<string>();
list.Add("Dependent A");
list.Add("Dependent B");
RptDependents.DataSource = list;
RptDependents.DataBind();
}
}
protected void OnClickSubmit(object sender, EventArgs e)
{
if (!Page.IsValid)
{
return;
}
var textbox = SpouseBasicInfo.FindControl("textbox") as TextBox;
string spouseName = textbox.Text;
}
protected void RptDependents_ItemCreated(object sender, RepeaterItemEventArgs e)
{
switch (e.Item.ItemType)
{
case ListItemType.Item:
case ListItemType.AlternatingItem:
{
var dependentInfo = e.Item.DataItem as String;
var dependentBasicInfo = e.Item.FindControl("DependentBasicInfo") as userInfo;
if (dependentBasicInfo == null) return;
dependentBasicInfo.applicantTitle = "Dependent " + (e.Item.ItemIndex + 1);
dependentBasicInfo.identity = dependentInfo;
dependentBasicInfo.Bind();
}
break;
}
}
}
</html>
Can you please post your code so we can get a look at it? Also, you say the runtime control becomes null after post back. Is the text property of the control set after the page is rendered? if so you may be able to use a hidden asp label on the page that can hold the text property. When the page is posted back the hidden label still holds the last value held for the text property of said control.
"User control (spouseBasicInfo). Why? Also the user control create a runtime control (assuming a TextBox). I found there is no way to retrieve the runtime control's Text property after clicking the submit as the control becomes null after PostBack. How to retrieve the property easily? Could any one help me? Thanks."
I haven't been working with asp.net for a long time so I don't think I can answer all the questions you are asking. But, if you are creating a text box at runtime and using the placeholder to deliver the object to the screen then yes, the textbox control will be null after post back. This is because it is not an asp.control like a textbox or label that is not runtime created. You can retrieve the value previously held in the text box by creating a label on screen that is hidden. Place your value in the hidden label and the runtime created text box. After post back the hidden label will still hold the value previous to post back and you can retrieve as you would normally retrieve a value held in a control. I hope this helps you get a step closer to solving your issue. Sorry I can't be of more help. Good luck.

Set ContextKey for AutoCompleteExtender inside GridView

I have an AJAX AutoCompleteExtender, in a GridView, as seen below:
<asp:GridView
ID="GV1"
runat="server"
AllowPaging="True"
OnPageIndexChanging="GV1_OnPageIndexChanging"
OnRowCommand="GV1_RowCommand">
...
<asp:TextBox
ID="txt1"
runat="server"
onkeyup = "SetContextKey()">
</asp:TextBox>
<cc1:AutoCompleteExtender
ID="AutoCompleteExtender1"
runat="server"
TargetControlID="txt1"
ServiceMethod="GetACEList"
ServicePath="AutoComplete.asmx"
UseContextKey = "true"
MinimumPrefixLength="1"
EnableCaching="true"
CompletionSetCount="1"
CompletionInterval="100"
CompletionListCssClass="autocomplete_completionListElement"
CompletionListItemCssClass="autocomplete_listItem"
CompletionListHighlightedItemCssClass="autocomplete_highlightedListItem">
</cc1:AutoCompleteExtender>
...
</asp:GridView>
When trying to set the context key, I am unable to access the AutoCompleteExtender on the client side as well as the server side.
On the client-side, I tried:
function SetContextKey() {
$find('AutoCompleteExtender1').set_contextKey($get("<%=ddlCountry.ClientID%>").value);
}
but JavaScript is unable to find the 'AutoCompleteExtender1' object. I realize that this is because there are a lot of 'AutoCompleteExtender1' objects in the generated HTML, each with a unique ID.
I then found this article, and I tried setting the context key on the server side:
protected void ddlCountry_OnSelectedIndexChanged(object sender, EventArgs e) {
AutoCompleteExtender1.ContextKey = ddlCountry.SelectedValue;
}
but the code compilation fails with the error:
The name 'AutoCompleteExtender1' does not exist in the current context
QUESTION:
How do I access the AutoCompleteExtender1 object on selected-index change of the drop down so I can set the context key?
Got it! I fixed how I was accessing the object incorrectly on the server side, and it worked!
Here's the server side code -- on selected-index change of the drop down, I loop through each of the rows of the GridView, and set each AutoCompleteExtender object's ContextKey to the drop-down's selected value:
protected void ddlCountry_OnSelectedIndexChanged(object sender, EventArgs e) {
foreach (GridViewRow gvRow in gvGV1.Rows) {
AjaxControlToolkit.AutoCompleteExtender AutoCompleteExtender1
= (AjaxControlToolkit.AutoCompleteExtender)gvRow.FindControl("AutoCompleteExtender1");
AutoCompleteExtender1.ContextKey = ddlCountry.SelectedValue;
}
}
Hope this helps someone stumbling upon this problem!
P.S:
I gave up trying to achieve the same thing on the client-side. I believe being able to set the Context Key on the server side (in C#) has a lot of advantages in terms of flexibility (like being able to change the context key as and when required, and not just on change of a drop-down). Still, if anyone knows how it can be done on the client-side (in JavaScript), please share.
Below code can be used if you want it to set at HTML end instead of c# code
for (var i = 0; i < grid.rows.length - 1; i++) {
var txtAmountReceive = $("input[id*=txt1]")
var GridRowID = (txtAmountReceive[i].valueOf('id').id).replace("txt1", "");
var AutoCompleteExt = GridRowID + 'AutoCompleteExtender1';
$find(AutoCompleteExt).set_contextKey("1");
}

How to find nested controls within nested user controls

I have a user control (Control1) which has a placeholder that may contain several additional user controls (of the same type - see below) which are added dynamically. How do I navigate the user control hierarchy to find the values of the nested sets of controls when the button located in Control 1 is clicked?
Control 1:
<%# Control Language="C#" AutoEventWireup="True" CodeBehind="Control1.ascx.cs" Inherits="Control1" %>
<%# Reference Control="Control2.ascx" %>
<div id="div1">
<div id="divPh"><asp:PlaceHolder ID="phControls" runat="server" /></div>
<div id="divContinue"><asp:Button ID="btnContinue" Text="Continue" OnClick="submit_Click" runat="server" /></div>
</div>
Code behind for Control1.aspx:
protected void submit_Click(object sender, EventArgs e)
{
// iterate through list of divControl2 controls and find out which radio button is selected
// for example, there may be 3 divControl2's which are added to the placeHolder on Control1, rdoBth1 might be selected on 2 of the controls
// and rdoBtn2 might be selected on 1 - how do I navigate this control structure?
}
Control 2 (Several of these may be added to the placeHolder on Control1):
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="Control2.ascx.cs" Inherits="Control2" %>
<div id="divControl2">
<p><strong><asp:RadioButton ID="rdoBtn1" GroupName="Group1" Checked="true" runat="server" /> Check this</strong></p>
<p><strong><asp:RadioButton ID="rdoBtn2" GroupName="Group1" Checked="false" runat="server" /> No, check this one</strong></p>
</div>
Check below code and let me know if you have any queries.
protected void submit_Click(object sender, EventArgs e)
{
for (int count = 0; count < phControls.Controls.Count; count++)
{
UserControl uc = (UserControl)(phControls.Controls[count]);
if (uc != null)
{
RadioButton rdoBtn1 = new RadioButton();
RadioButton rdoBtn2 = new RadioButton();
rdoBtn1 = (RadioButton)(uc.FindControl("rdoBtn1"));
rdoBtn2 = (RadioButton)(uc.FindControl("rdoBtn2"));
if (rdoBtn1.Checked == true)
{
Response.Write("1st checked ");
}
else if (rdoBtn2.Checked == true)
{
Response.Write("2nd checked");
}
}
}
This isn't the best design in the world, but you can accomplish what you're looking for with some relative ease. The problem is that the page where these controls are will have to have intimate knowledge of the inner workings of the dynamically added controls. And, you're going to want them to implement a common abstract class or interface so that you can look for the right ones by type.
The following code assumes that you've created properties for accessing the internal controls values rather than having to reference the internal controls yourself. This is just good practice when you use any kind of control.
protected void submit_Click(object sender, EventArgs e) {
foreach (var control in phControls.Controls) {
IMySpecialControl mySpecialControl = control as IMySpecialControl;
if (mySpecialControl != null) {
// access some properties (and probably need a cast to the specific control type :(
}
}
}
Instead, why not just access the fields via Request.Form collection instead?
string rdoBtn1Value = Request.Form["rdoBtn1"];

problem binding gridview 's bound columns datafield using the column name of my datatable

I'm binding my gridview's bound columns with datafield using the column name of my datatable. The problem is we have a scenario we need to put in a text where the datafield was int with value 0. I couldn't see any work around. Is there any easy way to do this?
If you don't like to use inline code in your aspx pages as David has suggested make a template with a literal control in it and implement the OnDataBinding event:
For example in your grid have the following template for your field:
<asp:TemplateField HeaderText="Your Header Name">
<ItemTemplate>
<asp:Literal runat="server" ID="litYourCustomField" OnDataBinding="litYourCustumField_DataBinding"></asp:Label>
</ItemTemplate>
</asp:TemplateField>
Then you implement the OnDataBinding in your code behind:
protected void litYourCustomField_DataBinding(object sender, System.EventArgs e)
{
Literal lit = (Literal)(sender);
int yourInt = Convert.ToInt32(Eval("YourNumber"));
lit.Text = (yourInt == 1) ? "It's a 1" : "It's something else";
}
I prefer this method to the inline code since it puts no code in your aspx pages. I usually have a #region defined in my .cs file that has all by databinding code. I am pretty sure performance wise they will be pretty much identical except for maybe the overhead of the literal control if you have the viewstate enable. Make sure to turn off viewstate when you don't need it.
If this is ASP.Net, you can make this a Template column and do the following:
<ItemTemplate>
<%# MyConversionFunction(Convert.ToInt32(DataBinder.Eval(Container.DataItem, "IntegerFieldName"))) %>
</ItemTemplate>
protected string MyConversionFunction(int ValueToCheck)
{
if(ValueToCheck.ToString() == "0")
{
return "SomeText";
}
else
{
return SomeValue.ToString();
}
}

How to set the RowStyle of a GridView row depending on a property of the Object that the row is being bound to

I'm currently using a GridView and I want to set the CssClass for the Row depending on a property of the object that the row is being bound to.
I tried the following but it does not work (see comments):
<asp:GridView id="searchResultsGrid" runat="server" AllowPaging="true" PageSize="20" AutoGenerateColumns="false">
<!-- The following line doesn't work because apparently "Code blocks
aren't allowed in this context: -->
<RowStyle CssClass="<%#IIF(DataBinder.Eval(Container.DataItem,"NeedsAttention","red","") %>
<Columns>
<!--............-->
</Columns>
</asp:GridView>
Now I could simply handle the GridView's RowDataBound event and change the css class of the row there...but I'm trying to keep a clear separation between the UI and the page/business logic layers.
I have no idea how to accomplish this and I'm looking forward to hearing any suggestions.
Thanks,
-Frinny
You cannot do this in declarative markup.
Nearly all of GridView's declarative properties (including GridView.RowStyle) are grid-level settings rather than row-level. Apart from TemplateFields , they are not bound data containers, so they don't have access to the data in their rows.
If you want to keep this logic in the .aspx template, your only real option is to use template fields and manipulate their contents:
<asp:TemplateField>
<ItemTemplate>
<span class="<%# ((string)Eval("property3")) == "NeedsAttention" ? "red" : string.Empty %>">
<%# Eval("property1") %>
</span>
</ItemTemplate>
</asp:TemplateField>
Depending on what you want to do, this may be awkward - you don't have access to the containing <td> (or <tr> for that matter) and you'll have to repeat the formatting for each cell.
The GridView class goes to a lot of lengths to hide the details of HTML and styling from you. After all you could create a GridView control adapter that wouldn't even render as HTML tables. (Unlikely though that may be.)
So even though you're trying to avoid it, you're probably best off dealing with this in a OnRowDataBound handler - or use a Repeater (if that's appropriate).
I know it has been almost a year, but if anyone else is trying this, try to subclass the GridView.
public class GridViewCSSRowBindable : GridView
{
public string DataFieldRowCSSClass { get; set; }
protected override void OnRowDataBound(GridViewRowEventArgs e)
{
base.OnRowDataBound(e);
if (!string.IsNullOrEmpty(DataFieldRowCSSClass))
{
//This will throw an exception if the property does not exist on the data item:
string cssClassString = DataBinder.Eval(e.Row.DataItem, DataFieldRowCSSClass) as string;
if (!string.IsNullOrEmpty(cssClassString))
{
string sep = string.IsNullOrEmpty(e.Row.CssClass) ? string.Empty : " ";
e.Row.CssClass += sep + cssClassString;
}
}
}
}
And then in your Page:
<custom:GridViewCSSRowBindable ID="gvExample" runat="server" DataFieldRowCSSClass="RowCSS">
</custom:GridViewCSSRowBindable>
The objects being bound to this example GridView should have a public string RowCSS property.
If you haven't used inherited controls before, you might have to look up how to set that up in your project.
foreach (TableCell gvc in gvRowPhistry.Cells)
{
gvc.ForeColor = System.Drawing.Color.Blue;
}

Resources