How to Invoke function in user control from parent page asp.net - asp.net

Asp.Net 4.0
Is it possible to call a function in a user control from the parent page in code behind?
The user controls are created by other programmers, however each will have a common public function that i look for called "Output" which returns values in need for the main page. The main page has a main menu, so only one user control will display based upon the main menu selection.
Folder in WebApp with user controls:
> UserControls
ProductA.ascx
ProductB.ascx
ProductC.ascx
ProductD.ascx
etc...
Code behind when user clicks a menu button:
Dim product As string = Session("MenuProduct")
Dim uc As UserControl
uc = LoadControl("~/UserControls/" & product & ".ascx")
InputPanel.Controls.Add(uc)
Function in user control I would like to access. This will be a common Function.
Public Function Output(ByVal ParamArray expr() As Object) As Object
...code
End Function

You can call functions in a userControl from a parent page if they have the right access modifier. Public I believe
Dim product As string = Session("MenuProduct")
Dim uc As UserControl
uc = LoadControl("~/UserControls/" & product & ".ascx")
InputPanel.Controls.Add(uc)
if (uc is TypeWithMethod )
{
Dim ucTyped As TypeWithMethod
ucTyped = uc As TypeWithMethod
ucTyped.Method()
}
Vb syntax is rusty, kept putting ; after every line.

Here are little details to what andrew is talking about...
Public Interface IGroupable
Function GetGroupId() As Int32
End Interface
Class MyUserControl
Inherits UserControl
Implements IGroupable
Public Function GetGroupId() As Integer Implements IGroupable.GetGroupId
Return 1
End Function
End Class
Class MyPage
Inherits Page
Dim id As Int32 = DirectCast(myUserControl, IGroupable).GetGroupId()
End Class
So basicly create a Interface, have all of your user controls implement that interface, and then when you load any of your user controls within webpage...simply CAST them to the the type of your interface name....your interface will have the access to the function.

Related

can I pass an aspx page class to a subroutine?

Here's what I'd like to do: Let's say I have a page named "foo.aspx". The class is called "foo". On the page is a checkbox named "bar". I want a subroutine to update that checkbox.
So what I want to write is something like:
In foo.aspx.vb:
partial class foo
... whatever ...
dim util as new MyUtility
util.update_checkbox(me)
In MyUtility
public sub update_checkbox(foo1 as foo)
foo1.bar.checked=true
end sub
But this doesn't work, as Visual Studio doesn't accept "foo" as a class name. Why not? Is there a magic namespace on it, or something else I have to do to identify the class besides say "foo"?
(And yes, I realize that in this trivial example, I could just pass in the checkbox, or move the one line of code into the aspx.vb, etc. My real problem involves setting a number of controls on the form, and I want to be able to do this in a class that has subtypes, so I can create an instance of the proper subtype, then just call one function and set all the controls differently depending on the subtype.)
Update
NDJ's answer works. For anyone else dropping by here, let me add that I was able to do something a little more flexible than his suggestion. I was able to create a property that returns the control itself, rather than some attribute of the control. Namely:
public interface ifoo
readonly property bar_property as literal
end interface
partial class foo
inherits system.web.page
implements ifoo
Public ReadOnly Property bar_property As Literal Implements ITest.bar_roperty
Get
' assuming the aspx page defines a control with id "bar"
Return bar
End Get
End Property
...
dim util=new MyUtility()
util.do_something(me)
...
end class
public class MyUtility
public sub do_something(foo as IFoo)
foo.bar_property.text="Hello world!"
foo.bar_property.visible=true
end sub
end class
This is a bit of a pain as you have to create an interface, and then create a property for each control that you want to be able to manipulate, but it does appear to work.
If there's a way to make the aspx class itself public, this is all unnecessary baggage in most cases. (It might be valuable if you have multiple pages that have controls that you want to manipulate in the same way.) But I can't figure out how to do that.
You can do this, but there are a few hoops to jump through.
Using your example...
If you create an interface with a Boolean property, then implement it in your page, then you can pass the interface about and changing the property will automatically change the checkbox. i.e.
interface:
Public Interface IFoo
Property Bar As Boolean
End Interface
implementation:
Partial Class _Foo
Inherits Page
Implements IFoo
Public Property Bar As Boolean Implements IFoo.Bar
Get
Return Me.CheckBox1.Checked
End Get
Set(value As Boolean)
Me.CheckBox1.Checked = value
End Set
End Property
Then some handler just needs to accept the interface:
Public Module SomeModule
Public Sub SetValues(foo As IFoo)
foo.Bar = True
End Sub
End Module
and the caller from the page passes itself:
Protected Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
SomeModule.SetValues(Me)
End Sub
You can expose the checkbox as a public property on the page. I don't write in VB.net, but it would look something like this in C#:
Can someone convert this to VB.Net?
public bool MyCheckBoxSetting
{
get { return mycheckbox.Checked; }
set { mycheckbox.Checked = value; }
}

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!

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.

ASP.NET Custom Control - Template Allowing Literal Content

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.

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