How to find outer control from nested inner control - c#

I have the following nested structure. What I need is to filter a DB Linq query from within CustomControl1 code behind based on the value of the RadioButtonList selection.
x MainPage1
x---- Panel1 (modal popup)
x--------- UpdatePanel (upMailOrStatusAction, on Panel1)
x-------------- RadioButtonList (rblActionLevel, on UpdatePanel)
x-------------- SubForm1 (on Panel1)
x------------------- CustomControl1 (on Subform1)
x------------------------ DropDownList (on CustomControl1)
I am trying something like the following to find the control, but it says "The name 'upMailOrStatusAction' does not exist in the current context.
RadioButtonList rbl = upMailOrStatusAction.FindControl("rblActionLevel") as RadioButtonList;
What is the best way to find the RadioButtonList control? Yes, I am fairly new with this!
Thank you,
Jim in Suwanee, GA
Ok, Here is the Popup aspx:
<asp:Panel ID="pnlAddMailOrStatusAction" runat="server" CssClass="modalPopupLarge" Style="display: none;">
<asp:UpdatePanel ID="upMailOrStatusAction" runat="server" UpdateMode="Conditional" ChildrenAsTriggers="false" RenderMode="Block">
<Triggers>
<asp:AsyncPostBackTrigger ControlID="rblActionType" />
</Triggers>
<ContentTemplate>
<div class="borderDiv">
<table class="borderTable0" cellpadding="0" cellspacing="0" width="100%">
<thead>
<tr align="left">
<th colspan="9">Action Detail</th>
</tr>
</thead>
<tbody>
<tr>
<td colspan="1">
<label class="labelfield">Action Level:</label>
</td>
<td colspan="1" rowspan="1" align="left">
<asp:RadioButtonList ID="rblActionLevel" runat="server" AutoPostBack="True" RepeatDirection="Horizontal"
RepeatLayout="Flow">
<asp:ListItem Selected="True" Value="Base" Text="Base " />
<asp:ListItem Value="Coverage" Text="Coverage" />
</asp:RadioButtonList>
</td>
<td colspan="1" align="left">
<asp:Label ID="lblMSCoverage" runat="server" class="labelfield" Text="Coverage:" />
</td>
<td colspan="1" align="left">
<asp:Label ID="txtMSCoverage" runat="server" />
</td>
<td colspan="5">
</td>
</tr>
<tr>
<td colspan="9">
<hr />
</td>
</tr>
<tr>
<td colspan="9">
<st:MailAddSubform runat="server" ID="mailAddSubform" />
<st:StatusActionAddSubform runat="server" ID="statusActionAddSubform" Visible="false" />
</td>
</tr>
<tr>
<td colspan="9">
<hr />
</td>
</tr>
</tbody>
</table>
</div>
</ContentTemplate>
</asp:UpdatePanel>
</asp:Panel>
And here is the subform aspx:
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="MailAddSubform.ascx.cs"
Inherits="Company.Solutions.Web.Controls.MailAddSubform" %>
Action:
Message:
And here is the custom control aspx:
Filters
And finally, here is the code behind for the custom control. Look for StackOverflow for where I am tring to lookup the radio button list:
using System;
using System.Collections.Generic;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using Company.Solutions.Data;
using Company.Solutions.Data.Model;
using Company.Solutions.Business.ViewModels;
using Company.Solutions.Business.Helpers;
namespace Comapny.Solutions.Web.Controls
{
public partial class StMailActionLookup : System.Web.UI.UserControl
{
protected void Page_Load(object sender, EventArgs e)
{
BindForm();
}
protected void BindForm()
{
IEnumerable actions = GetClaimMailActions(GetFilter());
ddlLookup.DataSource = actions;
ddlLookup.DataTextField = "CodeAndDescription";
ddlLookup.DataValueField = "actionCd";
ddlLookup.DataBind();
}
//protected void ddlLookup_DataBound1(object sender, System.EventArgs e)
//{
// ddlLookup.Items.Insert(0, new ListItem("<Please Choose an Action>", String.Empty));
//}
private MailActionFilters GetFilter()
{
MailActionFilters filters = new MailActionFilters();
if (chkForms.Checked)
filters |= MailActionFilters.Forms;
if (chkRequested.Checked)
filters |= MailActionFilters.RequestedInfo;
if (chkOther.Checked)
filters |= MailActionFilters.Other;
return filters;
}
public IEnumerable GetClaimMailActions(MailActionFilters filter)
{
RelationalDataContext db = RelationalDataContext.Create();
List<Expression<Func<ClaimMailAction, bool>>> predicates = new List<Expression<Func<ClaimMailAction, bool>>>();
const string MAIL_ACTIONS = "0";
const char FORMS = 'F';
const char REQUESTED_INFO = 'R';
const char EXCLUDE = 'X';
//StackOverflow help
//RadioButtonList rbl = (RadioButtonList) Control.Parent.FindControl("rblActionLevel");
if ((!chkForms.Checked && !chkRequested.Checked && !chkOther.Checked) | (chkForms.Checked && chkRequested.Checked && chkOther.Checked))
{
predicates.Add(cma => cma.ActionCd.StartsWith(MAIL_ACTIONS) && (cma.EsolutionsCode == null || cma.EsolutionsCode!= EXCLUDE));
} else {
if((filter & MailActionFilters.Forms) == MailActionFilters.Forms)
predicates.Add(cma => cma.ActionCd.StartsWith(MAIL_ACTIONS) && cma.EsolutionsCode == FORMS);
if((filter & MailActionFilters.RequestedInfo) == MailActionFilters.RequestedInfo)
predicates.Add(cma => cma.ActionCd.StartsWith(MAIL_ACTIONS) && cma.EsolutionsCode == REQUESTED_INFO);
if((filter & MailActionFilters.Other) == MailActionFilters.Other)
predicates.Add(cma => cma.ActionCd.StartsWith(MAIL_ACTIONS) && (cma.EsolutionsCode == null || (cma.EsolutionsCode != EXCLUDE && cma.EsolutionsCode != FORMS && cma.EsolutionsCode != REQUESTED_INFO)));
}
var predicate = PredicateBuilder.Make<ClaimMailAction>();
predicates.ForEach(delegate(Expression<Func<ClaimMailAction, bool>> expr)
{
predicate = predicate.Or(expr);
});
var qry = db.ClaimMailActions.Where(predicate).Select(c => new { c.ActionCd, CodeAndDescription = string.Format("{0} - {1}", c.ActionCd, c.ActionDesc) });
return qry.ToList();
}
}
}
New code list. My co-worker used this for another lookup. Could someone show me how I would do something similar for this lookup? Even if inefficient, if it works so be it.
HtmlForm form;
foreach(var ctl in Page.Controls[0].Controls)
{
if(ctl is HtmlForm)
{
form = ctl as HtmlForm;
ContentPlaceHolder holder = form.FindControl("DefaultContent") as ContentPlaceHolder;
if (holder != null)
{
PlaceHolder paymentControlHolder = holder.FindControl("plcPaymentForm") as PlaceHolder;
if (paymentControlHolder != null)
{
IListener listener;
foreach (var c in paymentControlHolder.Controls)
{
if (c is IListener)
{
listener = c as IListener;
rblPaymentType.SelectedIndexChanged += listener.AddHandler();
}
}
}
}
}
}
Ok, I am trying this, but have not quite figured out yet how to determine the selected value of the radio button:
HtmlForm form;
foreach (var ctl in Page.Controls[0].Controls) {
if (ctl is HtmlForm) {
form = ctl as HtmlForm;
ContentPlaceHolder holder = form.FindControl("DefaultContent") as ContentPlaceHolder;
if (holder != null) {
RadioButtonList rblControlHolder = holder.FindControl("rblActionLevel") as RadioButtonList;
if (rblControlHolder != null) {
}
}
}
}

