ASP.NET user control: Page_Load fires before property is set - c#

This is driving me crazy.
I have a very simple user control:
public int? ImageId {set; get;}
protected void Page_Load(object sender, EventArgs e)
{
... do something with ImageId...
}
And then I put this control on the page with ListView within UpdatePanel:
<asp:ListView ID="ListViewImages" runat="server" DataSourceID="src">
<LayoutTemplate>
<asp:PlaceHolder ID="itemPlaceholder" runat="server" />
</LayoutTemplate>
<ItemTemplate>
<My:MyControl ImageId='<%# Eval("Id") %>' ID="cipPreview" runat="server" />
</ItemTemplate>
</asp:ListView>
The problem is Page_Load fires BEFORE ASP.NET sets ImageId. With debugger's help I found out that for some reason ImageId in MyControl IS SET, but it happens only after Page_Load has finished processing. What's wrong?

It's probably because data binding on the ListView happens AFTER Page_Load fires, so therefore your property isn't set at that point. You could move your code to PreRender event since it is called after data binding is completed.
More info according to MSDN:
PreRender -- Before this event occurs:
The Page object calls EnsureChildControls for each control and for the page.
Each data bound control whose DataSourceID property is set calls its DataBind method.

Related

CommandName not visible in source code

I have this code
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<asp:DataList ID="DataList1" runat="server" OnItemCommand="DataList1_ItemCommand">
<ItemTemplate>
<asp:LinkButton ID="LinkButton1" runat="server" CommandName="Command1">
<img src="../images/image1.png" alt="" />
</asp:LinkButton>
</ItemTemplate>
</asp:DataList>
</ContentTemplate>
</asp:UpdatePanel>
and this code
protected void DataList1_ItemCommand(object source, DataListCommandEventArgs e)
{
if (e.CommandName == "Command1")
{
// Never drops here
}
}
The event is being triggered.
But when I debug, LinkButton1's CommandName is not visible in source code.
So, the if statement doesn't work.
Any ideas ?
Edit :
I realised that I have an other error my page that belongs this situation.
Then I used GridView rather than DataList and used the GridView's RowCommand Event and fixed this.
The reason is because your attempting to obtain the CommandName from the DataList not the LinkButton. Your code would work if you did the following:
protected void btnSample_Click(object sender, EventArgs e)
{
// Instantiate:
var command = ((LinkButton)sender).CommandName;
// Do additional logic.
}
I believe your intent is to actually do something on the Click Event. If that isn't your case, you would need to FindControl on an event within your DataList, then instantiate the LinkButton to obtain the CommandName. Hopefully this points you in the proper direction.
I would do a sample within the DataList, but without exact implementation and additional information I wouldn't be able to. It would be similar to the above, just allocating the Control within the current control.

Data not bound for ListView on postbacks

I am writing some message panel as a user control. The code looks like this (shortened for clarity)
protected void Page_PreRender(object sender, EventArgs e)
{
BindMessages(MessageType.Error, ErrListView);
}
private void BindMessages(MessageType type, ListView target)
{
List<string> messages = Session.PopMessages(type);
target.DataSource = messages;
target.DataBind();
}
ascx:
<asp:ListView runat="server" ID="ErrListView">
<ItemTemplate><li><%# Container.DataItem %></li></ItemTemplate>
</asp:ListView>
The code gets executed on each request (initial page load and postback) as it should, and the messages come out of the SessionState correctly. However, if the request is a postback, the messages are not actually updated (as if the DataBind() would not happen).
Anyone got a clue whats going on?
The reason I failed here was that my content did not contain an UpdatePanel. Changing my ascx file to include said control resolved my issues.
<asp:UpdatePanel runat="server" ID="StatusUpdatePanel">
<ContentTemplate>
<ul Class="Errors">
<asp:ListView runat="server" ID="ErrListView">
<ItemTemplate><li><%# Container.DataItem %></li></ItemTemplate>
</asp:ListView>
</ul>
</ContentTemplate>
</asp:UpdatePanel>

Telerik RadListView inside asp.net update panel does not refresh

