FindControl() return null - asp.net

I trying to create application whad add controlls dynamicaly. I have masterpage, my asp:Content is here:
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<asp:ScriptManager ID="scriptManager1" runat="server">
</asp:ScriptManager>
<div style="margin: 10px">
<asp:UpdatePanel ID="updatePanel1" runat="server">
<ContentTemplate>
<asp:PlaceHolder runat="server" ID="myPlaceHolder" />
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="btnAdd" EventName="Click" />
</Triggers>
</asp:UpdatePanel>
</div>
<asp:Button ID="btnAdd" runat="server" Text="Add" />
After click in btnAdd I want to add two textboxes. I trying do it like in http://jagdeepmankotia.wordpress.com/2010/01/30/dynamically-add-controls-in-asp-net-c/
This is my code:
static int myCount = 1;
private TextBox[] color;
private TextBox[] text;
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
color = new TextBox[myCount];
text = new TextBox[myCount];
for (int i = 0; i < myCount; i++)
{
TextBox tbColor = new TextBox();
tbColor.ID = "colorTextBox" + i.ToString();
myPlaceHolder.Controls.Add(tbColor);
color[i] = tbColor;
TextBox tbText = new TextBox();
tbText.ID = "textTextBox" + i.ToString();
myPlaceHolder.Controls.Add(tbText);
text[i] = tbText;
LiteralControl literalBreak = new LiteralControl("<br />");
myPlaceHolder.Controls.Add(literalBreak);
}
}
public Control GetPostBackControl(Page page)
{
Control control = null;
string ctrlname = page.Request.Params.Get("__EVENTTARGET");
if (ctrlname != null && ctrlname != string.Empty)
{
control = page.FindControl(ctrlname);
}
else
{
foreach (string ctl in page.Request.Form)
{
Control mycontrol = page.FindControl(ctl);
if (mycontrol is System.Web.UI.WebControls.Button)
{
control = mycontrol;
// This gives you ID of which button caused postback
break;
}
}
}
return control;
}
protected void Page_PreInit(object sender, EventArgs e)
{
Control myControl = GetPostBackControl(this.Page);
if (myControl != null)
if (myControl.ClientID.ToString() == "btnAdd")
myCount = myCount + 1;
}
protected void btnAdd_Click(object sender, EventArgs e)
{
//handled in PreInit
}
When in function GetPostBackControl() in loap foreach looking for my btnAdd, for example in first iteration for ctr "ctl00$MainContent$scriptManager1", myControl is null... In next iterations also... So my function always return null. What can be reason?