I'm not sure whether I fully understood what you're trying to achieve. But if you want to find a control on your page, you might want a solution as I posted here.
public static Control FindControlRecursive(Control parent, string controlId)
{
if (controlId == parent.ID)
return parent;
foreach (Control ctrl in parent.Controls)
{
Control tmp = FindControlRecursive(ctrl, controlId);
if (tmp != null)
return tmp;
}
return null;
}
It is a recursive implementation of the standard find control. But choose your parent wisely. If you have a large page and you indicate that to be the search root, then it will traverse all of the controls of the page till the deepest nested control. You could also go the reversed way, basically starting from your control and recursively go up till you reach the page level. Would be another option.
The only issue I found with this recursive find is that you might get problems when having a repeater on your page. You shouldn't traverse the repeater's inner controls. Due to its architecture there are some problems that it will loose it's viewstate otherwise. Once I've time I'll post an update of this recursive method.
Edit:
You get the selected entry of the radio button as follows:
RadioButtonList myInstance = //find my radio button list
string selectedValue = myInstance.SelectedValue;

Use the property Parent to make your way up the control tree.
CustomControl1.Parent.Parent.Parent.FindControl("rblActionLevel");

Piggybacking on Chaos...
RadioButtonList rbl = (RadioButtonList)Control.Parent.FindControl("rblActionLevel")

"An object reference is required for the non-static field, method, or property 'System.Web.UI.Control.Parent.Get' Using the following: RadioButtonList rbl = (RadioButtonList) StMailActionLookup.Parent.FindControl("rblActionLevel");
You reference the object type "StMailActionLookup" when you should reference "mailActionLookup", the ID of the instance of the control.
So that code would look like:
RadioButtonList rbl = (RadioButtonList) mailActionLookup.Parent.FindControl("rblActionLevel");
Not sure if that's the problem with the code you came up with, however, just a small correction.
Also, remember that the UpdatePanel is a templated control and the RadioButtonList and all other controls are rendered inside of its ContentTemplateContainer somewhere in the UpdatePanels's lifecycle (I think it's somewhere around CreateChildControls). It depends on where your BindForm() method is called from, but it could be that the RadioButtonList truly isn't available yet at the time you're trying to find it. Even if its in markup, controls in a template aren't created the same way as other controls in markup are. They're kind of weird beasts.
For a test, try running the find control code in an overridden Render method or something like that. By the Render method you're guaranteed that all controls will be available.
public override Render(HtmlTextWriter writer) {
RadioButtonList rbl = (RadioButtonList)upMailOrStatusAction.FindControl("rblActionLevel");
}
Also, since the upMailOrStatusAction is an UpdatePanel, the code might be
upMailOrStatusAction.ContentTemplateContainer.FindControl("rblActionLevel");

