ASP .net : register custom control from .vb code - asp.net

I try to create custom control, loaded dynamically from .vb code.
Here is my custom control "ControlCar" in file "controlcar.ascx"
<%# Control Language="VB" ClassName="ControlCar" %>
<script runat="server">
Private m_car As Car = Nothing
Public Property Car() As Car
Get
Car= m_car
End Get
Set(ByVal value As Car)
m_car = value
End Set
End Property
Protected Sub Panel_OnLoad(ByVal sender As Object, ByVal e As System.EventArgs)
If Me.m_car Is Nothing Then
lit_color.Text = "(m_car Is Nothing)"
Else
lit_color.Text = "color of Me.m_car is (" & Me.m_car.Color & ")"
End If
End Sub
</script>
<asp:Panel ID="panel" OnLoad="Panel_OnLoad" runat="server">
this is a car<br />
color = <asp:Literal ID="lit_color" runat="server"></asp:Literal><br />
<br />
</asp:Panel>
Here is my ASP web page in file "cars.aspx" which use .vb code for events, ...
<%# Page Language="vb" Explicit="true" Inherits="PageBase" Src="/code/cars.vb"%>
<html>
<body>
<!-- Html code here --->
<asp:panel ID="panel_cars" runat="server">
</asp:panel>
</body>
And here is my .vb code in file "cars.vb"
Private Sub CreateCar()
Dim car As Car = new Car()
Dim control As ControlCar = Nothing
control= CType(LoadControl("/code/controlcar.ascx"), ControlCar)
control.Car = car
panel_cars.Controls.Add(control)
End Sub
But it fails, saying 'ControlCar' is unrecognized in cars.vb.
I know it works, if move .vb code in .aspx file and using directive
<%# Register TagPrefix="uc" TagName="ControlCar" Src="/code/controlcar.ascx" %>
But I need to separate .vb code and .aspx code like in my example.
How can I make recognizing 'ControlCar' type (defined in .ascx) in .vb file?

You need to add some kind of reference. You can use a reference directive:
<%# Reference Control="controlcar.ascx" %>.
The control is being dynamically compiled. If you have no reference, the compiler has no idea what you are trying to use.
EDIT:
You may be able to use:
Imports ASP.controlcar_ascx
If the reference must be in the code behind. I have had issues with this in the past though.

Related

How to databind a local variable in an ASPX template?

I have a UserControl named SlidingScaleTableReadonly with a bindable public member ScaleEntries, which take a List of struct instances. In the parent page, these lists are stored in a dictionary (InsulinSlidingScaleProtocolEntries), and I must render one SlidingScaleTableReadonly for each entry in this dictionary. Here's my template code so far:
<%# Register TagPrefix="uc" TagName="SlidingScaleTableReadonly" Src="~/Patient/SlidingScaleTableReadonly.ascx" %>
<asp:Content ContentPlaceHolderID="phContent" runat="server">
<h1>Échelles</h1>
<% For Each drug In InsulinSlidingScaleProtocolEntries %>
<h2><%= drug.Key %></h2>
<uc:SlidingScaleTableReadonly runat="server" ScaleEntries="<%# drug.Value %>" />
<% Next %>
</asp:Content>
My problem is at line 7:
BC30451 'drug' is not declared. It may be inaccessible due to its protection level.
If I replace drug.Value with a property available in code behind, the new variable can be found. I'm guessing it's not possible to directly data-bind local variables in ASPX? If that's the case, how can I use data-binding inside an iteration?
Edit - Changing the provenance of the bound value
I've changed the code a bit. Right after the "For Each" loop opening, a code-behind property named CurrentDrug is reassigned with the value of drug.Value, and uc:SlidingScaleTableReadonly get it's ScaleEntry from that new property. But that doesn't work either, the reassignment won't do shuck and ScaleEntry is always Nothing (or whatever initial value I gave to CurrentDrug).
Here's the new template:
<%# Register TagPrefix="uc" TagName="SlidingScaleTableReadonly" Src="~/Patient/SlidingScaleTableReadonly.ascx" %>
<asp:Content ContentPlaceHolderID="phContent" runat="server">
<h1>Échelles</h1>
<% For Each drug In InsulinSlidingScaleProtocolEntries
CurrentDrug = drug.Value %>
<h2><%= drug.Key %></h2>
<uc:SlidingScaleTableReadonly runat="server" ScaleEntries="<%# CurrentDrug %>" />
<% Next %>
</asp:Content>
And code-behind:
Public CurrentDrug As List(Of ScaleEntry)
Public Property InsulinSlidingScaleProtocolEntries As Dictionary(Of String, List(Of ScaleEntry))
'...
End Property
Edit - (Not) a solution: Stopped using aspx templates
I have to move on, and it seems like I'm not going to do any progress with aspx templates. I've rewritten the page so the UserControl is declared and inserted in code-behind. I think that's not how one should build an interface, that's not where that kind of logic should be place but I really want to move on.
Here's the new template code:
<asp:Content ContentPlaceHolderID="phContent" runat="server">
<h1>Échelles</h1>
<div id="scaleContainer" runat="server"></div>
</asp:Content>
Code-behind:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
For Each prescription In InsulinSlidingScaleProtocolEntries
Dim label As New HtmlGenericControl("h2") With {
.InnerText = prescription.Key
}
Dim table = CType(Page.LoadControl("~/Patient/SlidingScaleTableReadonly.ascx"), SlidingScaleTableReadonly)
table.ScaleEntries = prescription.Value
scaleContainer.Controls.Add(label)
scaleContainer.Controls.Add(table)
Next
End Sub
If you have a better solution, I'll be happy to see it.