FindControl only searches direct children of the container. Since you are starting off at the page level, you will need to recurse through the child UpdatePanel control to get to your btnAdd control.
Have a look here for an example how to do do this.
Edit:
I'm not sure I understand why you are 'looking' for your button in this manner, since there is only one static button on the screen - you wouldn't need to use FindControl in this case.
<asp:Button ID="btnAdd" runat="server" Text="Add" onclick="btnAdd_Click" />
(or in code, btnAdd.OnClick += new EventHandler(btnAdd_Click);)
Even if you had multiple Buttons in your form added dynamically, you could wire ALL of them up to the same Button Click handler, in which case sender would then contain the Button Control which was clicked. You would typically use FindControl to scrape the data out of the dynamically added Input controls (text box etc), rather than to see which control caused the Postback (as 'sender' in an appropriate event handler would be easier)
Edit 2:
You can add the buttons dynamically just like your other controls
Button myButton = new Button();
myButton.Text = "Click Me";
myButton.Click += new EventHandler(btnAdd_Click);
myPlaceHolder.Controls.Add(myButton);
If you want all the controls that you've added already to 'stay' in between postbacks then enable viewstate on the page and on the controls, and then make sure that you only add the controls once without postback, in OnInit:
base.OnInit(e);
if (!IsPostBack)
{ // ... Add controls here
You can keep the state of 'mycount' in a hidden field (in the same updatepanel, and with viewstate enabled) - you'll need to parse it to an int each time. Or you can use SessionState to track it.

Related

Radio Button inside grid view control not firing oncheckedchanged event

I have following code in my application I have a gridview control insde my grid view I have radio button defined as templetefield.
<asp:View ID="View3" runat="server">
<script type = "text/javascript">
function RadioCheck(rb) {
var gv = document.getElementById("<%=grdAllPartsRequestList.ClientID%>");
var rbs = gv.getElementsByTagName("input");
var row = rb.parentNode.parentNode;
for (var i = 0; i < rbs.length; i++) {
if (rbs[i].type == "radio") {
if (rbs[i].checked && rbs[i] != rb) {
rbs[i].checked = false;
break;
}
}
}
}
</script>
<asp:TemplateField>
<ItemTemplate>
<asp:RadioButton ID="rbSelected" runat="server" AutoPostBack="True"
oncheckedchanged="rbSelected_CheckedChanged" GroupName="RequestSelection" onclick="RadioCheck(this);" />
<asp:HiddenField ID="HiddenField1" runat="server" />
</ItemTemplate>
</asp:TemplateField>
</<asp:View>
Code Behind .CS
protected void rbSelected_CheckedChanged(object sender, EventArgs e)
{
}
Now my problem is upon running my page when I click the radio button the Event doesn't fire. oncheckedchanged is not firing.
Surprisingly The same functionality works fine in another page with in the grid view.
I have searched the web for this answers. But so far nothing worked out. I have Update Panel in my Master Page.. I have tried to play with UpdateMode="Always" and ChildrenAsTriggers="true" but so far nothing worked out please help!
Try this,
void GridView_RowDataBound(Object sender, GridViewRowEventArgs e)
{
if(e.Row.RowType == DataControlRowType.DataRow)
{
Radiobutton rb = e.Row.FindControl("rbSelected") as Radiobutton;
rb.oncheckedchanged += this.rbSelected_CheckedChanged;
}
}
The event will be added to the radiobutton on rowdatabound event of GridView...
Hope this helps you...

Why do I have to click a button twice in an ASP.NET repeater to get the command to fire?

I have an ASP.NET page with the following 3 main areas:
1 - list of checkboxes on the left for fitlering results
2 - Repeater that displays the matching results in the middle (with a button for each item)
3 - Repeater that displays the selected items on the right
On initial page load the page will show the data bound checkboxes and will show all results (since nothing has been checked in the filters). As the user checks or unchecks the checkboxes, the page will reload and the matching results will change. So far this part works great.
In the Results Repeater, each item has a Button. When the user clicks the button for an item in the Results the idea is that the item will get added to the Selected Repeater on the right. What is happening is that after I check or uncheck filter checkboxes - the first time that I then try and click on the buttons in the Results repeater, nothing happens. The page just reloads. Then if I click the button a second time, the Repeater Command will fire and the item will get added to the Repeater on the right hand side. Then, as long as I don't change any of the checkboxes I can click on one of the command buttons and it will work right away. But if I check one of the checkboxes in the filters area (which causes the Results to get re-bound) then I have to click one of the buttons twice to get it to fire.
I have a sense that this has something to do with ViewState but I have no idea. Does anyone know why this would be happening?
Below is my code for both the ASPX page and the code behind.
ASPX Code:
<h3>Filters</h3>
<asp:Repeater ID="rptTechnologies" runat="server" OnItemDataBound="rptFacet_ItemDataBound">
<HeaderTemplate><h4>Technology</h4></HeaderTemplate>
<ItemTemplate><asp:CheckBox ID="chkFacet" runat="server" AutoPostBack="true" OnCheckedChanged="chkFacet_Changed" /><br /></ItemTemplate>
</asp:Repeater>
<asp:Repeater ID="rptVerticals" runat="server" OnItemDataBound="rptFacet_ItemDataBound">
<HeaderTemplate><h4>Vertical</h4></HeaderTemplate>
<ItemTemplate><asp:CheckBox ID="chkFacet" runat="server" AutoPostBack="true" OnCheckedChanged="chkFacet_Changed" /><br /></ItemTemplate>
</asp:Repeater>
<asp:Repeater ID="rptIndustries" runat="server" OnItemDataBound="rptFacet_ItemDataBound">
<HeaderTemplate><h4>Industry</h4></HeaderTemplate>
<ItemTemplate><asp:CheckBox ID="chkFacet" runat="server" AutoPostBack="true" OnCheckedChanged="chkFacet_Changed" /><br /></ItemTemplate>
</asp:Repeater>
<asp:Repeater ID="rptSolutions" runat="server" OnItemDataBound="rptFacet_ItemDataBound">
<HeaderTemplate><h4>Solution</h4></HeaderTemplate>
<ItemTemplate><asp:CheckBox ID="chkFacet" runat="server" AutoPostBack="true" OnCheckedChanged="chkFacet_Changed" /><br /></ItemTemplate>
</asp:Repeater>
<h3>Results</h3>
<asp:Repeater ID="rptMatchingSlides" runat="server" OnItemDataBound="rptMatchingSlides_ItemDataBound" OnItemCommand="rptMatchingSlides_Command">
<ItemTemplate>
<h4><asp:Literal ID="litName" runat="server"></asp:Literal></h4>
<asp:Button ID="btnSelect" runat="server" Text="Select" CommandName="Select" />
</ItemTemplate>
<SeparatorTemplate><hr /></SeparatorTemplate>
</asp:Repeater>
<h3>Selected</h3>
<asp:Repeater ID="rptSelectedSlides" runat="server" OnItemDataBound="rptSelectedSlides_ItemDataBound">
<ItemTemplate>
<h4><asp:Literal ID="litName" runat="server"></asp:Literal></h4>
</ItemTemplate>
<SeparatorTemplate><hr /></SeparatorTemplate>
</asp:Repeater>
Here is the code behind:
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
this.BindData();
}
}
public List<string> SelectedSlides
{
get
{
if (Session["SelectedIDs"] != null)
{
string[] _ids = Session["SelectedIDs"].ToString().Split(new char[] { '|' });
List<String> _retVal = new List<string>();
foreach (string _id in _ids)
{
_retVal.Add(_id);
}
return _retVal;
}
else
{
return new List<string>();
}
}
set
{
//Set the session value
string _val = "";
foreach (string _id in value)
{
if (_val == "")
{
_val = _id;
}
else
{
_val += "|" + _id;
}
}
Session["SelectedIDs"] = _val;
}
}
protected void BindData()
{
//Filters
rptTechnologies.DataSource = Repository.GetTaxonomyItems();
rptTechnologies.DataBind();
rptVerticals.DataSource = Repository.GetTaxonomyItems();
rptVerticals.DataBind();
rptIndustries.DataSource = Repository.GetTaxonomyItems();
rptIndustries.DataBind();
rptSolutions.DataSource = Repository.GetTaxonomyItems();
rptSolutions.DataBind();
this.BindMatchingSlides();
}
protected void BindMatchingSlides()
{
...build list of ids from checkboxes...
rptMatchingSlides.DataSource = Repository.GetMatchingSlides(_selectedIDs);
rptMatchingSlides.DataBind();
}
protected void BindSelectedSlides()
{
if (this.SelectedSlides.Count > 0)
{
rptSelectedSlides.DataSource = this.SelectedSlides;
rptSelectedSlides.DataBind();
}
else
{
divSelectedSlides.Visible = false;
}
}
protected void rptMatchingSlides_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
Literal _litName = (Literal)e.Item.FindControl("litName");
Button _btnSelect = (Button)e.Item.FindControl("btnSelect");
_litName.Text = ...set name here...
_btnSelect.CommandArgument = ...use unique ID of item from database...
_btnSelect.ID = "btnSelect_" + e.Item.ItemIndex;
}
}
protected void rptMatchingSlides_Command(object sender, RepeaterCommandEventArgs e)
{
if (e.CommandName == "Select")
{
Item _slide = ...get data from database based on Command Argument...
if (_slide != null)
{
List<string> _selectedSlides = this.SelectedSlides;
_selectedSlides.Add(_slide.ID.ToString());
this.SelectedSlides = _selectedSlides;
}
this.BindSelectedSlides();
}
}
Thanks to Jeremy - removing the line of code where I was setting the ID fixed it. Doh! Somewhere else I had read that you needed to set a unique value for the IDs of the buttons in a repeater. So that must have been the culprit. Thanks to Jeremy.