Ok, finally got this working thanks to everybody's help. Thanks JayRu for steering me in the right direction. Here is what I am using (it still needs a bit of work to hook it up):
HtmlForm form;
foreach (var ctl in Page.Controls[0].Controls) {
if (ctl is HtmlForm) {
form = ctl as HtmlForm;
ContentPlaceHolder holder = form.FindControl("DefaultContent") as ContentPlaceHolder;
if (holder != null) {
RadioButtonList rblControlHolder = holder.FindControl("rblActionLevel") as RadioButtonList;
if (rblControlHolder != null) {
if (rblControlHolder.SelectedValue == "Base") {
}
}
}
}
}

Related

Strange conflict between get postback control and RegisterStartupScript

Please consider this scenario:
I have a simple page and I want to log all controls causing postback. I create this simple page. It contains a grid to show some URLs and when user click on an icon a new tab should open:
<form id="form1" runat="server">
<div>
<table style="width: 100%;">
<tr>
<td style="background-color: #b7ffbb; text-align: center;" colspan="2">
<asp:Button ID="Button3" runat="server" Text="Click Me First" Height="55px" OnClick="Button3_Click" />
</td>
</tr>
<tr>
<td style="background-color: #f1d8fe; text-align: center;" colspan="2">
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="false" BackColor="White" OnRowCommand="GridView1_RowCommand">
<Columns>
<asp:BoundField DataField="SiteAddress" HeaderText="Address" />
<asp:TemplateField>
<ItemTemplate>
<asp:ImageButton ID="ImageButton1" ImageUrl="~/download.png" runat="server" CommandArgument='<%# Eval("SiteAddress") %>' CommandName="GoTo" Height="32px" Width="32px" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</td>
</tr>
</table>
</div>
</form>
and code behind:
public partial class WebForm2 : Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Button3_Click(object sender, EventArgs e)
{
List<Address> Addresses = new List<Address>()
{
new Address(){ SiteAddress = "https://google.com" },
new Address(){ SiteAddress = "https://yahoo.com" },
new Address(){ SiteAddress = "https://stackoverflow.com" },
new Address(){ SiteAddress = "https://learn.microsoft.com/}" }
};
GridView1.DataSource = Addresses;
GridView1.DataBind();
}
protected void GridView1_RowCommand(object sender, GridViewCommandEventArgs e)
{
ScriptManager.RegisterStartupScript(this, this.GetType(), "MyScript", "window.open('" + e.CommandArgument.ToString() + "', '_blank')", true);
}
}
class Address
{
public string SiteAddress { get; set; }
}
every thing is fine till here. Now I create a base class for all of my pages and add below codes for finding postback control:
public class MyPageBaseClass : Page
{
protected override void OnInit(EventArgs e)
{
if (!IsPostBack)
{
}
else
{
var ControlId = GetPostBackControlName(); <------
//Log ControlId
}
base.OnInit(e);
}
private string GetPostBackControlName()
{
Control control = null;
string ctrlname = Page.Request.Params["__EVENTTARGET"];
if (ctrlname != null && ctrlname != String.Empty)
{
control = Page.FindControl(ctrlname);
}
else
{
foreach (string ctl in Page.Request.Form)
{
Control c;
if (ctl.EndsWith(".x") || ctl.EndsWith(".y"))
{
string ctrlStr = ctl.Substring(0, ctl.Length - 2);
c = Page.FindControl(ctrlStr);
}
else
{
c = Page.FindControl(ctl);
}
if (c is System.Web.UI.WebControls.Button ||
c is System.Web.UI.WebControls.ImageButton)
{
control = c;
break;
}
}
}
if (control != null)
return control.ID;
else
return string.Empty;
}
}
and change this line:
public partial class WebForm2 : MyPageBaseClass
Now when I click on icons grid view disappears...(STRANGE...) and nothing happened. When I comment specified line then every thing will be fine...(STRANGE...).
In GetPostBackControlName nothings changed to Request but I don't know why this happened. I checked and I see if I haven't RegisterStartupScript in click event every thing is fine. Please help we to solve this problem.
Thanks
when I click on icons grid view disappears...
ASP.Net page class object instances only live long enough to serve one HTTP request, and each HTTP request rebuilds the entire page by default.
Every time you do a postback, you have a new HTTP request and therefore a new page class object instance and a completely new HTML DOM in the browser. Any work you've done for a previous instance of the page class — such as bind a list of addresses to a grid — no longer exists.
You could fix this by also rebuilding your grid code on each postback, but what I'd really do is skip the whole "RegisterStartupScript" mess and instead make the grid links open the window directly, without a postback at all.
The problem is related to OnInit event. I replaced it with OnPreLoad and every things is fine now.
For search engines: OnInit event has conflict with RegisterStartupScript

How do you get values from dynamic controls in a ListView on postback?