Visual Basic subprocedure issue

I'm working on some .NET code in visual studio web developer and I'm having an issue. When I double click on a button in design view, instead of loading a subprocedure, all it does is highlight this: <asp:Button ID="btnEnter" runat="server" Text="Enter" onclick="btnEnter_Click" />
When I try to run that just to see what happens, I get this error:
Line 8: <asp:Button ID="btnEnter" runat="server"
Compiler Error Message: BC30456: 'btnEnter_Click' is not a member of 'ASP.default_aspx'.
If I erase the onclick="btnEnter_Click" and run it, it works. Either way though, when I double click the button / element, shouldn't it create a subprocedure for me that looks something like?
Protected Sub TextBox1_TextChanged(sender As Object, e As EventArgs)
End Sub
I've tried entering that manaully but the keywords don't turn blue or any sort of color, and when I run it, it just shows up as text on the webform with my other elements. Here is all I have so far:
<%# Page Title="Home Page" Language="VB"%>
<form id="form1" runat="server">
<asp:Label ID="Label1" runat="server" Text="Enter a person's name below"></asp:Label>
<p>
<asp:TextBox ID="txtStudentName" runat="server"></asp:TextBox>
</p>
<p>
<asp:Button ID="btnEnter" runat="server"
Text="Enter" onclick="btnEnter_Click" />
</p>
<p>
<asp:Button ID="btnDisplay" runat="server" Text="Display all and exit" />
</p>
<p>
<asp:Label ID="lbl2" runat="server" Text=" "></asp:Label>
</p>
<p>
<asp:Label ID="lbl3" runat="server" Text=" "></asp:Label>
</p>
<p>
<asp:Label ID="lbl4" runat="server" Text=" "></asp:Label>
</p>
<p>
<asp:Label ID="lbl5" runat="server" Text=" "></asp:Label>
</p>
</form>
Edit:
When I enter my code inside the default.aspx.vb file, it highlights keywords and such, but it can't reference the elements I created in the design view.
Partial Class _Default
Inherits System.Web.UI.Page
End Class
Class Lab1
Protected Sub TextBox1_TextChanged(sender As Object, e As EventArgs)
End Sub
Public Const length As Integer = 3
Shared counter2 As Integer = 0
Public Shared studentList As String() = New String(2) {}
Protected Sub btnEnter_Click(sender As Object, e As EventArgs)
Label1.Text = "Enter a person's name"
Dim studentName As [String] = txtStudentName.Text
If studentList.Length <= length Then
If txtStudentName.Text <> "" Then
Dim match As [Boolean] = True
Dim i As Integer = 0
While counter2 >= i
If studentList(i) IsNot Nothing Then
If studentList(i).ToUpper() = txtStudentName.Text.ToUpper() Then
match = False
Label1.Text = "This name has already been used"
End If
End If
i += 1
End While
If match = True Then
studentList(counter2) = txtStudentName.Text
counter2 += 1
End If
End If
End If
End Sub
End Class
Your Page declaration seems to say you are using inline style:
<%# Page Title="Home Page" Language="VB"%>
Which means your code is/should be within aspx file itself in a script runat="server" tag:
<%# Page Language="VB" %>
<!DOCTYPE html>
<script runat="server">
Protected Sub Button1_Click(sender As Object, e As EventArgs)
'Do something
End Sub
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="Button" />
</div>
</form>
</body>
</html>
However, your other code indicates you seem to want to use code-behind model, where you have .vb files (e.g. foo.aspx.vb)
The Page is declared like this for web sites (for web applications it will say CodeBehind="foo.aspx.vb"):
<%# Page Language="VB" AutoEventWireup="false" CodeFile="foo.aspx.vb" Inherits="foo" %>
You will find a foo.asp.vb file in your project
it's class name will be the name of your file (foo)
your code should be scoped to this class (I'm not sure what Class Lab1 in your post above is for....
foo.aspx.vb:
Partial Class foo
Inherits System.Web.UI.Page
Protected Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'Do something
End Sub
End Class
The "fix" is for you to:
choose which model you want to write code: inline or code-behind
declare the Page appropriately
for inline, your code will be in the aspx file itself
for code-behind, your code will live, and should be encapsulated within the Class file of the page.
The Page declaration actually says so:
CodeFile="foo.aspx.vb" is the file where the code is
Inherits="foo" is the Partial Class in the file (Partial Class foo)
Try this
Private Sub btnEnter_Click(sender As Object, e As System.EventArgs) Handles btnEnter.Click
End Sub
Here, btnEnter_Click is your event name and use the Handles keyword at the end of a procedure declaration to cause it to handle events raised by an object variable

ASP.NET AJAX and Session Variables

I have an ASP.NET (VB.NET) application with 2 pages, a 'main' page and a second 'data-only' page whose only purpose is to be an AJAX data target for the main page, making a database call and rendering the results for a jQuery (AJAX) .get(). I'm using a session variable in the main page that I want to test for the existence of in the data-only page before it makes its DB call and renders the data.
I've tried doing this directly and it fails. From what I've been able to determine so far, the data-only page is unable to detect the session variable until its session is officially started (somehow using session_start, apparently). If this is correct, how do I start a session in the data-only page when it is only accessed via AJAX calls from the main page? I definitely need the data-only page to be session variable-aware. Thanks!
-- Rick
Both pages are ASP.NET. I added a label to the main page to validate (on page_load and on submit of the session value) that the session variable exists and what it is. The data_only page returns a yes or no message (it's always no) if it detects the presense of the session variable.
Page Code - main.aspx:
<%# Page Language="VB" AutoEventWireup="false" CodeFile="main.aspx.vb" Inherits="main" %>
<!DOCTYPE html>
<html>
<head runat="server">
<title></title>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
</head>
<body>
<form id="form1" runat="server">
<asp:TextBox ID="txt_1" runat="server"></asp:TextBox>
<asp:Button ID="but_1" runat="server" Text="Add Session Variable" /><br />
<asp:Label ID="lbl_1" runat="server"></asp:Label><br /><br />
<asp:Button ID="but_2" runat="server" Text="Get Data" />
<asp:Label ID="lbl_2" runat="server"></asp:Label>
</form>
<script type="text/javascript">
$(document).ready(function () {
$('#but_2').on('click', function (event) {
event.preventDefault();
$.get("data_only.aspx", function (data) {
$('#lbl_2').text(data);
});
});
});
</script>
</body>
</html>
Code-Behind - main.aspx:
Partial Class main
Inherits System.Web.UI.Page
Protected Sub Page_Load(sender As Object, e As System.EventArgs) Handles Me.Load
Call Check_Session()
End Sub
Protected Sub but_1_Click(sender As Object, e As System.EventArgs) Handles but_1.Click
Session("var1") = txt_1.Text
Call Check_Session()
End Sub
Private Sub Check_Session()
Dim strSession = Session("var1")
If strSession Is Nothing Then
lbl_1.Text = "No Session variable."
Else
lbl_1.Text = "Session Variable = " & strSession
End If
End Sub
End Class
Page Code - data_only.aspx:
<%# Page Language="VB" AutoEventWireup="false" CodeFile="data_only.aspx.vb" Inherits="data_only" %>
<!DOCTYPE html>
<html>
<head runat="server">
<title></title>
</head>
<body></body>
</html>
Code-Behind - data_only.aspx:
Partial Class data_only
Inherits System.Web.UI.Page
Protected Sub Page_Load(sender As Object, e As System.EventArgs) Handles Me.Load
Dim strSession = Session("var1")
If strSession Is Nothing Then
Response.Write("No session variable.")
Else
' Database call occurs here
Response.Write("Success! Get data here.")
End If
Response.End()
End Sub
End Class
Maybe you could try using a "master class" for both pages i.e both pages inherit from your master class (which in turn from Inherits System.Web.UI.Page) that has all the session handling logic.
Just to clarify and narrow the scope, all .aspx pages are session aware by default and I'm pretty sure that this is not your problem.
First of all make sure that you are using the correct url for the GET call from Ajax, and you can make sure of that by using Chrome developer tools (Network tab) and observer the exact url that Ajax calls. Maybe you need to add "/" before your page url or you need to specify the folder name if it's not in the same folder, like: "/otherFolder/page.aspx".

Nesting ASP.NET user controls programmatically

I have a user control that programmatically includes a second user control several times, but the end result is that no HTML is generated for the controls at all.
Default.aspx
<%# Page Language="vb" AutoEventWireup="false" CodeBehind="Default.aspx.vb" Inherits="TestApplication._Default" %>
<%# Register TagName="Ten" TagPrefix="me" Src="~/Ten.ascx" %>
<html>
<head runat="server"></head>
<body>
<form id="form1" runat="server">
<me:Ten ID="thisTen" runat="server" />
</form>
</body>
</html>
Ten.ascx
<%# Control Language="vb" AutoEventWireup="false" CodeBehind="Ten.ascx.vb" Inherits="TestApplication.Ten" %>
<asp:Panel ID="List" runat="server"></asp:Panel>
Ten.ascx.vb
Public Class Ten
Inherits System.Web.UI.UserControl
Protected Sub Page_Init() Handles Me.Init
For I As Integer = 0 To 11
List.Controls.Add(New One(I.ToString))
Next
End Sub
End Class
One.ascx
<%# Control Language="vb" AutoEventWireup="false" CodeBehind="One.ascx.vb" Inherits="TestApplication.One" %>
<asp:Button ID="OneButton" Text="Press ME!" runat="server" />
One.ascx.vb
Public Class One
Inherits System.Web.UI.UserControl
Private _number As String
Sub New(ByVal number As String)
_number = number
End Sub
Protected Sub OneButton_Click(ByVal sender As Object, ByVal e As EventArgs) Handles OneButton.Click
Dim script As String = "<script type=""text/javascript"">" +
"alert('Button " + _number + "');" +
"</script>"
ScriptManager.RegisterStartupScript(Me, Me.GetType(), "ServerControlScript", script, True)
End Sub
End Class
Edit:
Using load control (Ten.aspx)
Dim p() As String = {I.ToString}
Dim o As One = Me.LoadControl(New One("").GetType, p)
List.Controls.Add(o)
Edit 2:
Dim o As One = Me.LoadControl("~/One.ascx")
o._number = I.ToString
List.Controls.Add(o)
I wouldn't perform this operation in the OnInit event, but this may or may not be related to your issue. Instead, I would load the controls in the OnLoad event.
However, using the new keyword is not typically how user controls are loaded. Sorry, but I only know C# and don't know the exact translation:
In C#:
One one = (One)this.LoadControl("~/Controls/One.ascx");
Does this look right in VB.NET?
Dim one As One = Me.LoadControl("~/Controls/One.ascx")
You'll likely have to delete the constructor and just set the property after you load the control.

Using ASPX Web User Control with Visual Studio 2010

I am trying to implement a Web User Control into one of my APSX pages but keep getting the following warning:
Element 'IntFilter' is not a known element. This can occur if there is a compilation error in the Web site, or the web.config file is missing.
The user control is defined in the same web project as the aspx page.
Question:
How do I resolve this warning (I do not want to move the control to a separate project)?
Also, what do I need to do to enable IntelliSense for this control so I can set its FilterTypeSelection property from ASPX?
Code for "~/FilterControls/IntFilter.ascx"
<%# Control Language="vb" AutoEventWireup="false" CodeBehind="IntFilter.ascx.vb" Inherits="StaffSupport.Filters.IntegerFilter" %>
<asp:DropDownList ID="typeFilterDropDownList" runat="server">
<asp:ListItem Selected="True" Text ="Any" Value="-1" />
<asp:ListItem Selected="False" Text ="Equal" Value= "0" />
</asp:DropDownList><br />
<asp:TextBox ID="TextBox1" runat="server" /><asp:CheckBox ID="CheckBox1" runat="server" Text="Inclusive" /><br />
<asp:TextBox ID="TextBox2" runat="server" /><asp:CheckBox ID="CheckBox2" runat="server" Text="Inclusive" /><br />
Code for "~/FilterControls/IntFilter.ascx.vb"
Namespace Filters
Public Class IntegerFilter
Inherits System.Web.UI.UserControl
Public Enum NumberFilterTypes As SByte
Any = -1
Equals = 0
End Enum
Public Property FilterTypeSelection As NumberFilterTypes
Get
Dim value As SByte
If Not Integer.TryParse(typeFilterDropDownList.SelectedValue, value) Then
value = -1
End If
Return CType(value, NumberFilterTypes)
End Get
Set(value As NumberFilterTypes)
typeFilterDropDownList.SelectedValue = CSByte(value)
End Set
End Property
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
End Sub
End Class
End Namespace
Code for "OpenCases.aspx"
<%# Page Title="" Language="vb" AutoEventWireup="false" MasterPageFile="~/StaffSite.Master" CodeBehind="OpenCases.aspx.vb" Inherits="StaffSupport.OpenCases" %>
<%# Register TagPrefix="filters" TagName="IntFilter" src="~/FilterControls/IntFilter.ascx" %>
<asp:Content ID="bodyContent" ContentPlaceHolderID="cphBody" runat="server">
ID<br />
<filters:IntFilter ID="IntFilter1" runat="server" />
</asp:Content>
Code for "OpenCases.aspx.vb"
Public Class OpenCases
Inherits System.Web.UI.Page
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Page.ViewStateMode = UI.ViewStateMode.Disabled
End Sub
Update 2012/02/21:
Fixed the "filters" vs "filter" miss match.
Also of note, if you drag the control from the Solution Explorer to the page in Design view it will add the references you need (though it was still generating the warning for me). If you drag it to the page in source view it will add an a tag with a href to the element.
Update 2012/02/21 b:
Found the solution, see my answer below.
Apparently you have to reference both the ASCX page and the assembly.
If you drag the ASCX page from the "Solution Explorer" window to the Design view for the page you are editing it will add the reference for the ASCX page, but you will have to add the assembly reference manually.
OpenCases.aspx
<%# Page Title="" Language="vb" AutoEventWireup="false" MasterPageFile="~/StaffSite.Master" CodeBehind="OpenCases.aspx.vb" Inherits="StaffSupport.OpenCases" %>
<%# Register Assembly="StaffSupport" Namespace="StaffSupport.Filters" TagPrefix="filters" %><%-- Assembly Reference --%>
<%# Register TagPrefix="filters" TagName="IntFilter" src="~/FilterControls/IntFilter.ascx" %>
<asp:Content ID="bodyContent" ContentPlaceHolderID="cphBody" runat="server">
ID<br />
<filters:IntFilter ID="IntFilter1" runat="server" />
</asp:Content>
Note: beware of object type collisions. For example the following would also work:
<%# Page Title="" Language="vb" AutoEventWireup="false" MasterPageFile="~/StaffSite.Master" CodeBehind="OpenCases.aspx.vb" Inherits="StaffSupport.OpenCases" %>
<%# Register Assembly="StaffSupport" Namespace="StaffSupport.Filters" TagPrefix="pre1" %><%-- Assembly Reference --%>
<%# Register TagPrefix="pre2" TagName="IntFilter" src="~/FilterControls/IntFilter.ascx" %>
<asp:Content ID="bodyContent" ContentPlaceHolderID="cphBody" runat="server">
ID<br />
<pre1:IntFilter ID="IntFilter1" runat="server" />
</asp:Content>
This is why it started working for me Friday after I posted this, I had added a custom control which implemented System.Web.UI.WebControls.TextBox so I could drag and drop it from the Toolbox. Since it was in the same namespace the control added the assembly reference when it added the control to the page.
Note: if you are referencing dll files which are contained in your project then you may need to remove the page registrations, build, then add the page registrations back. Otherwise the compiler may complain that the dll files are not in the bin.
Update: 2013/04/18
It appears you only need to add the assembly reference if the UserControl is not defined in the same namespace.
If the parrent is defined in Proj.Presentation and the UserControl is defined in Proj.Presentation then you should not need the assembly reference.
If the parrent is defined in Proj.Page and the UserControl is defined in Proj.Page.UserControl then you should not need the assembly reference.
If the parrent is defined in Proj.Page and the UserControl is defined in Proj.UserControl then you need the assembly reference.
The control is declared as:
<%# Register TagPrefix="filters"
and in the markup
<filter:IntFilter
These must match.
You are registering a different prefix from the one you're attempting to use.
You can either change this:
<filter:IntFilter ID="IntFilter1" runat="server" />
to this:
<filters:IntFilter ID="IntFilter1" runat="server" />
Or change this:
<%# Register TagPrefix="filters" TagName="IntFilter"
to this:
<%# Register TagPrefix="filter" TagName="IntFilter"
Close Visual Studio, delete the schema cache, and re-open Visual Studio. You can find the schemas under something like:
C:\Users\Pavel\AppData\Roaming\Microsoft\VisualStudio\10.0\ReflectedSchemas
It is safe to delete all files in this folder.
Delete the contents of that above folder, and all is well.

Resources