ASP.NET Custom Control - Template Allowing Literal Content - asp.net

I want my User Control to be able to have Literal Content inside of it. For Example:
<fc:Text runat="server">Please enter your login information:</fc:Text>
Currently the code for my user control is:
<ParseChildren(True, "Content")> _
Partial Public Class ctrFormText
Inherits UserControl
Private _content As ArrayList
<PersistenceMode(PersistenceMode.InnerDefaultProperty), _
DesignerSerializationVisibility(DesignerSerializationVisibility.Content), _
TemplateInstance(TemplateInstance.Single)> _
Public Property Content() As ArrayList
Get
If _content Is Nothing Then
Return New ArrayList
End If
Return _content
End Get
Set(ByVal value As ArrayList)
_content = value
End Set
End Property
Protected Overrides Sub CreateChildControls()
If _content IsNot Nothing Then
ctrChildren.Controls.Clear()
For Each i As Control In _content
ctrChildren.Controls.Add(i)
Next
End If
MyBase.CreateChildControls()
End Sub
End Class
And when I put text inside this control (like above) i get this error:
Parser Error Message: Literal content ('Please enter your login information to access CKMS:') is not allowed within a 'System.Collections.ArrayList'.
This control could have other content than just the text, so making the Content property an attribute will not solve my problem.
I found in some places that I need to implement a ControlBuilder Class, along with another class that implements IParserAccessor.
Anyway I just want my default "Content" property to have all types of controls allowed in it, both literal and actual controls.

You need to set the ParseChildren attribute to "False", otherwise any content in your control will be parsed into the "Content" property. This does not meet your needs, as you want to have controls AND content.
In order to accomplish this, override the AddParsedSubObject method. Check for the parsed control type, and if it's a literal control, add it to your UserControl's Content property. If it's some other control, just add it to the Controls collection like usual.
At the end of parsing all your sub-objects, simply display the Content property in a literal control or a panel or something.

Related

Grabbing a property from an aspx page to an ascx toolbar in VB

I have a web page (aspx)- Purchasing page, with a ascx toolbar - Export Toolbar, that is used to export the data (either .xls or .csv).
I need to grab the Name of the Supplier from the Purchasing page and insert that value into the name of the export file on the ascx toolbar.
On the Purchasing page there is a ddl where the user can select the supplier and a grid that will display all the data. Above the grid there is the tool bar with an export button. I need to be able to grab the text of the dropdown list and utilize that on the ExportToolbar.ascx.vb page so I can take that text and insert it into the name.
I was trying to use a public property get and set method but it was not working. How would I go about grabbing that selected text from the Supplier ddl?
Conventional thinking goes like this: an ascx can be hosted on any aspx page. So usually it is bad form for an ascx to access properties of its host page. It is much more proper for the ascx to have a public property and the aspx will push the value into the ascx (as needed).
However, if you really want to go this route, the .Page property (of the ascx) referrs to the host page. If you cast it to the (stronger) type(name) of the host, you can get to the hosts properties. Like this:
'if your host page is called HostPage (and the class name is the same)
Dim host as HostPage = CType(me.Page, HostPage)
'now refer to the controls on the host (aspx) page
dim example as string
example = host.txtExample.Text
Keep in mind, this will cause errors if your ascx is hosted on several pages.
You can use an event form this purpose. Define the event on the UserControl like this:
Public BeforeExportEventArgs
Inherits EventArgs
Public Property FileName As String
End Class
Public Class ToolbarControl
Inherits UserControl
Public Event BeforeExport As EventHandler(Of BeforeExportEventArgs)
Public Sub btnExport_Click(sender As Object, e As EventArgs) Handles btnExport.Click
' Retrieve File Name
Dim beforeExpEventArgs As New BeforeExportEventArgs()
RaiseEvent BeforeExport(Me, beforeExpEventArgs)
' Set default filename if not provided by an event handler
If String.IsNullOrEmpty(beforeExpEventArgs.FileName) Then
beforeExpEventArgs.FileName = "DefaultFileName.csv"
End If
' Export data
End Class
Add an event handler to the form that hosts the UserControl:
Public Class WebForm1
Inherits Page
' ...
Public Sub expToolbar_BeforeExport(sender As Object, e As BeforeExportEventArgs) Handles expToolbar.BeforeExport
e.FileName = ddlSupplier.Text + ".csv"
End Sub
' ...
End Class
This way, you avoid tight coupling between the UserControl and the Page. The pages that host the UserControl can set a specific filename, but don't have to.
What I ended up doing was this-
On the ascx page I created a public property-
Public Property SupplierSelection As String
Get
Return Convert.ToString(ViewState.Item("SupplierSelection"))
End Get
Set(ByVal value As String)
ViewState.Add("SupplierSelection", value)
End Set
End Property
And then on the aspx page I added this on the load grid event-
SupergridToolbar1.SupplierSelection = ddlStrategy.SelectedItem.Text.ToString()
I was then able to use the Supplier Selection on the ascx page. Thanks for the help!