I've recently had to go back to dealing with webforms code and have run into an issue trying to update an existing page that shows a list of survey responses.
I have a ListView showing details of people who have answered a survey.
When you click on an icon in the row that row enters edit mode and shows their information (name, email etc) as input boxes.
So far so good, now I need to add the questions and the answers for that survey and that person. Writing them out is easy, but when the postback happens the row goes back to the ItemTemplate and the controls are gone.
I know with dynamic controls you are supposed to create the in Page_Init so webforms can rebind them, but here the controls aren't created until the ListItem ItemEditing event, and the data saved in the ItemUpdating event.
I'm stuck on how I could make the controls in the right place with the right data at the right point in the lifecycle. Right now I'm trying to pull the values from Request.Form but with a CheckboxList it's hard to figure out what has been selected.
Edit: New example that should actually run
<%# Page Language="C#" AutoEventWireup="true" CodeFile="QuestionExample.aspx.cs" Inherits="QuestionExample" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<table>
<asp:ListView runat="server" ID="LV" OnItemEditing="LV_OnItemEditing" OnItemUpdating="LV_OnItemUpdating">
<LayoutTemplate>
<tr>
<td><asp:PlaceHolder runat="server" ID="itemPlaceHolder"></asp:PlaceHolder></td>
</tr>
</LayoutTemplate>
<ItemTemplate>
<asp:LinkButton ID="EditButton" runat="server" CommandName="Edit" Text="Edit"/>
ID: <asp:Label runat="server" ID="lblLeadID" Text="<%# ((Lead)Container.DataItem).LeadID %>" /> Name: <%# ((Lead)Container.DataItem).Name %><br/>
</ItemTemplate>
<EditItemTemplate>
<asp:LinkButton ID="UpdateButton" runat="server" CommandName="Update" Text="Update" />
ID: <asp:Label runat="server" ID="lblLeadID" Text="<%# ((Lead)Container.DataItem).LeadID %>" /> <asp:Label runat="server">Name</asp:Label>
<asp:TextBox runat="server" id="tbName" Text="<%# ((Lead)Container.DataItem).Name %>"></asp:TextBox>
<asp:Panel runat="server" id="pnlQuestions"></asp:Panel>
</EditItemTemplate>
</asp:ListView>
</table>
</form>
</body>
</html>
Then the code behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.UI;
using System.Web.UI.WebControls;
public partial class QuestionExample : Page
{
#region fake data
protected List<Question> Questions = new List<Question>()
{
new Question { QuestionID = 0, QuestionType = QuestionType.Textbox, QuestionText = "TextBox", Options = null },
new Question { QuestionID = 1, QuestionType = QuestionType.DropDownList, QuestionText = "DDL", Options = new List<string> { "A", "B", "C"} },
};
protected List<Lead> Leads = new List<Lead>
{
new Lead { LeadID = 0, Name = "Bob", Answers = new Dictionary<string, string> { { "TextBox", "Hi" }, { "DDL", "B" } } },
new Lead { LeadID = 1, Name = "Fred", Answers = new Dictionary<string, string> { { "TextBox", "Stuff" }, { "DDL", "C" } } },
};
#endregion
protected void Page_Load(object sender, EventArgs e)
{
LV.DataSource = Leads;
LV.DataBind();
}
protected void LV_OnItemEditing(object sender, ListViewEditEventArgs e)
{
LV.EditIndex = e.NewEditIndex;
LV.DataBind();
var leadLabel = (Label)LV.Items[e.NewEditIndex].FindControl("lblLeadID");
var leadID = int.Parse(leadLabel.Text);
var panel = (Panel)LV.Items[e.NewEditIndex].FindControl("pnlQuestions");
var lead = Leads.First(l => l.LeadID == leadID);
foreach (var answer in lead.Answers)
{
var question = Questions.First(q => q.QuestionText == answer.Key);
panel.Controls.Add(CreatQuestionControl(question, lead, true));
}
}
protected Control CreatQuestionControl(Question question, Lead lead, bool setAnswers)
{
Control result = null;
switch (question.QuestionType)
{
case QuestionType.Textbox:
var tb = new TextBox();
if (setAnswers)
{
var answer = lead.Answers[question.QuestionText];
tb.Text = answer;
}
result = tb;
break;
case QuestionType.DropDownList:
var ddl = new DropDownList { DataSource = question.Options };
ddl.DataBind();
if (setAnswers)
{
var answer = lead.Answers[question.QuestionText];
ddl.SelectedValue = answer;
}
result = ddl;
break;
}
return result;
}
protected void LV_OnItemUpdating(object sender, ListViewUpdateEventArgs e)
{
// Get input data here somehow
LV.EditIndex = -1;
}
}
public class Lead
{
public int LeadID { get; set; }
public string Name { get; set; }
public Dictionary<string, string> Answers { get; set; }
}
public class Question
{
public int QuestionID { get; set; }
public string QuestionText { get; set; }
public QuestionType QuestionType { get; set; }
public List<string> Options { get; set; }
}
public enum QuestionType
{
Textbox = 0,
DropDownList = 1,
}
Without seeing your code it's difficult to give a complete answer, but basically you need to recreate the control tree, including the dynamically created controls, on postback.
You can do this, for example, in the Page_Load event handler.
You can store any data you need to recreate the control tree in ViewState so that it is available on PostBack.
I'm not sure what you mean by:
... when the postback happens the row goes back to the ItemTemplate
I would expect EditIndex to stay unchanged after a postback, unless you explicitly modify it, and therefore the edit row to be unchanged, and EditItemTemplate to be used.
If you can post a simplified sample that illustrates your problem I (or someone else) may be able to help further.
UPDATE
Based on the posted code, you need to recreate the dynamic controls in the ItemUpdating event handler and add them to the control tree in the same order as you created them in the ItemEditing event handler. I.e. you probably need to repeat the code:
var questionPannel = lL.Items[e.ItemIndex].FindControl("lead_table") as HtmlTable;
var campaignQuestionsTableRow = questionPannel.FindControl("campaign_questions") as HtmlTableRow;
var questions = GetQuestions();
foreach(var question in questions)
{
// This builds a WebControl for the dynamic question
var control = CreateQuestionControl(question);
campaignQuestionsTableRow.AddControl(control);
}
UPDATE 2
When updating, you need to recreate the dynamic controls on PostBack. Change your Page_Load event handler to something like:
protected void Page_Load(object sender, EventArgs e)
{
LV.DataSource = Leads;
LV.DataBind();
if (IsPostBack)
{
if (LV.EditIndex >= 0)
{
var leadLabel = (Label)LV.Items[LV.EditIndex].FindControl("lblLeadID");
var leadID = int.Parse(leadLabel.Text);
var panel = (Panel)LV.Items[LV.EditIndex].FindControl("pnlQuestions");
var lead = Leads.First(l => l.LeadID == leadID);
foreach (var answer in lead.Answers)
{
var question = Questions.First(q => q.QuestionText == answer.Key);
panel.Controls.Add(CreatQuestionControl(question, lead, true));
}
}
}
}
On the first Postback (click on Edit), EditIndex will be -1, and the dynamic controls are created in the OnItemEditing handler.
On the second Postback (click on Update), EditIndex will be the index of the line you're editing, and you need to recreate the dynamic controls in Page_Load. If you do this, you'll find your posted values when you get to the OnItemUpdating event handler.
While I think it's worthwhile understanding how to use dynamic controls in this way, in a production app I'd probably use a Repeater inside the EditItemTemplate as suggested by Elisheva Wasserman. The Repeater could contain a UserControl that encapsulates the logic to hide/show UI elements based on the question type.
You should use an inside Repeater or listView, which will be bind on item_edit event, instead of recreating dynamics controls every post.
this way:
<asp:ListView ID="lL" runat="server" OnItemEditing="lL_ItemEditing" OnItemUpdating="lL_ItemUpdating">
<itemtemplate>
Read only view here...
</itemtemplate>
<edititemtemplate>
<asp:LinkButton D="UpdateButton" runat="server" CommandName="Update" />
<table runat="server" ID="lead_table" class="lead_table">
<tr id="Tr1" style="background-color:#EEEEEE;" runat="server" >
<td>Name: <asp:TextBox ID="Name" /></td>
Other Fixed controls...
</tr>
<tr runat="server" ID ="campaign_questions">
<asp:Repeater id='repetur1' runat="server">
<ItemTemplate>
<asp:Label id='lblCaption' runat='server" Text='<%Eval("QuestionTitle")%>' />
<asp:TextBox id='name' runat="server" />
</ItemTemplate>
</asp:Repeater>
</tr>
</table>
on code behind
protected void lL_ItemEditing(object sender, ListViewEditEventArgs e)
{
var questionPannel = lL.Items[e.ItemIndex].FindControl("lead_table") as HtmlTable;
var campaignQuestionsTableRow = questionPannel.FindControl("campaign_questions") as HtmlTableRow;
// bind fixed controls
var lead = GetLead();
var nameControl = ((TextBox)lL.Items[e.ItemIndex].FindControl("Name"))
nameControl.Text = lead.Name;
// bind dynamic controls
var questions = GetQuestions();
var rep= (Repeater)lL.Items[e.ItemIndex].FindControl("repeatur1");
rep.DataSource=questions;
rep.DataBind();
}
protected void lL_ItemUpdating(object sender, ListViewUpdateEventArgs e)
{
// Get fixed fields
// var lead = GetLead();
lead.Name = ((TextBox)lL.Items[e.ItemIndex].FindControl("Name")).Text.Trim();
Repeater rep=(Repeater)e.FindControl("repeatur1"));
foreach (RepeaterItem item in rep.Items)
{
TextBox t=item.FindControl("txtName") as TextBox;
//do your work here
}
// Switch out of edit mode
lL.EditIndex = -1;
}

