This seems like it might be a bad idea, but I can't figure out why:
I have a class, cXYZ, with properties A, B and C. It also has a method 'sGetData' that loads those three properties from the database, and a method 'sSaveData' which saves it back.
class cXYZ
public property A as string...
public property B as string...
public property B as string..
public sub sGetData()...
public sub sSaveData()...
end class
A webform has the following property:
private property xyz() as cXYZ
get
return session("myXYZ")
end get
set (value as cXYZ)
session("myXYZ")=value
end set
end property
And the following events:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
if not ispostback() then
xyz=new cXYZ()
end if
end sub
Protected Sub ButtonLoad_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles ButtonLoad.Click
//Can now reference the class
txtA.text=xyz.A
txtB.text=xyz.B
txtC.text=xyz.C
end sub
Protected Sub ButtonSave_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles ButtonSave.Click
//Can now reference the class
xyz.A=txtA.text
xyz.B=txtA.text
xyz.C=txtC.text
xyz.sSaveData()
end sub
I can see some overhead with serializing/deserializing for each property reference- it might be worth doing this:
Protected Sub ButtonSave_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles ButtonSave.Click
dim localxyz as cXYZ=xyz
localxyz .A=txtA.text
localxyz .B=txtA.text
localxyz .C=txtC.text
xyz=localxyz
end sub
Other than that, views on why this is good or bad? The class is not large, it maintains the form state. Webforms suck, etc is not very useful..
I would improve that little bit:
private _xyz as cXYZ = nothing
private property xyz() as cXYZ
get
if _xyz is nothing then _xyz = TryCast(session("myXYZ"), cXYZ)
return _xyz
end get
set (value as cXYZ)
_xyz = value
session("myXYZ")=_xyz
end set
end property
I think its ok. I would addicionaly keep a variable to store the object to improve performance.
Something like this:
private _xyz as cXYZ = nothing
private property xyz() as cXYZ
get
if not _xyz is nothing then
return _xyz
else
return session("myXYZ")
end if
end get
set (value as cXYZ)
_xyz = value
session("myXYZ")=value
end set
end property
As long as your object is serializable you are OK.
Just don't hold any unmanaged object references in your session - otherwise you'll get into trouble once you move away from 'in process' session state to a web farm.
Related
I have an asp.net page which has amoungst some basic labels etc, and an updatepanel containing a dynamic list of UserControls. Each control has its own UpdatePanel, and basic controls. I have hatched together a collaps/expand feature for these controls and has been working fine to date.
To make the page more efficient I have been working to 'Async-ify' the heavy data processing that happens during load, to display in these fields and user controls.
After all of these changes everything still works fine (including a massive performance boost), except one thing - the collapse/expand no longer works. The limited data viewable in collapsed mode' has updated fine.
Reverting back to a non async call for the data, and everything works fine again, but slow again.
It is like they are constantly forced back to their original default state (collapsed), on each postback, triggered by the collapse/expand button. But nothing has changed from this perspective. I have literally only changed the data call to await async instead of non-async, which as i overstate, is fine for everything except this issue.
I can post any code, but not sure what is relevant. The only change I have made is as follows:
Protected Async Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Update()
End Sub
Private Async Function Update() As Task
Dim AController As New CIP_WS.ARFController
ARF = AController.GetARFWithResults(Client.ClientID, PRN, Nothing, StartTime, EndTime, CIP_WS.LabResultController.InequalityModes.AsIs)
PrintBasic()
PrintSPC()
PrintPhotos()
PrintResults()
PrintHistoricEdits()
End Function
Above works fine - non async
Protected Async Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
RegisterAsyncTask(New PageAsyncTask(Function() Update()))
End Sub
Private Async Function Update() As Task
Dim AController As New CIP_WS.ARFController
Dim ARFTask As Task(Of CIP_WS.ARF) = AController.GetARFWithResultsAsync(Client.ClientID, PRN, Nothing, StartTime, EndTime, CIP_WS.LabResultController.InequalityModes.AsIs)
ARF = Await ARFTask
PrintBasic()
PrintSPC()
PrintPhotos()
PrintResults()
PrintHistoricEdits()
End Function
Above, everything works fine, except usercontrol's not persisting their updated collapsed/expanded state.
Any help most appreciated
EDIT _____
Expand/Collaps method, as requested:
Partial Class wucLabResultPack
Inherits System.Web.UI.UserControl
Private _Expanded As Boolean
Public Property Expanded() As Boolean
Get
Return _Expanded
End Get
Set(ByVal value As Boolean)
_Expanded = value
ViewState("Expanded") = _Expanded
UpdateExpandPanel()
End Set
End Property
Protected Sub imgExpand_Click(ByVal sender As Object, ByVal e As System.Web.UI.ImageClickEventArgs) Handles imgExpand.Click
ToggleExpanded()
End Sub
Private Sub ToggleExpanded()
Expanded = Not Expanded
End Sub
Private Sub UpdateExpandPanel()
If Expanded Then
pnlDetail.Visible = True
imgExpand.ImageUrl = "Images/Shrink.png"
Else
pnlDetail.Visible = False
imgExpand.ImageUrl = "Images/Expand.png"
End If
End Sub
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If IsPostBack Then
Expanded = CInt(ViewState("Expanded"))
End If
End Sub
Hi I have website which has usercontrol, I can load the control. However if I assign the value of property of the user control, I get error" Type "WebUserControl) is not defined. Can someone told me how to solve it. Thanks in advance.
There is the code to use my Usercontrol
Partial Class Test
Inherits System.Web.UI.Page
Protected Sub form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles form1.Load
Dim ctrl As Control
ctrl = Page.LoadControl("Management/Profile/WebUserControl.ascx")
ctrl.ID = "ctrl_1"
CType(ctrl, WebUserControl).Level = 6 //this line cause error
PlaceHolder1.Controls.Add(ctrl)
End Sub
End Class
Here is my WebUserControl
Partial Class Management_Profile_WebUserControl
Inherits System.Web.UI.UserControl
Private m_Level As Integer
Public Property Level() As Integer
Get
Return m_Level
End Get
Set(ByVal Value As Integer)
m_Level = Value
End Set
End Property
Protected Sub Management_Profile_WebUserControl_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
TextBox1.Text = m_Level
End Sub
End Class
i have ASP.NET page, its code-behind, and a Class file:
Folder1/page.aspx (asp.net page), it contains a label:
<asp:Label runat="server" ID="Label1" Visible="false"></asp:Label>
Folder1/page.aspx.vb (code-behind), it calls connection.vb like this:
Dim x As New Connection
Protected Sub button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles button1.Click
x.checkusernameExists(TextBoxUsername.Text)
' I try to access `Boolean variable` Flag from Class file but I can't.
End Sub
App_Code/connection.vb (a class file that i created):
Public Class Connection
Public Sub checkusernameExists(ByVal username1 As String)
Dim flag as Boolean
' I try to access here `Label1.text` & `Label1.visible` to work on it but I can't.
End Sub
End Class
My Questions
1 - How can I access the Label1 from the ASP.NET page in Connection.vb?
2 - How can I access the Boolean variable from Connection.vb in page.aspx.vb (code behind)?
I am really stuck in this.
Thank you.
Use (public) properties or method parameters.
You have to ask yourself following: why should a class that is responsible for a connection(i assume to database) have access to your GUI at all? Don't hardlink different layers with each other, otherwise you won't be able to use them alone.
I would suggest to let the connection class do it's work and that is not to modify your frontend. Instead the controller (the aspx page) should manage it's GUI and call the connection class, using the return value to determine what to do next with the Label.
So return a Boolean to indicate if the user is valid:
Public Class Connection
Public Shared Function checkusernameExists(ByVal username1 As String)As Boolean
Dim userExists As Boolean
' acces db to check if the username exists '
Return userExists
End Sub
End Class
in your page:
Protected Sub button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles button1.Click
Dim userExists As Boolean = Connection.checkusernameExists(TextBoxUsername.Text)
Label1.Visible = userExists
If Label1.Visible Then Label1.Text = "Hello again " & TextBoxUsername.Text
End Sub
make flag as property and set this property in checkusernameExists function
Public Class Connection
Public Property Flag as Boolean
Public Sub checkusernameExists(ByVal username1 As String)
// set flag here
Flag = True // or whateever value returned from the database
' I try to access here `Label1.text` & `Label1.visible` to work on it but I can't.
End Sub
End Class
and access this instance level property in page.aspx.vb file
Dim x As New Connection
Protected Sub button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles button1.Click
x.checkusernameExists(TextBoxUsername.Text)
Label1.Visible= x.Flag;
' I try to access `Boolean variable` Flag from Class file but I can't.
End Sub
You can use Function to return value and pass label as parameter.
Public Function SaveChanges(ByRef Label1 As Label, ByVal username1 As String) As Boolean
{
Return True
}
It would be better if you pass the label properties to function instead of passing the object of label as it couple up two classes.
From the outside of a control, is it possible to find out what stage of the Page LifeCycle (Init, Load, PreRender etc), a particular control or page is up to?
For example, in pseudo code:
if myControl.CurrentLifeCycle == Lifecycle.Init
{ do something }
I'm afraid there is no builtin function to check in what Page-Lifecycle phase a Page is.
It is also difficult to add this functionality without handling all events in the Page itself, because some events are protected. Therefore you could also inherit the LifeCycleListener-class from Control, add it in the constructor to the page listening and override all events.
If you only need the public "phases" PreInit,Init,Load,DataBinding,PreRender,Unload,Disposed have a look at following approach(VB.Net, but i think you'll get the idea):
Public Enum LifeCyclePhase
AfterPreInit
AfterInit
AfterLoad
AfterDataBinding
AfterPreRender
AfterUnload
AfterDisposed
End Enum
Public Interface ITrackingLifeCycle
ReadOnly Property GetLifeCycleListener() As LifeCycleListener
End Interface
Public Class LifeCycleListener
Public Sub New(ByVal ctrl As Control)
Me._PageListening = ctrl.Page
AddListener()
End Sub
Private _CurrentPhase As LifeCyclePhase
Private _PageListening As Page
Public ReadOnly Property CurrentPhase() As LifeCyclePhase
Get
Return _CurrentPhase
End Get
End Property
Public ReadOnly Property PageListening() As Page
Get
Return _PageListening
End Get
End Property
Private Sub AddListener()
AddHandler _PageListening.PreInit, AddressOf PreInit
AddHandler _PageListening.Init, AddressOf Init
AddHandler _PageListening.Load, AddressOf Load
AddHandler _PageListening.DataBinding, AddressOf DataBinding
AddHandler _PageListening.PreRender, AddressOf PreRender
AddHandler _PageListening.Unload, AddressOf Unload
AddHandler _PageListening.Disposed, AddressOf Disposed
End Sub
Private Sub PreInit(ByVal sender As Object, ByVal e As EventArgs)
Me._CurrentPhase = LifeCyclePhase.AfterPreInit
End Sub
Private Sub Init(ByVal sender As Object, ByVal e As EventArgs)
Me._CurrentPhase = LifeCyclePhase.AfterInit
End Sub
Private Sub Load(ByVal sender As Object, ByVal e As EventArgs)
Me._CurrentPhase = LifeCyclePhase.AfterLoad
End Sub
Private Sub DataBinding(ByVal sender As Object, ByVal e As EventArgs)
Me._CurrentPhase = LifeCyclePhase.AfterDataBinding
End Sub
Private Sub PreRender(ByVal sender As Object, ByVal e As EventArgs)
Me._CurrentPhase = LifeCyclePhase.AfterPreRender
End Sub
Private Sub Unload(ByVal sender As Object, ByVal e As EventArgs)
Me._CurrentPhase = LifeCyclePhase.AfterUnload
End Sub
Private Sub Disposed(ByVal sender As Object, ByVal e As EventArgs)
Me._CurrentPhase = LifeCyclePhase.AfterDisposed
End Sub
End Class
The handler in this class are called after the handler in the page itself, so if you f.e. check the CurrentPhase in Page.Init you'll get PreInit. Therefor i have called this phase AfterPreInit.
Partial Public Class _Default
Inherits System.Web.UI.Page
Implements ITrackingLifeCycle
Private lcl As New LifeCycleListener(Me)
Public ReadOnly Property GetLifeCycleListener() As LifeCycleListener Implements ITrackingLifeCycle.GetLifeCycleListener
Get
Return lcl
End Get
End Property
You can now check the lifecycle-phase everywhere, even without a reference to a control via HttpContext.Current:
Public Class FooClass
Public Shared Sub Foo()
If Not (HttpContext.Current Is Nothing OrElse HttpContext.Current.Handler Is Nothing) Then
If TypeOf HttpContext.Current.CurrentHandler Is ITrackingLifeCycle Then
Dim page As ITrackingLifeCycle = DirectCast(HttpContext.Current.CurrentHandler, ITrackingLifeCycle)
Dim phase As LifeCyclePhase = page.GetLifeCycleListener.CurrentPhase
End If
End If
End Sub
End Class
This is neither tested sufficiently nor used by myself and certainly improvable, but maybe it helps you in your current situation.
I think what you try to achieve is conceptually wrong because you are thinking at the page events as page state. The page can’t be at “OnInit/OnLoad/…” state just because it’s an event.
What do you need it for? maybe we could suggest you a better approach to achieve your goal.
The „outside of your control” is the „inside” of another control or page or something being in a linked state of the lifecycle. You know its state already without testing.
I found out there is actually an internal property of the Page class that does just what I am looking for: it is an enum called ControlState:
internal enum ControlState
{
Constructed,
FrameworkInitialized,
ChildrenInitialized,
Initialized,
ViewStateLoaded,
Loaded,
PreRendered
}
I believe it is possible to access internal members in C#4, see here
As far as I understand the page lifecycle, you can't do it. Basically, Page class is the guy that has to raise events in a specific order. There is nothing in built that will tell the stage of processing. But in order to something, you can create a property and set up this property in different stages of processing.
I'm on the verge of madness ...
In the application I'm actually building, I'm dealing with two dynamically-added controls that need to interact with each other, but I've reduced the problem to an as-simple-as-I-can-make-it example with the controls being statically loaded, and it still presents the same problem: a NullReferenceException when invoking the delegate. Here's the gist:
Control 1
Partial Class Control1
Inherits System.Web.UI.UserControl
Private _delClicked As System.Delegate
Public WriteOnly Property UpdateLabel() As System.Delegate
Set(ByVal value As System.Delegate)
_delClicked = value
End Set
End Property
Protected Sub btnButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnButton.Click
Dim aObj(0) As Object
aObj(0) = 1
_delClicked.DynamicInvoke(aObj)
End Sub
End Class
Control 2
Partial Class Control2
Inherits System.Web.UI.UserControl
Protected WithEvents Control1 As New Control1
Delegate Sub ChangeLabel(ByVal int As Integer)
Protected Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Init
Dim delChangeLabel As New ChangeLabel(AddressOf UpdateLabel)
Me.Control1.UpdateLabel = delChangeLabel
End Sub
Private Sub UpdateLabel(ByVal int As Integer)
lblLabel.Text = "Value is now " & int
End Sub
End Class
If I put a breakpoint on the line of Control2 where the Control1 delegate is assigned, I can step through and watch it get set in the Control1 object. However, when the btnButton_Click event fires, the value of _delClicked has gone back to Nothing.
Any help would be greatly appreciated. Thanks!
My guess would be that you are losing the control on the postback. A dynamically added control gets lost on the postback. Sure you re-create a control that does the same thing, but that doesn't mean your going to be able to catch the click event. But the control doesn't have it's viewstate carried over between the postbacks.
See this article on how to go about doing this
Put this in Page_init()
Dim delChangeLabel As New ChangeLabel(AddressOf UpdateLabel)
Me.Control1.UpdateLabel = delChangeLabel