Expose Body tag from Master Page to Content Pages in ASP.NET

I want to expose the <body> of my Master Page to my Content Pages. Therefore I do:
Master.aspx
<body id="MasterPageBodyTag" runat="server">
Master.aspx.vb
Public Property Messaging() As Messaging
Get
Return mMessaging
End Get
Set(ByVal value As Messaging)
mMessaging = value
End Set
End Property
Public Property BodyTag() As HtmlGenericControl
Get
Return MasterPageBodyTag
End Get
Set(ByVal value As HtmlGenericControl)
MasterPageBodyTag = value
End Set
End Property
ContentPage.aspx
<%# MasterType VirtualPath="~/my.master" %>
ContentPage.aspx.vb
Master.BodyTag.Attributes.Add("onload", "MyScript()")
However, not only I don't get the BodyTag in my content pages but I also receive an error that I cannot access the Messaging property (error: is not a member of Master), that before was working correctly. What can be the cause?
I am not expert at VB. But it seems that you need at first to cast me.Master reference to the Class type of the your custom master page before trying to access properties and method specific to you custom master page. I suppose that in this case automatic custom can't be done. So you can try to do the following:
Dim myCustomMaster As Site = CType(me.Master, Site)
where Site type is custom master class type.

Need to reference controls/properties in an ascx control (asp.net, written in vb)

I have a user control that I'm adding to a webpage dynamically. The ascx has a couple of controls that I want to have access to at runtime. I can access the ascx itself, but none of the controls on the ascx are available. I have tried adding a simple public variable and also tried adding a public property to the ascx, but I am unable to get access to either of them at design time (compile errors). I would appreciate any ideas - I'm stuck... :-)
I added the following to the code-behind of the ascx control:
Public Property areaCode() As String
Get
Return iebEmpPhoneAreacode.Text
End Get
Set(ByVal value As String)
iebEmpPhoneAreacode.Text = value
End Set
End Property
Public AreaCodeStr As String = ""
and am trying to use variations of the following to access the property/ascx controls:
For Each ctrl As Control In pnlPhones.Controls
If ((TypeOf ctrl Is ctrlPhone) And (ctrl.ID = vbNullString)) Then
(DirectCast(ctrl, ctrlPhone)).AreaCodeStr = "test"
'or try this
ctrl.areaCode = "test"
End If
Next
The hosting page should have an #Reference Directive pointing to the loaded ascx so it will be compiled with the page.
Something like:
<%# Reference VirutalPath="YourReferenceControl.ascx" %>
This should go in the directives area somewhere below the #Page directive.
http://msdn.microsoft.com/en-us/library/w70c655a.aspx

ASP.NET Server Control - Is shared Viewstate between controls possible

