How do I raise an event from a user control that was created dynamically?
Here's the code that I'm trying where Bind is a public EventHandler
protected indDemographics IndDemographics;
protected UserControl uc;
override protected void OnInit(EventArgs e)
{
uc = (UserControl)LoadControl("indDemographics.ascx");
IndDemographics.Bind += new EventHandler(test_handler);
base.OnInit(e);
}
I get a null object for IndDemographics. Can anyone point me to a complete code sample?
Thanks in advance...
First off, you'll need to make sure that you have the event defined in your usercontrol's code.
for example:
public class MyUserControl
Inherits UserControl
Public Event Bind(sender as object, e as EventArgs)
public sub SomeFunction()
RaiseEvent Bind(me, new EventArgS())
End Sub
End Class
After this, then you can bind to the Event. Now,for your other issue, are you loading this control dynamically or is it declared on your ASPX side? If it's on your ASPX side, then you don't need the LoadControl, as declaring an object as Runat=Server on the ASPX side instantiates an instance of said class.
If not, then you'll need to make sure you're using the Virtual Path for the location of the ASCX file. (in your example, you'd use "~/indDemographics.ascx" if the ASCX was at the root of the website). At this point you'd need to add it to the page (or a placeholder or some other container object).
Regardless, of which way you instantiate an instance of the UserControl, you then associate the Event Handler to the Event of the instance of the class. For example:
Dim btn As New Button;
AddHandler btn.Click, AddressOf MyButtonClickEventHandler
Now, for the reason that you're getting a NULL reference in the example code.
When you use the LoadControl reference, then the instance of your object is in the UC variable. In the example, you declare two objects, UC as a type of UserControl and indDemographics as a type of indDemographics.
When you use the LoadControl, you're instantiating an instance of indDemographics and assigning it to UC. When you try to assign the event handler to the IndDemographics variable, it has never actually been instantiated.
Ultimately, your code should look more along these lines:
protected indDemographics IndDemographics;
override protected void OnInit(EventArgs e)
{
indDemographics = LoadControl("~/indDemographics.ascx");
IndDemographics.Bind += new EventHandler(test_handler);
base.OnInit(e);
}
I see it (IndDemographics) declared but never actually created, so I'd expect it to be null with just this code.
Thanks to Stephen for getting me on the right track. Here's the final working code in C#:
protected indDemographics IndDemo;
override protected void OnInit(EventArgs e)
{
Control c = LoadControl("~/indDemographics.ascx");
IndDemo = (indDemographics) c;
IndDemo.Bind += new EventHandler(test_handler);
place1.Controls.Add(IndDemo);
base.OnInit(e);
}
It's important to cast the generic control into the indDemographics class. After that everything else works fine.
Related
I'm having a problem with a Web Control that is dynamically created and inserted in my page. I create a couple of LinkButtons, depending on the data of the search that was made, and I'm trying to add an Event Handler to each of the Buttons, so it would filter the result.
The controls are initialized properly, but the event is never fired.
Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
Controls.Clear()
Dim btn As Controls.LocalizableLinkButton
For Each element As Generic.KeyValuePair(Of String, ResultFilterData) In m_list
btn = New LocalizableLinkButton
btn.ID = m_Name & "$lnk" & count
btn.Label = element.Value.Label.Append(" (" + CStr(element.Value.Count) + ")")
btn.CommandArgument = element.Value.Key
AddHandler btn.Click, AddressOf Me.btn_Click
Controls.Add(btn)
Next
End Sub
Since this code is in Page_Init all the controls should be recreated on a postback. (The LocalizableLinkButton is just an extension of a LinkButton to add multilingual features to the text).
The problem is that the method btn_Click is never called. The Link Buttons are properly initialized on the callback, with the same ID's as before. But the event doesn't fire.
I'm using ASP.Net 2.0
Any ideas?
I finally figured out the problem ASP.NET had with my Link Buttons.
The error was in using a '$' sign in my ID for each LinkButton. ASP.NET apparently uses the $ sign to build the control hierarchy when it creates the Postback Javascript. Therefore it thinks that the LinkButtons are nested within a control that does not exist. And so the events aren't fired of course.
Once I removed the $ signs it worked properly.
You probably want to put this piece of code in the Page_Load and see. It's generally advised not to access controls in this Page_Init as there is no guarantee of the controls been created at this stage.
I'm no VB guy but i put this into the codebehind of the default.aspx and it works fine.
protected void Page_Load(object sender, EventArgs e)
{
Button button = new Button();
button.Click += new EventHandler(button_Click);
button.Text = "test";
Form.Controls.Add(button);
}
void button_Click(object sender, EventArgs e)
{
throw new NotImplementedException();
}
Trying to access a property from the parent page on my user control.
Here's the start of my default.asp codebehind:
Partial Class _Default
Inherits System.Web.UI.Page
Private _selectedID As String = "74251BK3232"
Public Property SelectedID() As String
Get
Return _selectedID
End Get
Set(ByVal value As String)
_selectedID = value
End Set
End Property
Here's the start of my user control codebehind:
Partial Class ctrlAddAttribute
Inherits System.Web.UI.UserControl
Dim selectedID As String = Me.Parent.Page.selectedID()
I'm getting the error "selectedID is not a member of System.Web.UI.Page"
Please adivse!
You could access the property when you cast the Page to your actual implementaion called _Default.
Dim selectedID As String = DirectCast(Me.Page,_Default).selectedID()
But that is not the purpose of a Usercontrol(reusability).
Normally you would give the ID from the Controller(page) to the UserControl.
So define a property in the UserControl and set it from the page.
On this way the UserControl would still work in other pages.
Because the user control doesn't belong to the page, you can't access it directly unless you either explicitly set a property in the usercontrol from the including page or create a recursive function that cycles through all of the parent objects until it finds an object of type System.Web.UI.Page.
For the first, you could use a property (I've done this using a property called ParentForm) in the user control:
Private _parentForm as System.Web.UI.Page
Public Property ParentForm() As System.Web.UI.Page ' or the type of your page baseclass
Get
Return _parentForm
End Get
Set
_parentForm = value
End Set
End Property
In the "Parent" page, you would set this property in an event as early as possible. I prefer to use PreLoad because it comes before the load (thus is available when most other controls would need it) and after the init.
Protected Sub Page_PreLoad(ByVal sender as Object, ByVal e as EventArgs) Handles Me.PreLoad
Me.myUserControlID.ParentForm = Me
End Sub
You could also write a function trolls through the parent controls to find the page. Following code is untested so it may require tweaking, but the idea is sound.
Public Shared Function FindParentPage(ByRef ctrl As Object) As Page
If "System.Web.UI.Page".Equals(ctrl.GetType().FullName, StringComparison.OrdinalIgnoreCase) Then
Return ctrl
Else
Return FindParentPage(ctrl.Parent)
End If
End Function
EDIT: You also can't access this property directly because it does not exist within the type System.Web.UI.Page. As #Tim Schmelter recommends, you can either attempt to cast the page to the specific page type _Default or if this is something common to many of your pages, you may need to create a base page class and include your property in that class. Then you can inherit this class instead of System.Web.UI.Page
Public Class MyBasePage
Inherits System.Web.UI.Page
Public Property SelectedID() as Integer
...
End Property
End Class
Then in the page:
Partial Class _Default
Inherits MyBasePage
...
End Class
I have the following BasePage class...
Public Class BasePage
Inherits System.Web.UI.Page
Private litError As Literal
Protected SO As Session
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
SO = Session.Item("SO")
If SO Is Nothing Then
Session.Abandon()
Response.Redirect("~/timeout.htm")
End If
litError = Page.FindControl("litError")
If litError IsNot Nothing Then
litError.Visible = False
End If
End Sub
Protected Sub ShowMessage(ByVal Message As String)
Show(Message, "message")
End Sub
Protected Sub ShowError(ByVal Message As String)
Show(Message, "error message")
End Sub
Protected Sub ShowSuccess(ByVal Message As String)
Show(Message, "success message")
End Sub
Private Sub Show(ByVal Message As String, ByVal CssClass As String)
If litError IsNot Nothing Then
litError.Text = String.Format("<span class=""{0}"">{1}</span>", CssClass, HttpUtility.HtmlEncode(Message))
litError.Visible = True
End If
End Sub
End Class
Every page in this application inherits this class. The SO variable represents a custom session class, that is very simple and just holds a couple of basic settings to be used throughout the application. The problem is, my Page_Load in this base class does not fire if a natural postback occurs (in this case, it is a gridview postback by sorting/paging). Then later in my code when I reference SO, I get a null reference exception because it hasn't been pulled from session.
Why doesn't the base Page_Load fire?
Try moving your code into the Page_Init event.
Microsoft has some info on each event in the lifecycle http://msdn.microsoft.com/en-us/library/ms178472.aspx. This MSDN page tells you what types of things you should handle in each event.
You might want to think about implementing SO as a property, where the Get does (not sure if this is correct VB...)
Dim so As Session = Session.Item("SO")
If so Is Nothing Then
Session.Abandon()
Response.Redirect("~/timeout.htm")
End If
return so
It could be that something else is happening in the Init events that is causing it to fail. So rather than it not being called it just hasn't been called yet.
It could be that the autoevent wireup isn't wiring it up correctly, tend to override the OnInit event and attach the events manually myself, I have also read somewhere that this improves perfomance by not requiring the framework to do heaps of reflection on every post.
But back to your problem... try making the SO object private and create a property accessor for it that first checks that if the private is set, if not set it, before returning the private variable. If it isn't set and can't be found then it can abort the same way you are doing in the Load. This means that to load the variable you won't be dependent on the Page_Load from firing and thus the SO object should be available for you during the init routines, if you need it.
I have a ListView control and I have added a DataBound event (don't know if this is the correct one) to the control.
I'm wanting to access the data being bound to that particular ItemTemplate from this event, is that possible?
C# Solution
protected void listView_ItemDataBound(object sender, ListViewItemEventArgs e)
{
if (e.Item.ItemType == ListViewItemType.DataItem)
{
ListViewDataItem dataItem = (ListViewDataItem)e.Item;
// you would use your actual data item type here, not "object"
object o = (object)dataItem.DataItem;
}
}
Why they made this so different for ListView still sort of puzzles me. There must be a reason though.
A little late, but I'll try to answer your question, as I had the same problem and found a solution. You have to cast Item property of the ListViewItemEventArgs to a ListViewDataItem, and then you can access the DataItem property of that object, like this:
Private Sub listView_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.ListViewItemEventArgs) Handles productsList.ItemDataBound
If e.Item.ItemType = ListViewItemType.DataItem Then
Dim dataItem As Object = DirectCast(e.Item, ListViewDataItem).DataItem
...
End Sub
You could then cast the dataItem object to whatever type your bound object was. This is different from how other databound controls like the repeater work, where the DataItem is a property on the event args for the DataBound method.
Found a workaround, I created a method to format the data how I needed and called it from the markup using:
<%# doFormatting(Convert.ToInt32(Eval("Points")))%>
The data that is used for the current item can be found from the EventArgs.
So from the RepeaterItemEventArgs e we can access the current item by looking in e.Item.DataItem.
protected void listView_ItemDataBound(object sender, ListViewItemEventArgs e)
{
if (e.Item.ItemType == ListViewItemType.DataItem)
{
var currentItem = e.Item.DataItem;
}
}
How do you handle the Web User Control event? I notice my custom web user control have a event call OnError but it never fire when i tweak the control to fail. The control is basically a custom gridview control. I search for web user control event handling over the net but i haven't find a article that address what i looking for. Can someone do a quick explanation or point me to the right direction?
thank
You didn't mention what flavour of ASP.NET, so I'll make the assumption of VB - C# is largely the same with the exception of how the event handler is attached.
The normal pattern you would expect to see is something along these lines:
User Control "MyUserControl" CodeBehind
Public Event MyEvent(ByVal Sender As Object, ByVal e As EventArgs)
Private Sub SomeMethodThatRaisesMyEvent()
RaiseEvent MyEvent(Me, New EventArgs)
End Sub
Page Designer Code
Private WithEvents MyUserControl1 As System.Web.UI.UserControls.MyUserControl
Page or other Control that wraps MyUserControl instance CodeBehind
Private Sub MyUserControlEventHandler(ByVal Sender As Object, ByVal e As EventArgs) _
Handles MyUserControl.MyEvent
Response.Write("My event handled")
End Sub
In some instances, you see something called Event Bubbling which doesn't follow this kind of pattern exactly. But in the basic sense of handling events from a user control to a wrapper control or the page it sits in, that's how you would expect it to work.
I had an issue with a custom control that was throwing exceptions which were not firing Error event. Thus I could not catch exceptions from this control and display appropriate message in the ASP.NET page.
Here is what I did. I wrapped the code in the custom control in a try..catch block and fired the Error event myself, like this:
// within the custom control
try
{
// do something that raises an exception
}
catch (Exception ex)
{
OnError(EventArgs.Empty); // let parent ASP.NET page handle it in the
// Error event
}
The ASP.NET page was handling the exception using the Error event like this:
<script runat="server">
void MyCustomControl_Error(object source, EventArgs e)
{
MyCustomControl c = source as MyCustomControl;
if (c != null)
{
// Notice that you cannot retrieve the Exception
// using Server.GetLastError() as it will return null
Server.ClearError();
c.Visible = false;
// All I wanted to do in this case was to hide the control
}
}
</script>
<sd:MyCustomControl OnError="MyCustomControl_Error" runat="server" />