I've been banging my head against the wall for the last couple of days trying to get this scenario to work.
I have an <asp:UpdatePanel> on a page which has an external trigger and contains a Telerik RadListView:
<asp:UpdatePanel runat="server" ID="RationUpdatePanel" UpdateMode="Conditional" ChildrenAsTriggers="true">
<ContentTemplate>
<telerik:RadListView ID="RationListView runat="server"
DataSourceId="RationDataSource"
OnItemCanceling="ItemCancelingHandler"
OnItemEditing="ItemEditingHandler"
OnItemUpdating="ItemUpdateHandler">
<LayoutTemplate>
<table>
<thead>
<tr>...</tr>
</thead>
<tbody>
<tr id="itemPlaceHolder" runat="server"/>
</tbody>
</table>
</LayoutTemplate>
<ItemTemplate>
<tr>
<td>
<asp:LinkButton ID="EditButton" runat="server" CausesValidation="False" CommandName="Edit" Text="Edit" ToolTip="Edit" />
<asp:LinkButton ID="DeleteButton" runat="server" CausesValidation="False" CommandName="Delete" Text="Delete" ToolTip="Delete" />
</td>
<td>...</td>
</tr>
</ItemTemplate>
<EditItemTemplate>
<tr>
<td>
<asp:LinkButton ID="SaveButton" runat="server" CausesValidation="False" CommandName="Save" Text="Save" ToolTip="Save" />
<asp:LinkButton ID="CancelButton" runat="server" CausesValidation="False" CommandName="Cancel" Text="Cancel" ToolTip="Update" />
</td>
<td>...</td>
</tr>
</EditItemTemplate>
</telerik:RadListView>
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="UseRationButton" EventName="Click" />
</Triggers>
</UpdatePanel>
Code behind looks like this:
protected void RationListView_ItemEditing(object sender, RadListViewCommandEventArgs e)
{
((RadListViewDataItem)e.ListViewItem).Edit = true;
}
protected void RationListView_ItemCanceling(object sender, RadListViewCommandEventArgs e)
{
RationListView.ClearEditItems();
((RadListViewDataItem)e.ListViewItem).Edit = false;
}
Clicking on the EditButton or CancelButton produced the expected events and the ObjectDataSource routine to rebind the list is called, yet the update panel is not refreshed. Calling RationUpdatePanel.Update() from the event handlers does not change the situation. The code works properly outside of an update panel: the mode changes and the buttons from the other template are displayed.
In either scenario clicking on the external trigger causes the list to be displayed.
Update: I added a couple of javascript callbacks to the PageManager class in the browser for beginRequest and endRequest. These just display an alert when the event occurs.
The page contains several elements which basically look like this (details omitted):
<div>
<div>
<asp:DropDownList ID="RationDdl"/>
<asp:Button ID="UseRationButton"/>
</div>
<div>
<asp:DropDownList ID="AnimalGroupDdl"/>
<asp:UpdatePanel ID="AnimalWeightUpdatePanel"/>
<ContentTemplate>
<asp:DropDownList ID="AnimalWeightDdl"/>
</ContentTemplate>
</asp:UpdatePanel>
</div>
</div>
<div>
<telerik:RadListBox>
</telerik:RadListBox>
</div>
<div>
<asp:UpdatePanel ID="FeedPreviewUpdatePanel">
<ContentTemplate>
<asp:Repeater>
</asp:Repeater>
</ContentPanel>
</asp:UpdatePanel>
</div>
<div>
<!-- this contains the update panel shown above -->
</div>
The UseFeedRationButton in the first div is an external trigger for the RationUpdatePanel. AnimalGroupDdl is an external trigger for AnimalWeightUpdatePanel. The handler for the SelectedIndexChanged event on AnimalGroupDdl populates AnimalWeightDdl with available weight classes for the
selected group. It has no interaction with RationUpdatePanel or FeedPreviewUpdatePanel.
Here is the markup for UseRationButton:
<asp:Button ID="UseRationButton" runat="server"
Text="Use This Ration"
OnClick="UseRationButton_Click"
CausesValidation="true"
ValidationGroup="UseRation"
UseSubmitBehavior="false">
</asp:Button>
And here is the code behind:
protected void UseRationButton_Click(object sender, EventArgs e)
{
string text = this.RationDdl.SelectedItem.Text;
this.RationNameLabel.Text = text;
this.RationDataSource.SelectParameters["RationId"].DefaultValue = this.RationDdl.SelectedValue;
}
There are two scenarios I have been testing:
1) Choose a ration from RationDdl and click UseRationButton
2) Choose an animal group and then execute scenario 1.
In scenario 1 an async postback is initiated by the client side asp.net code and the button event is handled server side. The RationListView is populated but the client side endRequest code is never executed (the alert is not displayed). Subsequent clicks on the controls in the RadListView trigger an async postback but the state of the list is not changed and the client side endRequest code is not executed.
In scenario 2 an async postback is initiated, the event is handled on the server side and AnimalWeightDdl is populated. The client side endRequest code is executed in this case (the alert IS displayed). Subsequent to this selecting a ration and clicking on the use ration button initiates a postback, the list view is populated and the endRequest code is executed. At his point clicking on buttons in the list view causes an async postback to be initiated, the state of the list is changed and the endRequest code is executed.
My working theory is that something is causing the state of the page request manager class on the client to become fubared, but I can't figure out what might be causing it.
Firstly , If you use telerik rad control , I suggest you to change your asp:button to telerik:RadButton .
Here is example
<telerik:RadButton ID="v" runat="server" Text="Use This Ration"
AutoPostBack="true"
ValidationGroup="UseRation" OnClick="UseRationButton_Click">
</telerik:RadButton>
protected void UseRationButton_Click(object sender, EventArgs e)
{
string text = this.RationDdl.SelectedItem.Text;
this.RationNameLabel.Text = text;
this.RationDataSource.SelectParameters["RationId"].DefaultValue = this.RationDdl.SelectedValue;
}
Note that , you should add AutoPostBack="true" into your radButton .
And this links , MSDN Reference1 and MSDN Reference2 will show details of using update Panel .
The research I did late Friday and Saturday morning provided the answer to this one.
I had some code attached to the Sys.WebForms.PageRequestManager endRequest event which updated a text box which contained a temporary title for a new ration. The code looked like this:
///
/// reregister the change handlers after reloading the animal weight ddl and create a
/// temporary name for the ration based on animal choice and weight.
///
function animalWeightDdlOnLoad() {
var ddl = document.getElementById("<%= AnimalWeightDdl.ClientID %>");
//
// add event handler in a browser agnostic fashion
//
if (ddl.addEventListener) {
ddl.addEventListener("change", animalWeightDdlOnChange);
}
else if (ddl.attachEvent) {
ddl.attachEvent("onchange", animalWeightDdlOnChange);
}
else {
ddl.onchange = animalWeightDdlOnChange;
}
animalWeightDdlOnChange();
}
animalWeightDdlOnChange() referenced the options list of the animal weight ddl:
///
/// create a temporary name for the ration
///
function animalWeightDdlOnChange() {
var ddl1 = document.getElementById("<%= AnimalGroupDdl.ClientID %>");
var ddl2 = document.getElementById("<%= AnimalWeightDdl.ClientID %>");
var text = document.getElementById("<%= RationNameText.ClientID %>");
text.value = ddl1.options[ddl1.selectedIndex].text + ' ' + ddl2.options[ddl2.selectedIndex].text + ' - Copy';
}
However if the list had not yet been populated the reference to options caused a fault which halted endRequest processing. Rather than having this code run on every async request I changed it to be a handler for the change event on the animal group ddl, like this:
function animalGroupDdlOnSelectedChanged() {
//
// add a handler for the endRequest event which will be triggered by
// fetching the contents of the weight ddl.
//
Sys.WebForms.PageRequestManager.getInstance().add_endRequest(animalWeightDdlOnLoad);
}
Then in animalWeightDdlOnLoad() I added a line to remove the handler once the request had
run. This ensured that the change handler for the weight ddl would only run once the list had been populated. Problem solved. But just in case, I added a check on the length of the animal weight ddl's options list.
So even seemingly innocent javascript can mess up your async request handling in the browser.