ASP.NET find control by id

I am doing a simple web site using asp.net and i am having troubles finding my or objects by id in the code behind in c#. I have something like this:
<asp:ListView ID="InternamentosListView" runat="server"
DataSourceID="InternamentosBD">
<LayoutTemplate>
<table id="camas">
<asp:PlaceHolder runat="server" ID="TablePlaceHolder"></asp:PlaceHolder>
</table>
</LayoutTemplate>
the rest is irrelevant, and then i use this in the code behind:
Table table = (Table)FindControl("camas");
i also tried:
Table table = (Table)camas;
and
Control table= (Table)FindControl("camas");
and each one of this lines gives me Null. am i doing something wrong ?
EDIT: From your answers i did this:
<LayoutTemplate>
<table id="camas" runat="server">
</table>
</LayoutTemplate>
and tried all the things stated above. same result.
EDIT2: Whole Code:
C#
protected void Page_Load(object sender, EventArgs e)
{
Table table = (Table)FindControl("camas");
HiddenField NCamasHF = (HiddenField)FindControl("NCamas");
int NCamas = Convert.ToInt32(NCamasHF);
HiddenField NColunasHF = (HiddenField)FindControl("NColunas");
HiddenField CorMasc = (HiddenField)FindControl("CorMasc");
HiddenField CorFem = (HiddenField)FindControl("CorFem");
int NColunas = Convert.ToInt32(NColunasHF);
TableRow Firstrow = new TableRow();
table.Rows.Add(Firstrow);
for (int i = 1; i <= NCamas; i++)
{
if (i % NColunas == 0)
{
TableRow row = new TableRow();
table.Rows.Add(row);
TableCell cell = new TableCell();
cell.BackColor = System.Drawing.Color.LightGreen;
cell.CssClass = "celula";
row.Cells.Add(cell);
}
else
{
TableCell cell = new TableCell();
cell.BackColor = System.Drawing.Color.LightGreen;
cell.CssClass = "celula";
Firstrow.Cells.Add(cell);
}
}
}
asp.net
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
<asp:SqlDataSource
ID="ParametrosBD"
ConnectionString ="<%$ ConnectionStrings:principal %>"
ProviderName = "System.Data.SqlClient"
SelectCommand = "SELECT par_Id, par_NCamas, par_NColunas, par_CorMasculino, par_CorFeminino FROM Parametros WHERE par_Id=1"
runat="server">
</asp:SqlDataSource>
<asp:ListView ID="InternamentosListView" runat="server"
DataSourceID="InternamentosBD">
<LayoutTemplate>
<table id="camas" runat="server">
</table>
</LayoutTemplate>
<ItemTemplate>
<asp:HiddenField ID="NCamas" runat="server" Value='<%# Bind("par_NCamas") %>' />
<asp:HiddenField ID="NColunas" runat="server" Value='<%# Bind("par_NColunas") %>' />
<asp:HiddenField ID="CorMasc" runat="server" Value='<%# Bind("par_CorMasculino") %>' />
<asp:HiddenField ID="CorFem" runat="server" Value='<%# Bind("par_CorFeminino") %>' />
<tr id="cama"></tr>
</ItemTemplate>
</asp:ListView>
</asp:Content>
To expand on #Yura Zaletskyy's recursive find control method from MSDN -- you can also use the Page itself as the containing control to begin your recursive search for that elusive table control (which can be maddening, I know, I've been there.) Here is a bit more direction which may help.
using System;
using System.Collections.Specialized;
using System.Web;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI;
public partial class Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
Table table = (Table)FindControlRecursive(Page, "camas");
if (table != null)
{
//Do awesome things.
}
}
private Control FindControlRecursive(Control rootControl, string controlID)
{
if (rootControl.ID == controlID) return rootControl;
foreach (Control controlToSearch in rootControl.Controls)
{
Control controlToReturn = FindControlRecursive(controlToSearch, controlID);
if (controlToReturn != null) return controlToReturn;
}
return null;
}
}
As you already added runat="server" is step 1. The second step is to find control by id, but you need to do it recursively. For example consider following function:
private Control FindControlRecursive(Control rootControl, string controlID)
{
if (rootControl.ID == controlID) return rootControl;
foreach (Control controlToSearch in rootControl.Controls)
{
Control controlToReturn =
FindControlRecursive(controlToSearch, controlID);
if (controlToReturn != null) return controlToReturn;
}
return null;
}
None of the existing answers mention the obvious built-in solution, FindControl.
Detailed here, FindControl is a function that takes as a string the id of a control that is a child of the calling Control, returning it if it exists or null if it doesn't.
The cool thing is that you can always call it from the Page control which will look through all controls in the page. See below for an example.
var myTextboxControl = (TextBox)Page.FindControl("myTextboxControlID);
if (myTextboxControl != null) {
myTextboxControl.Text = "Something";
}
Note that the returned object is a generic Control, so you'll need to cast it before you can use specific features like .Text above.
You need to have runat="server" for you to be able to see it in the codebehind.
e.g.
<table id="camas" runat="server">
Ok. You have the table within a list view template. You will never find the control directly on server side.
What you need to do is find control within the items of list.Items array or within the item data bound event.
Take a look at this one- how to find control in ItemTemplate of the ListView from code behind of the usercontrol page?
When you're using a master page, then this code should work for you
Note that you're looking for htmlTable and not asp:Table.
// add the namespace
using System.Web.UI.HtmlControls;
// change the name of the place holder to the one you're using
HtmlTable tbl = this.Master.FindControl("ContentPlaceHolder1").FindControl("camas") as HtmlTable;
Best,
Benny
You need to add runat server like previously stated, but the ID will change based on ListView row. You can add clientidmode="Static" to your table if you expect only 1 table and find using InternamentosListView.FindControl("camas"); otherwise, you will need to add ItemDataBound event.
<asp:ListView ID="InternamentosListView" runat="server" DataSourceID="InternamentosBD" OnItemDataBound="InternamentosListView_ItemDataBound">
<LayoutTemplate>
<table id="camas">
<asp:PlaceHolder runat="server" ID="TablePlaceHolder"></asp:PlaceHolder>
</table>
</LayoutTemplate>
You will need to introduce in code behind
InternamentosListView_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
Table tdcamas = (Table)e.Item.FindControl("camas");
}
}

