by Nik Kalyani
Saturday, April 30, 2005 6:37:28 AM (Pacific Standard Time, UTC-08:00)
Order of events in the lifecycle of an ASP.Net page:
| Phase |
What a control needs to do |
Method or event to override |
| Initialize |
Initialize settings needed during the lifetime of the incoming Web request. |
Init event (OnInit method) |
| Load view state |
At the end of this phase, the ViewState property of a control is automatically populated as described in Maintaining State in a Control. A control can override the default implementation of the LoadViewState method to customize state restoration. |
LoadViewState method |
| Process postback data |
Process incoming form data and update properties accordingly. |
LoadPostData method (if IPostBackDataHandler is implemented) |
| Load |
Perform actions common to all requests, such as setting up a database query. At this point, server controls in the tree are created and initialized, the state is restored, and form controls reflect client-side data. |
Load event
(OnLoad method) |
| Send postback change notifications |
Raise change events in response to state changes between the current and previous postbacks. |
RaisePostDataChangedEvent method (if IPostBackDataHandler is implemented) |
| Handle postback events |
Handle the client-side event that caused the postback and raise appropriate events on the server. |
RaisePostBackEvent method(if IPostBackEventHandler is implemented) |
| Prerender |
Perform any updates before the output is rendered. Any changes made to the state of the control in the prerender phase can be saved, while changes made in the rendering phase are lost. |
PreRender event (OnPreRender method) |
| Save state |
The ViewState property of a control is automatically persisted to a string object after this stage. This string object is sent to the client and back as a hidden variable. For improving efficiency, a control can override the SaveViewState method to modify the ViewState property. |
SaveViewState method |
| Render |
Generate output to be rendered to the client. |
Render method |
| Dispose |
Perform any final cleanup before the control is torn down. References to expensive resources such as database connections must be released in this phase. |
Dispose method |
| Unload |
Perform any final cleanup before the control is torn down. Control authors generally perform cleanup in Dispose and do not handle this event. |
UnLoad event (On UnLoad method) |
Note To override an EventName event, override the OnEventName method (and call base. OnEventName).
by Nik Kalyani
Monday, April 25, 2005 12:43:46 AM (Pacific Standard Time, UTC-08:00)
Dharmesh Shah asks:
I’m exploring the use of third-party controls (like Infragistics, ComponentArt, etc.) within my custom modules. My first round of experimentation with Infragistics seems to indicate a problem because of some of the Javascript and other stuff the control tries to insert. Do you have any tips or experience in using these types of controls inside a DNN module?
Nik’s response:
This is an issue that is going to plague DNN developers for some time to come. To understand why, let’s examine how components use Javascript.
Generally speaking, a third-party component will inject JS into your page in one of two ways: (1) by having a <script src=”…”> tag that links to a .js file, or (2) by injecting the script in-line using a <script> tag without the “src” attribute. The former is more efficient because it can benefit from browser caching. It is used for generic code and not for instance-specific initialization etc. The latter is necessary because it allows server-side code to create script on-the-fly. For example, in order to render the portal menu, the data for the menus is rendered on-the-fly.
Most components are written for ASP.Net applications. Your average ASP.Net application makes use of usercontrols for “shared code” to avoid repeatedly coding things such as headers, footers, feedback forms etc. Embedding a third-party component into these usercontrols is not a problem because it is rare that the same usercontrol is used multiple times on a page. In the case of DNN, usercontrols manifest themselves as “modules” and as any DNN user knows, a module may appear any number of times on a page. This is where the problem begins.
Forward-thinking component developers have envisioned this scenario and taken measures necessary to ensure the uniqueness of dynamically injected variables and objects in code. If a component developer has not done this, it’s game over when you have multiple instances of the component on the same page since the initialization code will reference only the last instance (if it even works). For example, let’s say we have an image with an ID of “MyImage” and there is a variable tracking the position of this image. The (unoptimized) code injected might look something like this:
var imgX = document.getElementById(“MyImage”).left; var imgY = document.getElementById(“MyImage”).top;
If this code appears in a DNN module that is instantiated four times on a page, imgX and imgY will be set to the last occurrence of the code. Any further U.I. manipulation by JS will throw errors or produce generally erratic behavior.
This is the first thing to look for in a third-party component that uses client-side JS. Does it have the ability to be instantiated multiple times on the same page. The test scenario for this is easy. Download the trial version and modify one of the samples so two instances of the component are on the same page (do give them different ID’s). Does it work? If it seems to be working, double-check the page source. Look for Javascript variables and ensure that variables have a unique prefix or suffix. In most cases this will be driven by the ClientID property of the usercontrol as this is the Microsoft-recommended property to use for client-side identiciation of server-side controls.
I wish I could say that there was an easy workaround if the component does not readily allow multi-instance capability. There just isn’t. Your best bet is to contact the developer and make them aware of this problem. They may have a workaround or might make the change for you.
Another problem area is with initialization. A number of components will attach a handler to the body.onLoad() cleint-side event of a page to perform initialization. The problem occurs when the component is not well-behaved and or just plain dumb. In this case, it will either over-write an existing handler defined by some other component or even DNN itself, or just append multiple, duplicate handlers for multiple instances of a module. Again, this is something to verify through the page source.
The ID’s issued to dynamically generated U.I. objects is another problem area. Like variables and code objects, it is imperative that these U.I. objects be given unique ID’s otherwise when code tries to manipulate the object, it will fail.
The final and most perplexing problem is one of cross-compatibility. A component might do everything right, but if it sets a particular U.I. property to a certain value, and another module on the page emits script that also manipulates the same U.I. property, the result is disastrous. In this case, if changing code is not an option, the only solution is to not have the modules on the same page.
I hope this information will be helpful in figuring out which components are suitable for DNN and which ones aren’t.
by Nik Kalyani
Sunday, April 24, 2005 11:52:29 PM (Pacific Standard Time, UTC-08:00)
SnowCovered.com is the largest distributor of commercial third-party modules for DotNetNuke. If you are a DNN module developer and want to sell your product, this is the place to post it for sale. If you are a DNN end-user looking to buy a module, this is the place to buy it.
In recent months, more and more users post on the DNN forums complaining about some of the junk people pass off as modules on SnowCovered. As a result, there is a growing negative perception about the site. As a developer of commercial modules and also an end-user, I have many SnowCovered transactions and have a fairly strong opinion on the matter.
I would like to start by addressing the issue of module quality. This is simply not the responsibility of a distributor, especially when the terms are clearly spelled out for the buyer. Nor is it the responsibility of the distributor to judge what is good and what is crap. Do you go down to your local Wal-Mart and complain that they have crappy products along with good ones? Of course not, you just avoid the products that don’t meet your expectations. Why is this any different? Quite frankly, if you buy a module on SnowCovered without performing due diligence on the publisher, you’re an idiot because you failed to follow the most fundamental tenet of commerce — Caveat emptor!
Buying a module? Note the following:
1) Visit the publisher’s website prior to purchase. Is there a support page? Is there is an online forum with recent activity. Are there working product demos available? These are such basic things to look for pre-purchase, but unfortunately, people only do them post-purchase. If someone is selling a product and cannot fork over the $50 it takes to maintain a good website with working demos, you can conclude that they are opportunistic and not really in the software publishing business. Move on…
2) Do a cursory search on the net on the publisher’s name prior to purchase. Even if the publisher’s site is great, if Google is drawing a blank, chances are the publisher is not well-established. You can overlook this only if there are other means by which you can verify the publisher’s credibility.
3) DNN modules are dirt cheap. Spend the extra bucks for the source code. Then it won’t matter if the developer doesn’t publish an update for the latest version of DNN. Sure, it’s work for you if this happens and costs you a little more up front, but at least you are not stranded.
4) No one is going to give you a refund on software. This is a fact of life. Try opening a piece of software and returning it at Best Buy. Fat chance. This is no different.
The next issue I would like to address is the 25% commission. Any post where a developer is whining about this indicates that the developer is generally clueless about the market. Any distributor will charge you a commission of 18–30% for selling your software. If you don’t like this, then sell it yourself. But be prepared to have fairly low sales unless you are willing to put up some marketing dollars. All publishers benefit as a result of the aggregation on a distributor site. If you are not quite well-versed in the business of retail, you will whine about “SnowCovered taking your money.” This is plain and simple, a marketing fee. It is an industry-standard and for any sensible developer, a wise investment.
I launched a store for on my site in November 2004. I decided that Speerio has enough brand recognition and credibility that customers will not have a problem making a purchase directly on my site without a third-party. I was right. Over 85% of my sales now come from customers purchasing directly from the Speerio Online Store. However, I still want to capture business from new DNN users, so I will always continue to have a presence on SnowCovered and Xtras. It is a pain to manage upgrades etc. but in time, these systems will evolve.
One last comment on the issue of commission. The commission does not affect the buyer in any way, i.e. it is not an increased fee levied by the distributor. SnowCovered is actually using incorrect parlance. The industry standard term is “discount.” The publisher discounts their product by 25% so when SnowCovered sells at the MSRP, it gets to keep the difference. The buyer pays the MSRP in most cases unless the distributor has a promotion and gives up part of the discount by selling lower than MSRP.
Now, let’s talk about the SnowCovered Help Desk and FAQ system. Although I think Brice has done a great job of creating a market that was completely non-existent, on this topic, I must ask “What the heck are you thinking?” This is the most insane, dumb and generally speaking, lousy business decision I have seen made in some time. By extending its role from distributor to support provider, SnowCovered is setting itself up to fail in meeting customer expectations. The software for managing help requests and FAQs is a shade above really, really crappy. Every serious developer I know that sells on SnowCovered HATES it with a passion. It is a pain to use and a pain to support. I got so fed-up with it, I put a standard disclaimer on all my new SnowCovered posts…pre– and post-sales questions at SnowCovered will be ignored. Email me directly or visit the Speerio site and post on the forums. It has worked quite well, although there will be the occasional customer who will continue posting there. I wish the SnowCovered help desk and FAQ system would go away.
Now, I hope this post doesn’t give the impression that I really like SnowCovered and am OK with the fact that there is complete crap being passed off as DNN modules on there. I don’t. I hate it, especially because I put in a lot of effort into creating quality products and I hate being shelved with sub-standard products. But this is how a market economy works. Ultimately, the market decides on what is good and what isn’t. If I want the benefit of the visibility SnowCovered has, I have to publish my products on their site. Thankfully, with few exceptions, customers are smart and can tell the difference. So, for the moment, I can live with this.
by Nik Kalyani
Saturday, April 23, 2005 5:31:40 PM (Pacific Standard Time, UTC-08:00)
If you wish to create strong named assemblies in .Net, you need to sign them. This is done using a key that you reference through the project’s AssemblyInfo.cs or AssemblyInfo.vb file. There are three attributes related to signing:
[assembly: AssemblyDelaySign(false)] Used to allow signing of the assembly with only the public key. This allows the assembly to be used during development without having to share the private key.
[assembly: AssemblyKeyFile("")] Used to specify the path to a key file.
[assembly: AssemblyKeyName("")] Used to specify the name of a key to use from the Crypto Service Provider (CSP).
If you are going to sign all your assemblies, then the AssemblyKeyName is the best option as it does not require you to remember a path to the key file each time you create a new project. It’s much easier to remember a simple name associated with the key. Here’s how you would sign an assembly using the CSP:
1) Generate a key pair: sn -k MyCompany.snk
2) Install into the CSP: sn -i MyCompany.snk MYCOMPANY
3) Use in your code: [assembly: AssemblyKeyName(“MYCOMPANY”)]
by Nik Kalyani
Thursday, April 21, 2005 9:33:00 AM (Pacific Standard Time, UTC-08:00)
Just got word from ComponentArt that I am going to be a member of their MVP Program. Quite excited about collaborating with other developers to create cool ComponentArt-based solutions for DotNetNuke.
by Nik Kalyani
Wednesday, April 20, 2005 7:31:38 PM (Pacific Standard Time, UTC-08:00)
There are many different solutions for creating tabbed content in HTML pages. For a number of my apps, I needed a simple, lightweight solution that was easy to use and to integrate with my apps. I came up with a simple solution and am sharing it here. Hopefully someone will find it useful.
The solution consists of a table containing the tabs and DIV’s representing the content for each tab. The tabs table has one cell for each tab with ID of “Tab_n” where n starts with 0 and increases by 1 for each tab. Add an onClick handler for each cell that calls tabClickHandler(n). Finally, create a DIV for each tab’s content and set its ID to be “Panel_n” where n starts with 0 and increases by 1 for each tab. Add a style attribute of “display: none” for all DIVs representing the tab content except for the first, which should be set to “display:block”.
<script language="javascript"> function tabClickHandler(tab) { i = -1; while (true) { i++; objTab = eval(document.getElementById("Tab_" + i));
if (objTab) { objTab.className = (i == tab) ? "Speerio-SelectedTab" : ((i > tab) ? "Speerio-RightTab" : "Speerio-LeftTab"); document.getElementById("Panel_" + i).style.display = (i == tab ? "block" : "none"); } else break; } } </script> <style> .Speerio-Tabs { width:100%; color: black; font-family: Verdana,Arial,Helvetica,Sans Serif} .Speerio-TabLeftSpacer {width:10%; BORDER-BOTTOM: 1px solid black} .Speerio-TabRightSpacer {width:10%; BORDER-BOTTOM: 1px solid black} .Speerio-SelectedTab { width:20%; BORDER-RIGHT: 1px solid black; BORDER-TOP: 1px solid black; BORDER-LEFT: 1px solid black} .Speerio-LeftTab {cursor: hand; width:20%; BORDER-TOP: 1px solid #cccccc; BORDER-LEFT: 1px solid #cccccc; CURSOR: hand; BORDER-BOTTOM: 1px solid black; background-color: #eeeeee} .Speerio-RightTab {cursor: hand; width:20%; BORDER-RIGHT: 1px solid #cccccc; BORDER-TOP: 1px solid #cccccc; BORDER-LEFT: medium none; CURSOR: hand; BORDER-BOTTOM: 1px solid black; background-color: #eeeeee} .Speerio-TabLabel { font-size: 8pt; font-weight: bold; color: black; font-family: Verdana,Arial,Helvetica,Sans Serif} </style> <table cellspacing="0" cellpadding="2" border="0" align="center" class="Speerio-Tabs"> <tr> <td class="Speerio-TabLeftSpacer"> </td> <td id="Tab_0" class="Speerio-SelectedTab" nowrap align="Center" valign="Middle" onClick="tabClickHandler(0)"> <span class="Speerio-TabLabel">ABC</span></td> <td id="Tab_1" class="Speerio-RightTab" nowrap align="Center" valign="Middle" onClick="tabClickHandler(1)"> <span class="Speerio-TabLabel">DEF</span></td> <td id="Tab_2" class="Speerio-RightTab" nowrap align="Center" valign="Middle" onClick="tabClickHandler(2)"> <span class="Speerio-TabLabel">GHI</span></td> <td id="Tab_3" class="Speerio-RightTab" nowrap align="Center" valign="Middle" onClick="tabClickHandler(3)"> <span class="Speerio-TabLabel">JKL</span></td>
<!-- Add as many additional cells corresponding to tabs here --> <td class="Speerio-TabRightSpacer" align="right"> </td> </tr> </table> <!-– Add a DIV for each tab’s content here -->
<div id="Panel_0" style="display:block" width="100%">Contents of the first tab</div> <div id="Panel_1" style="display:none" width="100%">Contents of the second tab</div> <div id="Panel_2" style="display:none" width="100%">Contents of the third tab</div> <div id="Panel_3" style="display:none" width="100%">Contents of the fourth tab</div>
by Nik Kalyani
Wednesday, April 20, 2005 8:46:40 AM (Pacific Standard Time, UTC-08:00)
The Secrets of Strong Naming by Mike Gunderloy -- If you've been working with .NET for any length of time, you've probably run across the concept of a strong name. No, that doesn't mean that your assemblies should have names like MyCompany.Gorilla.Biceps. The strength of a strong name lies in the protection that it offers your assemblies. The .NET Framework uses strong names to identify assemblies and to protect them from tampering. In this article, Mike Gunderloy shows you how strong names are constructed and demonstrates the mechanics of working with strong names in .NET.
by Nik Kalyani
Tuesday, April 19, 2005 4:07:50 PM (Pacific Standard Time, UTC-08:00)
Navin Pathuru asks:
Currently we have a requirement where our client wants us to do something about loading Dnn in Netscape 4.x version. We know Dnn uses a lot of client side javascript so there are going to be problems with this. But we just want to do our best. The approach we want to take is to figure out the browser version and load a different skin one which has really minimal graphics when we detect it is Netscape 4.x. I am wondering if you will be able to write your thoughts about this approach in your blog.
Nik’s response:
Navin, this is a great question. Before I answer it, I do want to point out that most of the client-side Javascript you see on a DNN page is emitted by ASP.Net, the menu and other modules. DNN itself does not generate a lot of client-side script, especially if you disable the Client API.
Now, to answer your question. Since we have the DNN source code, the dime answer is change the core to suit your needs. But this is neither fun nor practical. Let’s go on to the dollar answer.
DNN uses the tab settings (inherited or set directly) to determine which skin is to be displayed. You can override this with querystring parameters to force a certain skin to be displayed. Also, due to its IBuySpy legacy, DNN has the rather unfortunate artifact called the Admin skin which forces a skin switcheroo whenever a non-View module control is displayed. (I think this will go away as part of DNN’s continued evolution.)
So, if there is no way to dynamically switch the skin, how is this problem to be solved. After noodling about this for a minute, it occurred to me. Although DNN allows skins to be intelligent, most skins are dumb, i.e. they intersperse some DNN controls, HTML, images and CSS to render a page layout. Most skins do not take advantage of their ability to do anything ASP.Net allows by virtue of their being usercontrols. This is precisely what we can do to solve the problem at hand.
The solution consists of a single file that I’ll call a “Skin Proxy.” The Skin Proxy can be used to dynamically select a skin based upon any user, portal, tab or browser properties. In my example, I have used a browser property since that was the focus of Navin’s question. Basically, the Skin Proxy creates a string based on some browser properties and then attempts to load a control (i.e. a skin) with a name matching the string.
SkinProxy.ascx
<%@ Control language="c#" %> <script runat="server"> protected void Page_Load(object s, EventArgs e) { string browser = Request.Browser.Browser.ToLower(); string version = Request.Browser.MajorVersion.ToString();
if (Request.QueryString["debug"] != null) Response.Write(browser + version + ".ascx"); else { try { this.Controls.Add(this.LoadControl(browser + version + ".ascx")); } catch { this.Controls.Add(this.LoadControl("Default.ascx")); } } } </script>
In this code, the first couple of lines get the browser and browser major version into string variables. Then, an attempt is made to load a control whose name corresponds to the concatenated browser and version. If this is unsuccessful, a control named “Default.ascx” is loaded. (If you add a querystring variable named “debug” the script reports the name of the control that would be loaded instead of actually loading it. This is helpful for figuring out what to name your control.)
I tested this out with IE and Firefox and everything seemed to work OK. Please do post here if you find problems with the script.
by Nik Kalyani
Monday, April 18, 2005 3:50:26 PM (Pacific Standard Time, UTC-08:00)
I don’t think I will be using Yahoo maps again. Nothing against Yahoo. It’s just that Google maps are so much cooler. There’s nothing like seeing a satellite map of your intended travel route and close-ups of each turn along the way.
Just for fun I mapped the walk from my place to the White House. Be sure to click on the numbers next to each turn instruction.
by Nik Kalyani
Friday, April 15, 2005 8:14:32 PM (Pacific Standard Time, UTC-08:00)
I am continually baffled by how some people are always seeing conspiracies where none exists. The latest debacle was the launch of the DotNetNuke Online Help. Ignoring the fact that my Navigator module is used for delivery, I think it’s a fantastic feature. And for free, to boot. But some people didn’t see it that way judging from the posts in the DotNetNuke forums on ASP.Net. People complained abou tthe fact that the Online Help link went straight to DotNetNuke.com and changing it required a wee bit of work. How awful!!!
I guess people who have concealed from their clients the fact that DNN is what’s driving the $5,000 website they “developed” have a concern that their little secret will be exposed when the client clicks on that View Online Help link and discovers that they’ve been had. What amazed me even more is that these individuals expended more time and energy on complaining than finding a workaround that did not require a re-compile. Setting aside the sheer ludicrousness of people complaining about software created by volunteers, for free, under an amazingly liberal license, one has to wonder about the business ethics of these people. I have lost all respect for the people who expressed such views.
Anyway, I try not to get involved in these asinine arguments, because they are quite pointless. (So please don’t bother posting any comments about the above. It’s my opinion, on my blog and I don’t care to discuss it.) What I do like to do is solve problems, especially technical ones. And this is a nice, juicy problem.
So, if you don’t want to change the link in the database, and you don’t want to recompile, what other option is there. Well, turns out that the link text is localized. So it should be pretty simple…just blank out the text in the language file and you’re good to go. Not quite. If the localization text is blank, the control reverts to displaying the text value that’s defined in script.
Ah, an even more interesting problem. After mulling it over for a bit, I came up with an amazingly simple solution. Here’s what I did:
– Opened ~/controls/App_LocalResources/Help.ascx.resx in Notepad
– Found the line containing the key cmdHelp.Text and changed its value to:
<script language=javascript>this.parentNode.style.display="none"</script>
That did it. If it isn’t already obvious, what the text above does is insert some Javascript as the link value so that the hyperlink is prevented from being displayed.
That was fun.
by Nik Kalyani
Tuesday, April 12, 2005 8:00:35 AM (Pacific Standard Time, UTC-08:00)
Now that the Speerio File Manager Pro module has out for a few days, I have had a chance to experience it as an end-user instead of a developer. My favorite features have become:
1) Folder explorer and drag-and-drop folder moves.
2) Self-extracting zip creation
3) Huge file uploads
4) Private user folders, and
5) Auto-populate user folders with default content.
by Nik Kalyani
Tuesday, April 12, 2005 6:58:05 AM (Pacific Standard Time, UTC-08:00)
I am quite excited. The DotNetNuke Online Help system is now live. Not so much because I have been craving online help for DNN, but more because the underlying module is Speerio Navigator. This module is the complete solution for organizing hirarchical information in DNN. It will soon be released as a free Commuity Edition and a commercial Pro Edition.
by Nik Kalyani
Tuesday, April 05, 2005 12:31:46 AM (Pacific Standard Time, UTC-08:00)
Haven’t blogged in a few days because I have been busy preparing the latest version of one of Speerio’s most popular products File Manager Pro for release. The DotNetNuke module has now been released and soon we’ll release the ASP.Net version of the product. Can’t wait…
|