I'm stuck! I understand the page lifecycle and how i need to add the dynamic controls on page_init if I want to take advantage of viewstate. Also I know that I should try to avoid dynamic controls when possible. The dynamic controls are created depending on an object that is created from custom event arguments sent from a custom treeview. Problem is I need viewstate so I need to create them in page_init but I don't have the event args to create the object that tell me what controls to add until later in the lifecycle. Solution...
Private Function GetEventArgs() As npTreeViewEventArgs
Dim control As Control = Nothing
Dim e As npTreeViewEventArgs = Nothing
Dim ctrlname As String = Page.Request.Params("__EVENTTARGET")
Dim args As String = Request.Params("__EVENTARGUMENT")
If ctrlname IsNot Nothing AndAlso ctrlname <> String.Empty Then
control = Page.FindControl(ctrlname)
End If
If TypeOf control Is npTreeView AndAlso Not String.IsNullOrEmpty(args) Then
e = New npTreeViewEventArgs(args)
End If
Return e
End Function
I use this in page_init to create my object and controls. This feels very dirty to me. Is there another way to handle this?
This is actually the most straightforward solution to this type of problem. If you can't add all the controls to the page on every postback and use visibility to control their appearance, then what you are doing there is exactly what I would recommend. (And have recommended before.)
I cringe when I see people resort to redirects, or implementing their own viewstate tracking, or doing extreme dynamic control manipulation to solve this. It may feel dirty, but it's infinitely more understandable and maintainable than the alternatives.
Yes. The way I did it is to overload the viewstate of the dynamic controls to store it in their parents viewstate. Also overload the reading of the dynamic controls view state. Then you can create them late in the page cycle.
Of course it is a little trickier than that... but you get the idea. (I would post code examples, but it was a prior job and don't have access to them right now.)
Related
I am wondering how to access the datakeys of a ListViewDataItem that is a member of a ListView.
What I find strange is that when debugging it is possible to access the DataKeysContainer which can be seen here
However when attempting to access the DataKeyContainer during coding it is impossible, which can also be seen here
Any advice on how to access the datakeys and their values for a ListViewDataItem would be greatly appreciated.
You can use late binding, i.e. box your loop variable in an Object. This allows you to defer member validation until when the member is accessed at run-time. It is similar to dynamic in c#.
For Each itemObject As Object In lstViewModules.Items
' the DataKeysContainer you were looking for
Dim container = CType(itemObject.DataKeysContainer, Control)
' the same l in your loop
Dim l = CType(itemObject, ListViewDataItem)
' from here on, the rest of your loop code should work
Dim key As DataKey = lstViewModules.DataKeys(l.DataItemIndex)
Dim value = key("name")
Next
Since you know it's a ListView, you could also cast container to ListView for design-time ease.
Note: you must have Option Strict Off for this to work.
I have created an ASP.NET class derived from the standard WebControls.TextBox, with the intention of adding extra properties that will persist between post-backs. However, I cannot figure how to get the values in these properties to persist.
I have tried setting the value of the properties into the controls ViewState as part of the PreRender handler, but the value is then not accessible in the Init handler on the post-back, because the ViewState has not yet been setup.
I could look for the ViewState value in the Load handler of the control, but if the page/usercontrol that is using the control asks for the properties value during its Load handler, the control hasn't yet reached it's Load handler, and it therefore not there.
My current class looks something like this...
Public Class MyTextBox
Inherits TextBox
Private _myParam As String = ""
Public Property MyParam As String
Get
Return _myParam
End Get
Set(value As String)
_myParam = value
End Set
End Property
Private Sub MyTextBox_Init(sender As Object, e As EventArgs) Handles Me.Init
If Page.IsPostBack Then
_myParam = ViewState("myParam")
End If
End Sub
Private Sub MyTextBox_PreRender(sender As Object, e As EventArgs) Handles Me.PreRender
ViewState("myParam") = _myParam
End Sub
End Class
I must be missing something really simple, such as whether there is an attribute I can set against the property.
UPDATE
Thanks to #AVD pointing out that I really had very little clue about the ViewState and the Init / Load stages, I finally figured it all out.
If you have the time (and if you're doing any major ASP.NET work, you need to make the time) please read the Understand ASP.NET View State document that #AVD pointed me to. It will explain a lot.
However, what it didn't explain is if you place your control within a <asp:Repeater>, you may as well throw all the rules out of the window... and that is exactly the problem I was experiencing.
In the end, the way I managed to get it to work was to use a <asp:PlaceHolder> control within the repeater, create an instance of my control within the ItemDataBound handler of the repeater, and then add the control to the <asp:PlaceHolder>... all done within the Init section (which fortunately I'm able to do).
As Andrew found out in this previous question you can end up in a chicken/egg situation, where you need to create the controls in the Init, but you won't know what controls you need until the Load.
(I have still made AVD's answer the correct one, because in the context of my original question, it is absolutely correct).
You have to store/retrieve value to/from ViewState within the properties accessors.
Public Property MyParam As String
Get
If IsNothing(ViewState("myParam")) Then
return string.Empty
End IF
Return ViewState("myParam").ToString()
End Get
Set(value As String)
ViewState("myParam") = value
End Set
End Property
Maybe someone can help me out. I have created a simple web user control, a date and time picker, to be dropped onto my webform. This all works very nicely, I can set properties, usee the control satisfactorily for ui etc.
When it comes time to "use" the controls selected_date_time property, I just cant get it to persist. Nothing.. I have researched and tried endlessly using viewsate, context and session. Onbiouosly session works, but its dirty, and I am using two copies of this control (start and end time), so session vars really needs to be hacked to maket his work.
Am I missing something? The control gets initialized every time something happens, and obviuosly loses its state information. The ui keeps its state tho, as I can select a date, write that date to a label, and that persists. But when I try to access my properties of the control to retreive the selected combined date and time, (that is already persisting visually), its nothing. I debuggeed and it gets initialized everytime I do any form of post on the page.
Can someone please shed some light on this for me? Its really starting to be an issue now.
Thanks in advance.
Example: (simple components)
UC _ save_method
ViewState("var_time") = "My veiwstate text"
form _read_method
dim str as string = ViewState("var_time")
form sees nothing in the viewstate var.
I have also tried it with normal properties and values, which wasn't working, which is why I moved on to viewsate var's for my properties. Right now, I am just trying to get the viewstate to work even without properties.
Seems my form and the control must have two seperate viewstates? I am a bit of a n00b concerning viewstates.
Thanks
[solution]
You have to explicitly reset your properties on the prerender method of the control. My misunderstanding was that the page and the control share the same single viewstate. Turns out the control and the page that its on, has its own independant viewstates.
So absically, on your property set function in the control, set your value in the viewstate, and upon prerender, take that viewstate value, and set the property get variable =- the value in viewstate.
You can now access the property from your page as if everything was normal and the world is not ending... pheww
Thanks to cnay for directing me.
'Usr control xyz
Protected Sub Page_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreRender
my_time = ViewState("var_time")
'my_time is the get variable for property date_time
End Sub
'page use
xyz.date_time
Perhaps, you should post the complete control code to get the better answer but speaking in general terms - view-state (or control state) is a typically correct approach to persists control state such as properties. Each control instance get the different view-state bag and hence collisions are easily avoided.
Now, coming to your problem, a typical editable control first restores its state from view-state and then uses the request data to modify the state as needed. For example, a simple text-box control would persists its text value in view-state. On post-back, the text value will be restored from the view-state and then gets overwritten by the value present in the request (looked up by UniqueID property).
Now, for user controls, typically you can use child controls values (or properties) to derive the control value/properties - so these may not use view-state. However, if you add properties/state that is not backup by child controls then you may have to back them up in the view-state. So let's say your user control has two child controls - one for Date and other for time then can combine their values to get the selected date-time value for your control.
I've two issues currently preventing me from finishing two projects properly. I'll put them both here as I believe they're connected to the asp.net page lifecycle, but I can't find a way around them.
First I have a DropDownList which I must sort in codebehind. It only contains text, so I should be able to do that with the following method called in page load:
Dim alist As ArrayList = New ArrayList
For Each litem As ListItem In ltEsittelyDropDownList.Items
alist.Add(litem.Text)
Next
alist.Sort()
Dim uusiDDList As New DropDownList
For i As Integer = 0 To alist.Count - 1
Dim litem As New ListItem
litem.Text = alist(i).ToString
litem.Value = alist(i).ToString
uusiDDList.Items.Add(litem)
' Response.Write(alist(i).ToString)
Next
ltEsittelyDropDownList = uusiDDList
ltEsittelyDropDownList.DataBind()
As you can see, there's a commented response.write in there, which shows the list is actually sorted. So why, when I load the page, can't I see any effect?
The other problem, which is more critical and difficult, is as follows:
In the aspx page I'm binding a SQL Server 2005 datasource to a gridview. And in the code-behind I catch on to the RowDataBound event in which I handle some links and properties inside the gridviews' cells. But I cannot get this to work on the first page load, only after the first extra postback.
So, what is there to do? And thanks for all advice in front!
Your first problem is calling DataBind on a control you have filled manually. You likely have a DataSource specified in the control declaration, which is being used when DataBind is called. You can simplify the code by just adding the list items to the original control:
For i As Integer = 0 To alist.Count - 1
ltEsittelyDropDownList.Items.Add(New ListItem(alist(i).ToString())
Next
Alternatively, as you have a collection already, you can just bind it to the control:
ltEsittelyDropDownList.DataSource = alist
ltEsittelyDropDownList.DataBind()
For your second problem, some example code would help - specifically, where and how the control is databound and the code in RowDataBound.
Simple question, I guess.
For a long time I've blindly followed a (supposedly) common pattern when programmatically databinding my ASP.NET controls. Namely:
gridView1.DataSource = someList;
gridView1.DataBind();
However, if I were setting my GridView to bind to a DataSource control via the DataSourceID property, the call to DataBind() is unnecessary. Namely:
gridView1.DataSourceID = LinqDataSource1;
is sufficient.
Furthermore, if you try to set the DataSource property in ASPX markup, you are greeted with the following:
You cannot set the DataSource property declaratively.
I assume these are related, but I am still stumped as to why DataBind() is necessary. The difference between DataSource and DataSourceID is secondary - I can understand some magic taking place there. The real question is why doesn't the DataSource propery setter cause databinding automatically? Are there any scenarios in which we want to set the DataSource but not bind to it?
In ASP.Net, it's often important to have certain data available and ready at certain points in the page life cycle, and not before. For example, you may need to bind to a drop down list early to allow setting the selected index on that list later. Or you might want to wait a bit to bind that large grid to reduce the amount of time you hold that connection active/keep the data in memory.
Having you explicitly call the .DataBind() method makes it possible to support scenarios at both ends of the spectrum.
DataSource is a property of the BaseDataBoundControl class. DataSourceID is a property of the DataBoundControl class, which inherits from BaseDataBoundControl and did not exist before ASP.NET 2.0.
Since DataBoundControl is explicitly for displaying data in a list or tabular form, and BaseDataBoundControl cannot make that assumption, binding is not automatic when DataSource is set because the type of control may not match the structure of the data.
Of course, this is all just a guess based on the MSDN documentation, so I could be wrong.
I noticed that without using DataBind() that nothing will be displayed in my GridView so I always include it as shown in this section of code:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
' TableAdapter object.
' Provide communication between this application and the database.
'-----------------------------------------------------------------
Dim suppliersAdapter As New SuppliersTableAdapter
' Get the data from the TableAdapter into the GridView.
'------------------------------------------------------
GridView1.DataSource = suppliersAdapter.GetSuppliers()
' Display the result set from the TableAdapter in the GridView.
'--------------------------------------------------------------
GridView1.DataBind()
End Sub
Please forgive the extra commenting as I'm also still learning ASP.Net as well and the comments will help me learn better "what and why" to use certain statements.
Try this:
if (GridView1.EditIndex == e.Row.RowIndex)
{
TextBox t2 = (TextBox)e.Row.FindControl("TextBox2");
DateTime dt2;
if (DateTime.TryParse(t2.Text, out dt2))
{
t2.Text = dt2.ToString("yyyy-MM-dd");
}
}