can I pass an aspx page class to a subroutine? - asp.net

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; }
}

Related

Property binding to web controls in asp.net with linq and reflection

I'd like to "bind" properties from a DTO to corresponding controls, for example checkboxlists or textboxes. This is so that I can use a dto (passed through the querystring) to set the initial state of the control, and then have the control set it's value to the dto when it comes time to leave the page. I could do this with two mapping functions (control->DTO, DTO->Control) but instead I'm using Linq and Reflection to maintain a list of property<->control relationships.
I've never done this before and I suspect there may be a better way ... how would you maintain a property<->control relationship?
Here is the code I've used:
Public Class ObjectPropertyMapper
''' <summary>Provides a binding of a control to one or more properties in a DTO.</summary>
Private Class ControlBinding
''' <summary>A reference to the control which will be bound.</summary>
Public Property Control As Control
''' <summary>The property(ies) which the control will be bound to.</summary>
Public Property Props As New List(Of PropertyInfo)
Public Sub New(ctrl As Control, props As IEnumerable(Of PropertyInfo))
Me.Control = ctrl
Me.Props.AddRange(props)
End Sub
End Class
''' <summary>A list of all the control to dto bindings for this page.</summary>
Private Property ControlBindings As New List(Of ControlBinding)
''' <summary>Returns the property info from a linq expression.</summary>
Private Function GetPropertyFromExpression(Of T)(linqExpression As System.Linq.Expressions.Expression(Of Func(Of T))) As PropertyInfo
If TypeOf linqExpression.Body Is MemberExpression Then
Return DirectCast(linqExpression.Body, MemberExpression).Member
Else
Dim op = (CType(linqExpression.Body, UnaryExpression).Operand)
Return DirectCast(op, MemberExpression).Member
End If
End Function
''' <summary>Binds a control to a single property of the DTO.
''' Usage: BindControlToProperty(cblFundTypes, Function() Me.SearchParams.FundTypeIDs) ... </summary>
Protected Overloads Sub BindControlToProperty(Of T)(ctrl As Control, linqExpression As System.Linq.Expressions.Expression(Of Func(Of T)))
' This works by passing through a linq expression, which in turn has a reference to the property.
' We can therefore extract the property from the linq expression, allowing us to store a reference to the property against the reference to the control
Dim prop As PropertyInfo = GetPropertyFromExpression(linqExpression)
ControlBindings.Add(New ControlBinding(ctrl, {prop}))
End Sub
End Class
And the consumer:
Public Class MyPage
Public Sub OnLoad
BindControlToProperty(myCheckBoxList, Function() MyDTO.IntListProperty)
End Sub
End Class
The only alternate method I found was projecting properties onto an anonymous type, as per https://stackoverflow.com/a/1984190/767599
An example would be:
Public Class exampleDTO
Public Property MyProp1 As String
Public Property MyProp2 As String
End Class
Public Function GetVariableName(Of T)(obj As t) As String
Dim properties As System.Reflection.PropertyInfo() = obj.GetType.GetProperties
If properties.Length = 1 Then
Return properties(0).Name
Else : Throw New Exception
End If
End Function
Public Sub BindToControl(Of T)(ctrl As Control, prop As T)
Response.Write(GetVariableName(prop))
End Sub
Public Sub Main
Dim c As New Control
Dim dto As New exampleDTO
BindToControl(c, (New With {dto.MyProp1}))
End Sub
However I have no idea which solution is "better".

How to Invoke function in user control from parent page 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.

Getting a ScriptReference from a ScriptResourceMapping definition in a custom control

I am building a custom control with client side scripts that I would like to reference using ScriptManager.ScriptResourceMapping (to make use of the Path and DebugPath attributes).
I would like the custom control to be easily ported to other projects - i.e. I would like to drag and drop the codebehind files (and eventually make the control a separate DLL, but for now the drag and drop will suffice). I would therefore like to avoid (1) having the client script as an embedded resource, (2) referenced as a WebResource in the AssemblyInfo, or (3) have the ScriptManager.ScriptResourceMapping.AddDefinition in global.asax.
In simple terms can I get the script management code to be in just the custom control's code?
At the moment I am getting an error stating that the script reference cannot be found in the assembly, and I guess I am setting the wrong assembly.
My custom control code is as follows:
Public Class MyControl
Inherits System.Web.UI.LiteralControl
Implements ISectionControl, IScriptControl
Private _scriptReference As ScriptReference
Public Sub New()
' Add the resource mapping
ScriptManager.ScriptResourceMapping.AddDefinition("MyControlScript", New ScriptResourceDefinition With {
.ResourceAssembly = System.Reflection.Assembly.GetExecutingAssembly,
.ResourceName = "MyControlScript.js",
.Path = "Path.To.MyControlScript.minimised.js",
.DebugPath = "Path.To.MyControlScript.original.js"
})
' Set the script reference
_scriptReference = New ScriptReference("MyControlScript.js", Assembly.GetExecutingAssembly.FullName)
End Sub
Protected Overrides Sub OnPreRender(e As System.EventArgs)
MyBase.OnPreRender(e)
' Register the script
ScriptManager.GetCurrent(Page).RegisterScriptControl(Of MyControl)(Me)
' Some code to set the Text of the literal control
' ...
End Sub
Public Function GetScriptDescriptors() As System.Collections.Generic.IEnumerable(Of System.Web.UI.ScriptDescriptor) Implements System.Web.UI.IScriptControl.GetScriptDescriptors
Return New ScriptDescriptor() {}
End Function
Public Function GetScriptReferences() As System.Collections.Generic.IEnumerable(Of System.Web.UI.ScriptReference) Implements System.Web.UI.IScriptControl.GetScriptReferences
Return New ScriptReference() {_scriptReference}
End Function
End Class
I hope the question makes sense. Thanks for taking the time to read through.
Ali
Answered this myself, I was getting confused with the assemblies and the constructors for ScriptReference. I just wanted a ScriptReference with the (mapped) name so I used the blank constructor and then set Name. I could then remove the assembly information.
Adjusting the following sorted the problem out:
Public Sub New()
' Add the resource mapping
ScriptManager.ScriptResourceMapping.AddDefinition("MyControlScript", New ScriptResourceDefinition With {
.Path = "Path.To.MyControlScript.minimised.js",
.DebugPath = "Path.To.MyControlScript.original.js"
})
' Set the script reference
_scriptReference = New ScriptReference() With {.Name="MyControlScript"}
End Sub

Do I Need a Class if I only need 1 Property (at the moment)?

Update: I didn't make it clear but I meant this to be a question about where/how I would use a function to return a list of strings when I'm trying to just work with classes.
I have a class called Account.
I have data access class called AccountDAO.
I have various functions that return lists of objects like GetAllAccounts, GetAccountByID etc.
I want to populate a drop down list with just the account names and nothing else. It's proving rather slow when using lists of objects and databinding them to the dropdownlist.
I feel like I should be using a simple "Select Account_Name From blah" type statement and returning a list of strings but I don't know how to work this into my class and data access class.
How should I handle this predicament?
You can use a list of string,s and bind the list of strings to a dropdownlist no problem... the DDL can support that, just leave out DataTextField and DataValueField props, and it will display the account name as is, which that name would be accessible through the ListItem's Text and Value property.
I like to use objects to be consistent with the rest of the app (which other areas might need a class), and if for some reason you want to add AccountKey later, if you use an object, all you need to do is add a property. Otherwise, if using strings, you'd have to switch up the binding later to point to the object.
HTH.
There is nothing wrong by making a function that only returns a list of strings. YOu could however wonder if it's not better to restrict the number of records you want to put in the list and use some kind of paging.
Assuming that you're using a List<>, you can try something like this:
IEnumerable<string> nameList = accountList.Select(t => t.AccountName);
Or if you need a List:
List<string> nameList = accountList.Select(t => t.AccountName).ToList();
Go with your feelings. Use a datareader to select the list and then load them into an arraylist which can then be bound to the dropdown. Alternately, use something like this method I use to provide both a DisplayMember and a ValueMember which uses a class (with both values) as members of the arraylist. This should give you the general idea. (Note: I normally include this code in a data access class (MyBase) where StartReader, _datRdr, ReadNext and_ReaderValid are a members. But the general idea is intact.)
Public Sub LoadDataSource(ByRef PlantDataSource As PlantSource, Optional ByVal Filter As String = "", Optional ByVal IncludeBlankItem As Boolean = False)
PlantDataSource = New PlantSource
If IncludeBlankItem Then
PlantDataSource.Add(0, "")
End If
If Filter = String.Empty Then
Call StartReader(" Order by PlantName")
Else
Call StartReader(String.Concat(" Where ", Filter, " Order by PlantName"))
End If
If _DatRdr.HasRows Then
While MyBase._ReaderValid
PlantDataSource.Add(PlantId, PlantName)
ReadNext()
End While
End If
Call CloseReader()
End Sub
Private Class PlantListing
Private _PlantList As New ArrayList
Public Sub Add(ByVal PlantId As Integer, ByVal PlantName As String)
_PlantList.Add(New PlantDataItem(PlantId, PlantName))
End Sub
Public ReadOnly Property List() As ArrayList
Get
Return _PlantList
End Get
End Property
End Class
Private Class PlantDataItem
Private _PlantId As Integer
Private _PlantName As String
Public Sub New(ByVal pPlantId As Integer, ByVal pPlantName As String)
Me._PlantId = pPlantId
Me._PlantName = pPlantName
End Sub
Public ReadOnly Property PlantName() As String
Get
Return _PlantName
End Get
End Property
Public ReadOnly Property PlantId() As Integer
Get
Return _PlantId
End Get
End Property
Public ReadOnly Property DisplayValue() As String
Get
Return CStr(Me._PlantId).Trim & " - " & _PlantName.Trim
End Get
End Property
Public Overrides Function ToString() As String
Return CStr(Me._PlantId).Trim & " - " & _PlantName.Trim
End Function
End Class

Connect reusable ASP.NET WebControl to a method for loading data

I'm trying to create a control that can extend other webcontrols and set some properties like visible and enabled, based on user permissions.
Here's an example where your user role would need to include the "CanSave" permission:
<asp:Button ID="btn1" runat="server" Text="Save"/>
<myControls:PermissionsExtender runat="server" ControlToSet="btn1" Permission="CanSave"/>
I'm trying to keep this reusable, that's why the PermissionExtender is in a separate project that can not have any dependencies to other projects. To make a decision, the control of course needs to get this info from somewhere else (database or something). I made another control and, using events, the above extender will be set by a master control, so only that needs to know where to look up the information.
The master control now needs to be configured to know where the information about roles and permissions will be coming from. My idea was to have an interface inside the reusable project, and implement that somewhere else, then configure my control to go and find the class that implements the method I need and load it through reflection. But I'm unclear how this could work. I would probably place the master control in the masterpage and supply it a class name like PermissionClass="SecurityLibrary.PermissionsClass". Kinda like ObjectDatasource does it, but other suggestions are welcome.
The method signature would be like:
bool HasPermission(string permission)
It would know the current users role and using that combination, looks up if the role includes the permission.
How can I wire up a call from the control to a method inside my main project that can supply the necessary information without making them dependent.
I think I've got something that will work for you (tested fine for me but I may have misunderstood part of what you were looking for). With this implementation the asp.net designer code will look like this:
<web:PermissionMasterControl runat="server" ID="masterController" PermissionClass="SecurityLibrary.RandomPermissionClass" />
<asp:Button ID="btnSave" runat="server" Text="save" />
<web:PermissionExtender runat="server" ControlToSet="btnSave" Permission="CanSave" MasterControllerID="masterController" />
Now for the SecurityLibrary. Pretty straight forward, I included a simple "RandomPermissionClass" that randomly returns true/false.
Namespace SecurityLibrary
Public MustInherit Class PermissionClass
Public MustOverride Function HasPermission(ByVal permission As String) As Boolean
End Class
Public Class RandomPermissionClass
Inherits PermissionClass
Private rand As New Random()
Public Overrides Function HasPermission(permission As String) As Boolean
Return If(rand.Next(2) = 0, False, True)
End Function
End Class
End Namespace
Now we have the "myControls" library, which contains no references to SecurityLibrary. I created two controls and a delegate. The controls are "PermissionMasterControl" and "PermissionExtender". The delegate is what is used to actually perform the check against the reflected object.
Namespace myControls
Public Delegate Function HasPermissionDelegate(ByVal permission As String) As Boolean
Public Class PermissionMasterControl
Inherits System.Web.UI.Control
Public Property PermissionClass As String
Get
Return If(ViewState("PermissionClass") Is Nothing, "", ViewState("PermissionClass").ToString())
End Get
Set(value As String)
ViewState("PermissionClass") = value
End Set
End Property
Private ReadOnly Property PermissionDelegate As HasPermissionDelegate
Get
If _permissionDel Is Nothing Then
If Not String.IsNullOrEmpty(PermissionClass) Then
Dim t = Type.GetType(PermissionClass, False)
If t IsNot Nothing Then
_permissionObj = Activator.CreateInstance(t)
Dim mi As MethodInfo = _
t.GetMethod("HasPermission", BindingFlags.Public Or BindingFlags.Instance)
_permissionDel = [Delegate].CreateDelegate(GetType(HasPermissionDelegate), _permissionObj, mi)
End If
End If
End If
Return _permissionDel
End Get
End Property
Private _permissionObj As Object = Nothing
Private _permissionDel As HasPermissionDelegate = Nothing
Public Function HasPermission(ByVal permission As String) As Boolean
If PermissionDelegate Is Nothing Then
Throw New NullReferenceException("The specified permission class (" + PermissionClass + ") could not be loaded/found.")
End If
Return PermissionDelegate(permission)
End Function
End Class
Public Class PermissionExtender
Inherits System.Web.UI.Control
Public Property ControlToSet As String
Get
Return If(ViewState("ControlToSet") Is Nothing, "", ViewState("ControlToSet").ToString())
End Get
Set(value As String)
ViewState("ControlToSet") = value
End Set
End Property
Public Property Permission As String
Get
Return If(ViewState("Permission") Is Nothing, "", ViewState("Permission").ToString())
End Get
Set(value As String)
ViewState("Permission") = value
End Set
End Property
Public Property MasterControllerID As String
Get
Return If(ViewState("MasterControllerID") Is Nothing, "", ViewState("MasterControllerID").ToString())
End Get
Set(value As String)
ViewState("MasterControllerID") = value
End Set
End Property
Protected ReadOnly Property MasterController As PermissionMasterControl
Get
If _mastercontroller Is Nothing Then
_mastercontroller = Me.Page.FindControl(MasterControllerID)
End If
Return _mastercontroller
End Get
End Property
Protected ReadOnly Property ManagedControl As Control
Get
If _controlToSet Is Nothing Then
_controlToSet = Me.NamingContainer.FindControl(ControlToSet)
End If
Return _controlToSet
End Get
End Property
Private _controlToSet As Control = Nothing
Private _mastercontroller As PermissionMasterControl = Nothing
Protected Overrides Sub OnLoad(e As System.EventArgs)
MyBase.OnLoad(e)
Dim bResult As Boolean = MasterController.HasPermission(Permission)
ManagedControl.Visible = bResult
End Sub
End Class
End Namespace

Resources