PostBackTrigger on multiple LinkButtons in an update panel

I have a search form in an updatePanel which retrieves a list of users in a grid in the same UpdatePanel. The name of each user is a commandLink. I want to make the commandLinks as PostBackTriggers.
But when I do it I get an error at the pageLoad time that the controlId does not exist and its true because the grid of users does not render at the load time but through an ajax call.
Any ideas on how can I make the multiple command buttons in a grid retrieved through ajax call as post back triggers?
When adding the items to the grid, within the ItemDataBound event handler, you should register the postback for each specific control (the static identifiers in your HTML declarations are essentially placeholders - not all things repeated in the grid can actually have the same ID). You do this using the ScriptManager.RegisterAsyncPostBackControl method:
The RegisterAsyncPostBackControl method enables you to register Web
server controls as triggers so that they perform an asynchronous
postback instead of a synchronous postback. When the
ChildrenAsTriggers property of an UpdatePanel control is set to true
(which is the default), postback controls inside the UpdatePanel
control are automatically registered as asynchronous postback
controls.
As stated above, using ChildrenAsTriggers is a possibility, too, but this is commonly set to false for more stringent management.
I have found the solution. Here is the code on asp
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<asp:TextBox ID="txtFirstName" runat="server"></asp:TextBox>
<asp:Button ID="btnSearch" runat="server" OnClick="btnSearch_Click" Text="Search" />
<asp:GridView ID="gvSearchResult" runat="server" OnRowCommand="gvSearchResult_RowCommand"
OnRowDataBound="gvSearchResult_RowDataBound">
<Columns>
<asp:TemplateField HeaderText="Name">
<ItemTemplate>
<asp:LinkButton ID="lnkbtnDetail" runat="server" CommandArgument='<%# Bind("CNIC") %>' CommandName="Detail">
<asp:Label ID="lblName" Text='<%# Bind("Employee_Name") %>' runat="server</asp:Label>
</asp:LinkButton>
</ItemTemplate>
<ItemStyle HorizontalAlign="Left" VerticalAlign="Middle"Height="25px"Width="30%" />
</asp:TemplateField>
</Columns>
</asp:GridView>
Ihad to place OnRowDataBound="gvSearchResult_RowDataBound" on gridView and that function looks like below. So I had to register the iterative control in Scriptmanager as PostBackControl in RowDataBound event of GridView.
protected void gvSearchResult_RowDataBound(object sender, GridViewRowEventArgs e)
{
try
{
if ((e.Row.RowType == DataControlRowType.DataRow))
{
LinkButton lnkbtnDetail = (LinkButton)e.Row.FindControl("lnkbtnDetail");
ScriptManager.GetCurrent(this).RegisterPostBackControl(lnkbtnDetail);
}
}
catch (Exception ex)
{
}
}