If I have a master 'composite custom server control', with several child custom controls, is it possible, for the master control to share its viewstate with the child controls, which are also custom composite server controls,(all of the controls are composite custom server controls)?
To expand a little more, say I have a Person control, Address control, Phone control and Notes control. Address, Phone and Notes can exist as either independant controls, or as part of the Person control. Since each control is responsible for its own viewstate, and stores required data in it, so it can render/postback etc, it ends up that there is a lage duplication in the viewstate, since the Person control stores all the data, and then each child control stores its own data again. Also, to further complicate things, the Person Control adds the child controls dynamically, so its possible to add a 2nd address/phone number/note etc,which can cause yet a larger viewstate(up to 1MB).
In the case of when all the Address/Phone/etc controls are children of the the Person Control, is it possible for me to somehow share common viewstate data, so I dont have 2/3/4 copies of some stuff in the viewstate, and when they are not components, just act as normal?
I do have one idea for a solution, but its fairly nasty imo, where I could modify the Person control, expose its viewstate or the data, and then in the child control, would check the control hierarchy, and if the child control is part of a Person, dont use to its own viewstate, use the exposed one. This would require me to rework alot of the existing code, which I'd rather avoid if possible.
The first question I'd ask is, "How much of this data do you really need to keep on the ViewState?" For example, is there enough information in a typical page request to be able to rebuild all of the information that is in some of these controls?
I find that ASP.NET typically uses ViewState far more than I really need it to, and if you're getting a 1MB viewstate, I suspect the same is probably true for you. I highly recommend reading this article to get a more full understanding of how ViewState works, what causes values to get saved to ViewState, and how to avoid using it where it's not necessary. You may be able to solve the bulk of your problem simply by doing more of your work in Page_Init instead of Page_Load, for example.
You can "share" a ViewState between any number of objects by passing it around as a StateBag type, or by using delegate functions that return the ViewState that needs to be shared. The usage of this, however; should be limited to very specific circumstances, because typically controls use Properties to expose their ViewState data to other objects (See the link #StriplingWarrior posted here). That being said, here is some example code:
User Control: SharedState.ascx
<%# Control Language="vb" AutoEventWireup="false" CodeBehind="SharedState.ascx.vb" Inherits="TestSite.SharedState" %>
User Control: SharedState.ascx.vb
Public Class SharedState
Inherits System.Web.UI.UserControl
Public Delegate Function GetStateDelegate() As StateBag
Public Const SharedValueKey = "The key used to access the ViewState dictionary"
Public Property GetState() As GetStateDelegate
' Different ways to get values
Public Function GetTypedValue(Of TValue)() As TValue
Return CTypeDynamic(GetValue(), GetType(TValue))
End Function
Public Function GetValue() As Object
' Use the delegate to get the view state from the parent
Return GetState.Invoke()(SharedValueKey)
End Function
Public Function GetValue(state As StateBag) As Object
Return state(SharedValueKey)
End Function
' Different ways to set values
Public Sub SetTypedValue(Of TValue)(value As TValue)
Me.SetValue(value)
End Sub
Public Sub SetValue(value As Object)
GetState.Invoke()(SharedValueKey) = value
End Sub
Public Sub SetValue(state As StateBag, value As Object)
state(SharedValueKey) = value
End Sub
' Set ViewState value using a key and the delegate
Public Sub SetStateWithKey(key As String, value As Object)
GetState.Invoke()(key) = value
End Sub
' Get ViewState value using a key and the delegate
Public Function GetStateWithKey(key As String) As Object
Return GetState.Invoke()(key)
End Function
End Class
Page: SharedStatePage.aspx
<%# Page Language="vb" AutoEventWireup="false" CodeBehind="SharedStatePage.aspx.vb" Inherits="TestSite.SharedStatePage" %>
<%# Register src="SharedState.ascx" tagname="SharedState" tagprefix="uc1" %>
<uc1:SharedState ID="SharedState1" runat="server" />
<uc1:SharedState ID="SharedState2" runat="server" />
Page: SharedStatePage.aspx.vb
Public Class SharedStatePage
Inherits System.Web.UI.Page
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
' Set up SharedState 1 & 2 to use the delegate '
SharedState1.GetState = AddressOf GetState
SharedState2.GetState = AddressOf GetState
' Set the view state from the page '
ViewState(SharedState.SharedValueKey) = 23
' Read the view state from the user controls '
Dim sharedInt As Integer
sharedInt = SharedState1.GetTypedValue(Of Integer)()
sharedInt = SharedState1.GetValue()
sharedInt = SharedState1.GetValue(ViewState)
sharedInt = SharedState2.GetValue()
' Set the view state from one of the user controls '
SharedState2.SetTypedValue(Of String)("Hello")
Dim sharedStr As String = ViewState(SharedState.SharedValueKey)
sharedStr = SharedState1.GetTypedValue(Of String)()
sharedStr = SharedState2.GetValue()
' Use a different key to set and get view state data '
ViewState("MyKey") = New With {.Name = "Some Object", .Value = 46}
Dim myObj = SharedState1.GetStateWithKey("MyKey")
SharedState2.SetStateWithKey("MyKey", "New Value")
myObj = ViewState("MyKey")
End Sub
Private Function GetState() As StateBag
Return ViewState
End Function
End Class
Use with caution, or don't use with abandon.

How to enable/disable web elements of a parent aspx page from the child ascx page?

I have an aspx page with three web controls: one to control the List Users page, one to control the Edit Users page, and one to control the Add User page. I have discovered a method for accessing these elements, but it seems to be limited. Here is what I have done:
Protected Sub editUser(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewEditEventArgs)
'set selected user from gridview.
Dim index As Integer = e.NewEditIndex
Dim userId As Integer = gvListUsers.DataKeys(index).Value
Session.Item("SelectedUserId") = userId
'show edit page, hide list and add page.
With Page.Form.Controls(1)
.Controls(getControlId("loadAddUser")).Visible = False
.Controls(getControlId("loadEditUser")).Visible = True
.Controls(getControlId("loadListUser")).Visible = False
End With
End Sub
The getControlId function looks like this:
Public Function getControlId(ByVal control As String) As Integer
Dim enumer As System.Collections.IEnumerator = Page.Form.Controls.Item(1).Controls.GetEnumerator
Dim i As Integer
For i = 0 To (Page.Form.Controls.Item(1).Controls.Count - 1)
If Page.Form.Controls(1).Controls.Item(i).ID = control Then
Return i
End If
Next
Return Nothing
End Function
This works in most cases. However, I am unable to access the "enabled" attribute of these web controls. Why is this, and how might I access that attribute?
Thanks :)
You could raise events from your UserControls which you subscribe to in the parent ASPX page. In the parent page event action you could enable/disable your controls,
Here's an example of events in UserControls: http://codebetter.com/blogs/brendan.tompkins/archive/2004/10/06/Easily-Raise-Events-From-ASP.NET-ASCX-User-Controls.aspx
Something else to think about: are you getting any benefit from moving this code into usercontrols? Would any of the individual controls be re-usable on their own? Creating tightly coupled controls that rely on each other being present doesn't give you much re-usability of the individual controls.
Visible is a property provided by the System.Web.UI.Control class, which is why you can access it directly. Enabled is not a property on this class, so you need to map the control object to a variable of the type of your custom control class if you want to access the Enabled property.
Dim myControl As TheAddUserControl
With Page.Form.Controls(1)
myControl = .Controls(getControlId("loadAddUser"))
myControl.Enabled = False
.Controls(getControlId("loadEditUser")).Visible = True
.Controls(getControlId("loadListUser")).Visible = False
End With
To expose an Enabled property in you user control:
Public Property Enabled As Boolean
Get
Return (Child1.Enabled And Child2.Enabled And Child3.Enabled)
End Get
Set(ByVal value As Boolean)
Child1.Enabled = value
Child2.Enabled = value
Child3.Enabled = value
End Set
End Poperty

Resources