ControlsCollection

N

Nick Stansbury

Hi,

I've observed some weirdness with the ControlsCollection - do people
think this behaviour makes sense?

I populate a list, comprised of list-items which are user controls at
run time - because they are not tracked by the viewstate I manually "cache"
the controls collection of the list form to session memory at PreRender
thus:

viewstate("Controls") = New Guid().NewGuid().ToString
Session(viewstate("Controls")) = pnlResults.Controls

Now on page_load - i get them back with this:

Dim ControlCollectionID As String
ControlCollectionID = viewstate("Controls")
If Not Session(ControlCollectionID) Is Nothing Then
Dim controls As ControlCollection
controls = Session(ControlCollectionID)
For Each currentCtl As Control In controls
If TypeOf (currentCtl) Is UserControl Then
'write a routine to load the user control in here - first make a
duplicate copy
Dim Cuser As UserControl
Cuser = currentCtl
pnlResults.Controls.Add(Cuser)
Else
'if it isn't a user control then just add it in here as normal
Me.pnlResults.Controls.Add(currentCtl)
End If

Now when I do this you'd expect the CurrentCtl.Controls reference to now
point to the panel's controls collection *not* to the controls collection in
session memory. This is does. However it *also* removes the reference from
the session controls collection to CurrentCtl - the result - the for each
fails to add the last control in the collection (the count is reduced by one
as the reference to CurrentCtl is removed). Does this seem more than a
little bit weird to anyone else? Can anyone explain why it should do this?

Thanks

Nick
 
G

Guest

There are many problems in the code snippet you provided.

First, you don't need to generate a GUID and persist it to the view state to
get the user-specific control collection because, unlike the
HttpApplicationState object, the HttpSessionState object (Session) is unique
for each request. So when you say Session("something"), it's gonna retrieve
whatever has been stored in the key "Something" for the same unique user in a
pervious request. In other words, if you have a hundred concurrent threads
referencing the Session object, each has its own unique, per-user copy of it,
and using the same key ("Something") doesn't matter.

Second, the code is adding the controls from ControlCollection stored in the
session object to the pnlResults whether they're of type UserControl or not.
That's because you're not really making a duplicate (or a clone) of the
control in your code. You're just defining a reference of type UserControl
and making it point to the same object. So in both the if- and else- part,
you're basically adding the same control, albeit through another reference in
the if- part.

I'm not sure what the business scenario you're trying to implement, but it
doesn't seem like the right way to do it. Dynamic controls are meant to be
recreated depending on contextual data in each request. You don't really need
to cache them.
 
G

Guest

I looked further into your code. When you wire events in the ASPX page
through OnClick="EvenHandler", you need to either define the event handler
Button_Click in the <script runat=server> section of the ASPX page with
private accessibility or define it as protected in the Code Behind class.
That's because the ASPX page becomes a derived class from the Code Behind
class, and it won't be able to see the Code Behind's private members.
 
G

Guest

Sorry, this reply was meant to another message.

Homam said:
I looked further into your code. When you wire events in the ASPX page
through OnClick="EvenHandler", you need to either define the event handler
Button_Click in the <script runat=server> section of the ASPX page with
private accessibility or define it as protected in the Code Behind class.
That's because the ASPX page becomes a derived class from the Code Behind
class, and it won't be able to see the Code Behind's private members.
 
N

Nick Stansbury

Homann,
First, you don't need to generate a GUID and persist it to the view state to
get the user-specific control collection because, unlike the
HttpApplicationState object, the HttpSessionState object (Session) is unique
for each request. So when you say Session("something"), it's gonna retrieve
whatever has been stored in the key "Something" for the same unique user in a
pervious request. In other words, if you have a hundred concurrent threads
referencing the Session object, each has its own unique, per-user copy of it,
and using the same key ("Something") doesn't matter.

I understand what you are saying - but i think you've misunderstood
the point of attaching the
GUID to the viewstate - it means that a) i don't have to worry about using
this logic on multiple web forms - because I know that on each
webform the GUID to reference the control collection is different, and B) I
can have many copies of the same form open inside a single session
and not have cross references to the same controls collection
Second, the code is adding the controls from ControlCollection stored in the
session object to the pnlResults whether they're of type UserControl or not.
That's because you're not really making a duplicate (or a clone) of the
control in your code. You're just defining a reference of type UserControl
and making it point to the same object. So in both the if- and else- part,
you're basically adding the same control, albeit through another reference in
the if- part.

That was the general idea - when the webform dies references to each
control should be persisted - so that I can simply add them back in the
second time
around - rather than pulling the data back in from the database etc. I do
know that I am simply adding the same object via the IF / else / end if
statement - it just wasn't working if I didn't cast it to a user control
explicitly like that. I don't know why....

The point I was really asking about was - why does the behaviour in terms of
the user controls / controls collection exist - why does adding a control to
one collection remove it from the other? It just doesn't seem to make sense
to me
 
G

Guest

If you want to cache the controls per form for each user, you can just
generate the GUID once and reuse it for all requests to this particular form
(i.e. one GUID per form), or even better, just use the form's name as the key
to the Session. As it stands, there's a memory leak because you're not
cleaning up the Session reference of the previous GUID.

If you're simply caching the same controls and adding them to the panel on
each post, then there's no point in generating a new GUID for each request
and re-populating the sesssion object with these controls in the PreRender
event for each request.

What you're essentially doing here is shuffling the same controls back and
forth to the session in each request:

Load:
Panel Controls = Session Controls

PreRender:
Session Controls = Panel Controls

I guess what you need to do is cache the controls once in the session and
don't touch it again on subsequent requests. That's why a single GUID (or any
ID) per form is adequate. In other words, you need to discard the code in the
PreRender event and move the session caching code to the same function that
retrieves the information from the DB.

I'm not sure why you need any casting to assign a Control (supertype)
variable to UserControl (subtype) object. It shouild work without any casting.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top