How do I add a event to an ASP.NET control when the page loads?

ASP.Net web forms, .NET 2.0
I have a dilemma. I have an aspx page that has a custom control:
<def:CustomControl ID="customID" runat="server" />
The custom control can be added multiple times to the ASPX page if certain criteria is met:
<asp:Repeater runat="server" ID="options" OnItemDataBound="options_OnItemDataBound">
<HeaderTemplate>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
</HeaderTemplate>
<ItemTemplate>
<td>
<span>
<asp:Label runat="server" ID="optionName">
</asp:Label>
<asp:DropDownList runat="server" ID="optionValues" CssClass="PartOption" >
</asp:DropDownList>
</span>
</td>
</ItemTemplate>
<FooterTemplate>
</tr>
</table>
</FooterTemplate>
</asp:Repeater>
In the code behind of the aspx page, I wanted to append a event (OnSelectedItemChange) to the dropdown control that isn't actually on the ASPX page (yet). However, when I try to do something like the following in the code behind:
foreach(Control eachControl in customID.Controls) {
if (eachControl.HasControls) {
Control DdControl = eachControl.FindControl("optionValues"); //always null
if (DdControl != null && DdControl is typeOf(DropDownList)) {
DdControl = DdControl as DropDownlist; //I might have the syntax wrong
//here, but you get the point.
//add event to control.
}
}
}
But All that eachControl has is a repeater, and it shows its childcontrol count as 0. So when it get to the FindControl method, it always returns null.
QUESTION
So what I am trying to do is add an event (OnSelectedItemChange) to the dropdownlist control inside the repeater (which is a custom control that gets added to the page if criteria is met). When a selection is made in the drop down, I want it to trigger the Event method.
How can I either:
A) Find a way to add the event if the dropdownlist control exists on the page?
OR
B) A way to be able to call a method in the code behind and pass the selections from all the drop down lists (since the custom control could be added more than once creating multiple drop downs?
NOTE I don't have the code in front of me, so if I have something syntactically incorrect, please overlook. I also know that ID's are unique, so this is also assumed to be a problem with my logic. When it renders these, the ID's might be mangled by the asp name convention which I would assume I have to find a way around.
If you create that event on the custom control, you won't need to check if it exists on the page or not. If it does, it will definately trigger the event. Or am I missing something here?
EDIT:
What you'll have to do is declare a public event on your control, then when the SelectedIndexChanged Event fired, you'd fire that event, and receive it on the page.
So on your control you'd have:
public delegate void MyIndexChangedDelegate(string value);
public event MyIndexChangedDelegate MyEvent;
protected void myDropDown_SelectedIndexChanged(object sender, EventArgs e)
{
MyEvent(myDropDown.SelectedValue); // Or whatever you want to work with.
}
Then on your page, on your control declaration you'd have:
<usc:control1 runat="server" OnMyEvent="EventReceiver" />
And on the code behind:
protected void EventReceiver(string value)
{
// Do what you have to do with the selected value, which is our local variable 'value'
ClientScript.RegisterStartupScript(typeof(Page), "Alert", string.Format("<script language='JavaScript'>alert('User selected value {0} on my DropDown!');</script>"), value);
}
That should work.
Assuming that you can't put this logic in the control itself, what you need to do I think is access that repeater and attach an ItemDataBound event to it. Something like this:
Repeater options = customID.FindControl("options") as Repeater;
if (options != null)
{
options.ItemDataBound += new RepeaterItemEventHandler(OptionsItemDataBound);
}
Then in the ItemDataBound event you would do something like this:
void OptionsItemDataBound(object sender, RepeaterItemEventArgs e)
{
DropDownList optionValues = e.Item.FindControl("optionValues") as DropDownList;
if (optionValues != null)
{
//perform logic you need here
}
}
I don't have time to try that out right this minute, but I suspect that should work.

Categories

Resources