I just recently became involved with .Net. In fact, so recently, it was about 2 and a half weeks ago in coding terms.
Dreamer that I am, I had promised myself never to deal with MS products again; if I could help it; so help me (insert deity here); but the marketplace dictates the means, the money, and how much you can buy for your nearest and dearest for the festive season. So, a big contract came up. It was .Net, and that was that.
I started out hating everything. I have been developing websites in PHP for the last two and a half years, and I was thoroughly enjoying myself. I was therefore determined to deal with the big, corporate offering, but to disdain it as less than dog poo on my worthy soul.
I was wrong. I was more than wrong. I was one of those daft rich people who “ooh” and “aah” at a modern art gallery about a block of granite titled, imaginatively “A Block of Granite”, or an unmade bed. I was a code snob. So caught up in the revolution of the righteous that I could not see a good thing for what it was.
.Net is really good as a framework. It’s delicious actually. For a brief moment, forget that it comes from the Devil, and appreciate the beauty of the creation..just for a tiny bit.
I’m not one to approach a new coding system lightly. I’ve been in the game too long to take the easy way out. I’m cranky and opinionated; have rules. Not for me the simple coding of an HTML form, no; I must be able to delve into the bowels of the beast; extract its very essence, and make it a slave to my will. Sounds looney yes, but you code for fifteen years and come back to me :)
So. I tried the difficult stuff straight off. I wanted to create one single class that would populate x number of pages, based on a wizard-type scenario. I did not want to create page1.aspx, page2,apx…ad infinitum, but have one page only that would create all of the HTML elements on the page, as well as the form, and various buttons – with related click events – and all in code. Nothing much to ask really ;)
Right. So How to do this?
- Create a page class that inherits from System.Web.UI.Page.
- Import the following :
Imports System.Web.UI Imports System.Web.UI.HtmlControls Imports System.Web.UI.WebControls
(and anything else you need beyond that, such as Imports System.Data.SqlClient if you want to build your page on a database result. I created a class that inherits from this base class so that I could create methodology to connect to the database, read from it, create a command object, create parameters etc. I used only the SqlDataReader as it is apparently about 30% faster than other methods, and I have a high-volume site to maintain)
- Create one aspx file. Take out everything except for the <%@Page...%> directive.
- In the code-behind file of your aspx file change the Inherits from System.Web.UI.Page to
- Create an OnInit Event in your page class that overrides the Page OnInit event. This is very important. Page_load is not going to do it if you want to set any values prior to loading the page.
- In your code-behind class remember to do anything you need to do prior to the OnInit event of your page class here.
In my case I needed to tell my page class which page it had to load based on the value of _WhichPage in my page class. After much muttering, and fooling about, and then emailing the freelancers forum, Danny helped to realise that, since my code-behind file inherited from my page class, I could override its OnInit method too! So, to tell my page class which stage it was on was simple :
MyBase.WhichStage = 1 (or whatever) [VB.Net] base.WhichStage = 1 (or whatever) [C#]
So now I could dynamically create a page for any stage in the process, add the form, add controls to the form; even add buttons and their events, dynamically. Not only that, but I could call other classes, say a file upload class, pass them the object I needed, and have them deal with it. I was really excited :)
So, how does this all work? Well, let’s start with the page class first. (All examples are in VB.net. You can convert them into C# as you wish :)
Imports System Imports System.Web.UI Imports System.Web.UI.HtmlControls Imports System.Web.UI.WebControls Public Class MyPage Inherits System.Web.UI.Page End Class
And the aspx Page? :
<%@ Page Language="vb" AutoEventWireup="false" Codebehind="pageName.aspx.vb" Inherits="myNameSpace.pageName"%>
That’s all that goes into the file.
Now, pageName.aspx.vb :
Public Class PageName Inherits myNameSpace.MyPage Protected Override Sub OnInit(ByVal e as System.EventArgs) MyBase.WhichLevel = x (whatever it is) End Sub End Class
Okay, so what about MyPage?
Private _WhichStage as Integer Public Property WhichStage() As Integer Get Return _whichStage End Get Set(ByVal Value As Integer) _whichStage = Value End Set End Property Protected Sub OnInit(ByVal e as System.EventArgs) BuildPage(GenerateHTMLForm()) MyBase.OnInit(e) End Sub Protected Sub BuildPage(ByVal oForm as HtmlForm) Me.Controls.Add(oForm) End Sub
This is very simplistic, and does not take into account header and footer HTML, but you get the picture.
Private Function GenerateHTMLForm() as HTMLForm Dim oForm as New HTMLForm AddControlsForStage(oForm) AddControlsDerivedFromPage(oForm) 'just in case :) End Function Public Sub AddControlsForStage(ByRef oForm as HtmlForm) Select Case _WhichStage Case 1 Call something Case 2 Call something else Case Else do something End Select End Sub Private Sub AddControlsDerivedFromPage(ByRef oForm As HtmlForm) Dim cCount As Integer Dim i As Integer Dim ctrl As System.Web.UI.Control cCount = Me.Controls.Count For i = 0 To cCount - 1 ctrl = Me.Controls(i) oForm.Controls.Add(ctrl) Me.Controls.Remove(ctrl) Next i End Sub
Right. That’s the start. Next time I will show you how to create the controls dynamically, including buttons and their events, and how to pass a file upload input to another class to deal with, outside of the page.
As usual, you can contact me at caz[at]daybreaksolutions.com.
Everything’s Eventual, but some things are more Eventual than others
Now, there I was, thinking I had a handle on this .Net Stuff after two weeks. “Gah!” I say to you, and “Gah” again. One thing I had not reckoned with, for I was set in the ways of Programming aft these many fifteen years or so, was the event model that .Net processes when a page loads.
Okay folks; things to remember here :
- Init(), and Load() Happen before anything else [Init() happens before Load()]
- .Net Forms prefer themselves as postees above all else
- Events on form controls happen *after* the form has loaded
- In .Net there is one Form to rule them all, and one form only.
So, what does that mean to the “form created on the fly” model that I previously espoused? It means that we have to be very careful with what we do, that’s what it means. If we have three buttons on the page, that all do different things, we need to make sure that we can either, handle their events after Init() or Load() properly..or we have to cheat.
.Net Buttons are server side controls. They generally post, and if posting, post back to the same form they came from (as .Net likes one Form to Rule Them All). You can handle their events after the Load() by attaching event handlers to every button, but if what you need to do falls within Page Load you have a problem. When I first encountered this issue (I had multiple buttons created OnInit(), dynamically within a table) I went to various forums to check out the lie of the land, and the only thing I found was “you can’t do it”
Now, “can’t” is not a word in my vocab – not when it comes to code. There is *always* a way, might not be pretty, but it can be done…
Just wanted to add a comment. I see this work as genius. But I can’t figure out how to do it. Had I wrote it I would have included a sample of each entire page. I don’t know if the author put this code in a single aspx file or used the asp.net ide or what. So I had to tackle this same problem using a differnet method…
Asp( not .net) page opens a database to find a discription of the fields that it should generate dumps the following asp.net code to screen to be copied to aspt.net ide
Dim frmSearch as new HTMLForm frmSearch = Me.Page.FindControl("Form1") '<----- frmSearch.ID = "frmSearch" frmSearch.Method="post" Dim textBox1 as new TextBox textBox1.id = "textbox1" placeholder1.controls.add(textbox1) Controls.Add(frmSearch)
<asp:PlaceHolder id="Placeholder1" runat="server"></asp:PlaceHolder>;
That’s it no need to inherit or change the class of anything, simple and it works
allows the separation of the image and coding that asp.net was willing to sell its soul for