Put focus back on a gridview's selected row after postback

Is it possible to put focus back on a gridview row after that a selection of the row generates a postback?
I'm trying to add an onkeydown handler on the gridview rows in order to use the keyboard for navigation. My problem, I believe, is that after the first postback, the selected cell loses focus, and so the next key stroke is not caught by the cell.
I have the following code
The grid view
<asp:GridView runat="server" ID="gdvPersons" AutoGenerateColumns="false"
onrowcreated="gdvPersons_RowCreated" onselectedindexchanged="gdvPersons_SelectedIndexChanged">
<Columns>
<asp:TemplateField HeaderText="Name">
<ItemTemplate>
<%# ((GridviewFocus.Person) Container.DataItem).Name %>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Age">
<ItemTemplate>
<%# ((GridviewFocus.Person) Container.DataItem).Age %>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
The Code behind
protected void Page_Load(object sender, EventArgs e)
{
var persons = new List<Person> {new Person() {Name = "Fikre", Age = 24},
new Person() {Name = "Mike", Age = 29},
new Person() {Name = "Mark", Age = 35}};
gdvPersons.DataSource = persons;
gdvPersons.DataBind();
}
protected void gdvPersons_RowCreated(object sender, System.Web.UI.WebControls.GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
e.Row.Attributes.Add("onkeydown", ClientScript.GetPostBackEventReference((Control)sender, "Select$" + e.Row.DataItemIndex));
}
protected void gdvPersons_SelectedIndexChanged(object sender, EventArgs e)
{
gdvPersons.SelectedRow.Focus();
}
On your onkeydown script code copy the id of the cell to a hidden input field.
<input type="text" id="gridviewcell_id" onkeydown="lastcell.value = this.id" />
<input type="hidden" id="lastcell" runat="server" />
the example above is plain html, and you would have to add the proper onkeydown code to your gridview.
In your postback (for example onclick) event handler code you can retrieve the id from the hidden fields value property and register javascript to execute once the page is refreshed. If you have a button which is clicked which performs the postback you could do something like this:
protected void MyButton_Click(object sender, EventArgs e)
{
string id = lastcell.Value;
string script = "var ctrl = document.getElementById('" + lastcell.Value + "');";
script += "ctrl.focus();";
ClientScript.RegisterClientScriptBlock(this.GetType(),
"focusScript", script, true);
}
This should make your page execute the following script once it's loaded, and the control should retrive focus:
var ctrl = document.getElementById("yourid");
ctrl.focus();
Add MaintainScrollPositionOnPostBack="true" in the #Page directive in your .aspx file or add it to system.web/pages section in web.config
<system.web>
<pages controlRenderingCompatibilityVersion="3.5" clientIDMode="AutoID" maintainScrollPositionOnPostBack="true"/>
</system.web>
This works also when navigating to previous page with large table e.g.
When Web pages are posted back to the server, the user is returned to the top of the page. On long Web pages, this means that the user has to scroll the page back to the last position on the page.
Documentation