Passing Value from UserControl to ParentPage control not working properly

I have create a photo gallery user control that show photographer in a fancy box.
I also use this control on several page to show page related photos. In hide photogallery user control if their are no photos for perticular page. For this i pass public event
public event SendMessageToThePageHandler showGallery;
to pass a boolean value to hide and show usercontrol on the page.
Page runs without any errors and show the gallery. But problem is with the Pager which doesn't work the way i am coded it. It generate NullReferenceException when i click on the page 2 or the gallery
I would appreeciate a more professional approach to this scenario in which i can pass passvalue to parent page correctly to hide/show correctly.
I am also using UpdatePanel in my usercontrol. Below is the sample code from Parent Page & User Control page
PageOne.aspx
<uc1:PhotoGallery ID="ucPhotoGallery" runat="server" />
CODE BEHIND
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
try
{
int _LangID = int.Parse(Helper.GetAppSettingValue("EnglishLanguageID"));
if (string.IsNullOrEmpty(Request["PID"]))
{
_LID = int.Parse(HttpContext.Current.Request.RequestContext.RouteData.Values["LID"].ToString());
_PID = int.Parse(HttpContext.Current.Request.RequestContext.RouteData.Values["PID"].ToString());
getPageDetails(_PID);
getPageSubMenus(_PID);
// WriteThisMessageToThePage1.sendMessageToThePage += delegate(string message)
ucPhotoGallery.showGallery += delegate(bool showControl)
{
bool bShowControl = showControl;
ucPhotoGallery.Visible = bShowControl;
};
}
else
{
Response.Write("Page not be found, Wrong URL");
}
}
catch (Exception ex)
{
}
}
}
Photo-Gallery.ascx
<%# Control Language="C#" AutoEventWireup="true" CodeFile="Photo-Gallery.ascx.cs" Inherits="Photo_Gallery" %>
<%# Register Src="~/en/UserControls/PagerControl.ascx" TagName="PagerControl" TagPrefix="uc1" %>
<div class="uc-gallery-wrapper">
<div class="page-title-side-wrapper"><h5><asp:Label ID="lblPageTitleSide" CssClass="lbl-page-title-side" runat="server" Text=" Gallery"></asp:Label></h5></div>
<asp:UpdatePanel ID="updPnlAlbums" runat="server" UpdateMode="Conditional">
<ContentTemplate>
<div class="rptAlbums-wrapper">
<asp:Repeater ID="rptAlbums" runat="server" >
<ItemTemplate>
<div class="uc-album-wrapper">
<div class="uc-album-icon"><asp:Image ID="imgAlbumIcon" CssClass="uc-album-img" ImageUrl='<%# getImage(Eval("AlbumIcon")) %>' AlternateText='<%# getTitle(Eval("AlbumName")) %>' runat="server" /></div>
<div class="uc-album-name">
<asp:HyperLink ID="hylnkAlbum" CssClass="fancybox-iframe" runat="server" NavigateUrl='<%# getAID(Eval("AlbumID")) %>'>
<asp:Label ID="lblAlbumName" runat="server" Text='<%# getTitle(Eval("AlbumName")) %>'></asp:Label>
</asp:HyperLink>
</div>
</div>
</ItemTemplate>
</asp:Repeater>
</div>
<div class="uc-gallery-pager-wrapper-o">
<div class="uc-pager-wrapper-i">
<uc1:PagerControl ID="PagerControl1" runat="server" CssClass="gold-pager" PageMode="LinkButton" />
<asp:UpdateProgress ID="UpdateProgress1" runat="server" AssociatedUpdatePanelID="updPnlAlbums" Visible="False">
<ProgressTemplate>
<div id="imgLoadingNewsList" class="uc-PagerLoading">
<asp:Image ID="imgLoading" runat="server" ImageUrl="~/Images/preloader-160x15.gif" Visible="false" AlternateText="loading" />
</div>
</ProgressTemplate>
</asp:UpdateProgress>
</div>
</div>
</ContentTemplate>
</asp:UpdatePanel>
<!-- UpdatePanel -->
</div>
CODE BEHIND
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Threading;
using System.Globalization;
using System.Data;
using CMS.SqlHelper;
using CMS.DataAccessLayer;
using System.Text;
public delegate void SendMessageToThePageHandler(bool showGallery);
public partial class Photo_Gallery : System.Web.UI.UserControl
{
int _PageID = 0;
public event SendMessageToThePageHandler showGallery;
protected void Page_Load(object sender, EventArgs e)
{
PagerControl1.PagerControl_PageIndexChanged += new EventHandler(PagerControl1_PagerControl_PageIndexChanged);
Page.MaintainScrollPositionOnPostBack = false;
if (!IsPostBack)
{
if (string.IsNullOrEmpty(Request["PID"]))
{
_PageID = int.Parse(HttpContext.Current.Request.RequestContext.RouteData.Values["PID"].ToString());
//_PageID = 3;
getAlbumByPageID(3);
}
else
{
Response.Write("Page not be found, Wrong URL");
}
}
}
public void getAlbumByPageID(int _PageID)
{
PagerControl1.PageSize = 2;
PagerControl1.DisplayEntriesCount = 5;
//Will show 2 links after ...
PagerControl1.EdgeEntriesCount = 0;
DataSet ds = DataProvider.GetAlbumByPageID(_PageID);
DataView dv = ds.Tables[0].DefaultView;
if (ds.Tables[0].Rows.Count > 0)
{
showGallery(true);
}
else
{
showGallery(false);
}
//pass the datatable and control to bind
PagerControl1.BindDataWithPaging(rptAlbums, dv.Table);
}
void PagerControl1_PagerControl_PageIndexChanged(object sender, EventArgs e)
{
_PageID = int.Parse(HttpContext.Current.Request.RequestContext.RouteData.Values["PID"].ToString());
getAlbumByPageID(_PageID);
}
}
WORK AROUND
I found a work around by wrapping my users control in a panel and hide/show panel if i have photos for that page or not. This is working fine but i still want to fix problem i have or even a better way of doing it.
<asp:Panel ID="pnl_uc_Gallery" runat="server" Visible="false">
// I have PUT all the USER CONTROL CODE In SIDE THIS BLOAK
</asp:Panel>
if (ds.Tables[0].Rows.Count > 0)
{
// showGallery(true);
pnl_uc_Gallery.Visible = true;
}
else
{
//showGallery(false);
pnl_uc_Gallery.Visible = false;
}
This code:
ucPhotoGallery.showGallery += delegate(bool showControl)
{
bool bShowControl = showControl;
ucPhotoGallery.Visible = bShowControl;
};
is in a !Page.IsPostback call; move it outside so that you are attaching to the event on every postback, not just the first one, or use a method as the event handler and set the method name in the markup. Either way, the issue is the event handler is established only on the first page load, not subsequent loads, and it needs to be done every time a request hits the server.