Bit of help with DataPager template & LinkButton please?

I have a datapager with a pagertemplate. In the template I have a "Show All" button, which sets the PageSize of the datapager to show all records. This works fine but I want to be able to hide the button when it's clicked. It's in an UpdatePanel so I don't know if that makes a difference?
<asp:DataPager ID="Pager" runat="server" PagedControlID="rangeList" PageSize="15" EnableViewState="false">
<Fields>
<asp:TemplatePagerField>
<PagerTemplate>
<asp:LinkButton ID="LinkButton1" runat="server" CommandArgument="<%# Container.TotalRowCount.ToString() %>"
oncommand="LinkButton1_Command" >Show All Ranges</asp:LinkButton>
</PagerTemplate>
</asp:TemplatePagerField>
<asp:numericpagerfield ButtonCount="10" NextPageText="..." PreviousPageText="..." CurrentPageLabelCssClass="pageOn" />
</Fields>
</asp:DataPager>
And the codebehind:
protected void LinkButton1_Command(object sender, CommandEventArgs e)
{
this.Pager.PageSize = int.Parse(e.CommandArgument.ToString());
LinkButton lb = (LinkButton)sender;
if (lb != null)
{
lb.Visible = false;
}
rangeList.DataBind();
}
The first click works fine, and refreshes the ListView which in turn adjusts the pager to show one page with all the results on it, but the button doesn't disappear as I want it to.
Any ideas?
If there's nothing to display within the pager, why not hide the Pager control itself:
protected void LinkButton1_Command(object sender, CommandEventArgs e)
{
this.Pager.PageSize = int.Parse(e.CommandArgument.ToString());
this.Pager.Visible = false;
lnkShowPages.Visible = true; // EDIT only
rangeList.DataBind();
}
EDIT:
You could have a second "Show Pages" LinkButton that's initially not visible and becomes visible when the Show All LinkButton is clicked (above). When this new LinkButton is clicked, it could then enable paging by setting the Pager's PageSize and visibility and hiding itself:
protected void lnkShowPages_Command(object sender, CommandEventArgs e)
{
this.Pager.PageSize = int.Parse(e.CommandArgument.ToString());
this.Pager.Visible = true;
lnkShowPages.Visible = false;
rangeList.DataBind();
}

UpdatePanel where Button Event does not fire

I have a UpdatePanel with a PlaceHold contained in it. I create some controls with Labels and Buttons, when Button is clicked it fires an Event that clears PlaceHolder and adds some Textboxes and a Button with an Event.
Problem is when this Button is click it appears to do a PostBack and does not fire Event associated with Button. I thought that since these controls are all contained within the UpdatePanel the would be no PostBack, am I missing the flow.
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
<%--<%# Register assembly="System.Web.DynamicData, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" namespace="System.Web.DynamicData" tagprefix="cc1" %>
--%>
<div id="content" > <!--start content div-->
<asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
<br />
<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>
<br />
<asp:UpdatePanel ID="upBlog" runat="server" UpdateMode="Conditional">
<ContentTemplate >
<asp:PlaceHolder ID="phBlog" runat="server"></asp:PlaceHolder>
</ContentTemplate>
<Triggers >
<%-- <asp:AsyncPostBackTrigger EventName="Click" ControlID = "btnSave" />--%>
</Triggers>
</asp:UpdatePanel>
<br />
</div> <!--ends content div-->
</asp:Content>
//Code Behind
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
FirstView();
}
protected void Page_Load(object sender, EventArgs e)
{
}
private void FirstView()
{
FileStream fs = new FileStream(Server.MapPath(GlobalVar.compathver), FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
DataSet dset = new DataSet();
dset.ReadXml(fs);
fs.Close();
//other controls
Button btnComments = new Button();
btnComments.ID = "mybtnComments" + i.ToString();
btnComments.BorderWidth = 0;
btnComments.Text = MyFunc.CountComments(row["ID"].ToString(), dset) + " Comments";
phBlog.Controls.Add(btnComments);
btnComments.CommandArgument = row["ID"].ToString();
btnComments.BorderWidth = 0;
btnComments.Command += new CommandEventHandler(Button1_Click)
}
private void CommentView(string ID) /// THIS DOES not FIRE
{
DataView myCommentView = GetCommentView(ID);
Button btnCommentSave = new Button();
btnCommentSave.ID = "mySavebtnComments" + i.ToString();
btnCommentSave.Text = "Publish";
btnCommentSave.BackColor = Color.Aquamarine;
phBlog.Controls.Add(btnCommentSave);
btnCommentSave.CommandArgument = row["ID"].ToString();
btnCommentSave.Click += new EventHandler(btnSave_Click);
}
protected void Button1_Click(object sender, CommandEventArgs e)
{
CommentView(e.CommandArgument.ToString());
}
protected void btnSave_Click(object sender, EventArgs e)
//protected void btnSave_Click(object sender, CommandEventArgs e)
{
FileStream fsxml = new FileStream(Server.MapPath(GlobalVar.compathver), FileMode.Truncate, FileAccess.Write, FileShare.ReadWrite);
//other code
// XML Document Saved
xmldoc.Save(fsxml);
}
The UpdatePanel still does a postback. The whole page life-cycle is repeated when the button is clicked, and therefore the Page_Init and Page_Load for example will be executed again. AJAX does not remove the postbox, just hides it from the client.
I can't obviously see something wrong with the code you've posted however I would suggest you place a break point in the Page_Init as a start. This will be able to tell you if the postback is actually happening or not, since this will be fired if the button is indeed trying to fire the button click event. Keep in mind any click events is fired after the page life cycle completes (Theoretically).
On second look I would suggest that you change the Button event in the FirstView() method to a Click rather then a Command and see if this triggers the event.
it is better to add any event to the control before adding the control to another container.
secondly the button with the parameter name "btnCommentSave" will not work. You have to be careful on OnInit, OnLoad events. It is server-client side issue. The page pushed from server to client does not include the "btnCommentSave" button, so that this button stays at the clients page but it does not work.
btnComments works, because it is rendered by server before pushing to client at UpdatePanel's OnLoad function.
What rendered after OnLoad functions, does not work.

Resources