How to dynamically display loop items in tables?

The following is the design.
<table>
<tr>
<td>Project Title</td>
<td>Download Link</td>
</tr>
<tr>
<td><asp:Label ID="dlLbl" runat="server"></asp:Label></td>
<td><asp:Label ID="dlLink" runat="server"></asp:Label></td>
</tr>
</table>
And the following is the backend codes.
foreach (SPListItem objInnovationListItem in objInnovationList.Items)
{
if (Convert.ToString(objInnovationListItem["Innovation Approval Status"])== status)
{
countStatus++;
//Displays name of the document and download link
dlLbl.Text = objInnovationListItem["Project Title"].ToString();
dlLink.Text = "<a href='/RIDepartment/Innovation%20Submission/" + objInnovationListItem.File.Name + "'>Download</a><br>";
}
}
Hence, my question is, what can I do to allow the tables to dynamically accommodate the document and dl link when there's more than 1 in the loop?
Appreciate some code samples.
With your code style (manual creating html without web-controls) i recommend you to look on ASP.NET MVC side. But i can answer to your question:
First - you need to use asp:Repeater like this:
<table>
<tr>
<td>Project Title</td>
<td>Download Link</td>
</tr>
<asp:Repeater ID="repLinks" runat="server"
onitemdatabound="repLinks_ItemDataBound">
<ItemTemplate>
<tr>
<td>
<asp:Label ID="lblProject" runat="server" Text="Label"></asp:Label>
</td>
<td>
<asp:HyperLink ID="hlLink" runat="server">HyperLink</asp:HyperLink>
</td>
</tr>
</ItemTemplate>
</asp:Repeater>
</table>
second: you need to initialize your collection, that you want to display. For example: you want to display a collection of objInnovationListItem class:
public class objInnovationListItem
{
public string Name { get; set; }
public string Title { get; set; }
public override string ToString()
{
return Title;
}
}
you need do next:
// list - it's source List<objInnovationListItem>
var bindList = list.Where(p => objInnovationListItem["Innovation Approval Status"] == status); // filter your collection - replace you foreach and if statement
repLinks.DataSource = bindList; // set to repeater your displayed collection
repLinks.DataBind(); // bind your collection
and last - you need to indicate in your Repeater ItemTemplate how to display your objInnovationListItem instance - subscribe to event of your Repeater ItemDataBound:
protected void repLinks_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
var item = e.Item.DataItem as objInnovationListItem;
((Label) e.Item.FindControl("lblProject")).Text = item.Name;
((HyperLink) e.Item.FindControl("hlLink")).NavigateUrl = string.Format("/downloaduri?id={0}", item.Title);
}
Result will look like that:
I would use a repeater... Something like this (code might not be exact):
<table>
<tr>
<td>Project Title</td>
<td>Download Link</td>
</tr>
<asp:Repeater id="rptItems" runat="server">
<ItemTemplate>
<tr>
<td><asp:Label ID="dlLbl" runat="server"></asp:Label></td>
<td>Download<br></td>
</tr>
</ItemTemplate>
</asp:Repeater>
</table>
and then in the ItemDataBound event of the repeater, do something like this:
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem) {
((Label)e.Item.FindControl("dlLbl")).Text= ((SPListItem)e.Item.DataItem)["Project Title"].ToString();
}
Why don't you skip the server side controls, and just write the actual html?
Include this div in your aspx file:
<div runat="server" id="divTable"></div>
And put this in your Page_Load():
StringBuilder sb = new StringBuilder();
sb.Append("<table><tr><td>Project Title</td><td>Download Link</td></tr>");
for (int i = 0; i < 10; i++)
{
sb.AppendFormat("<tr><td>{0}</td><td><a href='{1}'>{1}</a></td></tr>", "Title", "Link");
}
sb.Append("</table>");
divTable.InnerHtml = sb.ToString();
You'll of course need to replace "Title" and "Link" with the appropriate values.
Your other options is to actually create new labels and links, but ASP.net is notoriously difficult to work with when you create your server side controls dynamically.

Categories

Resources