<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:blogChannel="http://backend.userland.com/blogChannelModule" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:pingback="http://madskills.com/public/xml/rss/module/pingback/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" version="2.0">
  <channel>
    <title>Ben's Quarters</title>
    <description>adventures in application development ...</description>
    <link>http://allben.net/</link>
    <docs>http://www.rssboard.org/rss-specification</docs>
    <generator>BlogEngine.NET 1.5.1.26</generator>
    <language>en-US</language>
    <blogChannel:blogRoll>http://allben.net/opml.axd</blogChannel:blogRoll>
    <dc:creator>My name</dc:creator>
    <dc:title>Ben's Quarters</dc:title>
    <geo:lat>0.000000</geo:lat>
    <geo:long>0.000000</geo:long>
    <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/allben" type="application/rss+xml" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><item>
      <title>App_offline.htm – Page Not Found</title>
      <description>&lt;p&gt;For a few years, .NET has had the built-in capability to easily take your entire application offline when you need to make an update or perform some maintenance on your site.&lt;/p&gt;  &lt;p&gt;By simply putting a file named app_offline.htm in the root directory of your site, ASP.NET will serve the app_offline.htm file, instead of the requested page.&lt;/p&gt;  &lt;p&gt;I recently employed this feature for probably the first time.&amp;#160; I put the app_offline.htm file on the site, and pulled up my site in Firefox.&amp;#160; The contents of app_offline.htm displayed as expected.&lt;/p&gt;  &lt;p&gt;However, if I were to pull my site in Chrome or IE, I would get a Page Not Found error that appeared as though my entire site did not exist.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;App_offline.htm result in IE8:&lt;/strong&gt;&lt;/p&gt;  &lt;p style="text-align: left"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="App_offline.htm in IE" border="0" alt="App_offline.htm in IE" src="http://allben.net/image.axd?picture=app_offline_ie_404.png" width="758" height="382" /&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;App_offline.htm result in Chrome:&lt;/strong&gt;&amp;#160;&lt;/p&gt;  &lt;p style="text-align: left"&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="App_offline in Chrome" border="0" alt="App_offline in Chrome" src="http://allben.net/image.axd?picture=app_offline_chrome_404_1.png" width="764" height="298" /&gt; &lt;/p&gt;  &lt;p&gt;As mentioned above, in Firefox, the contents of App_offline.htm would display as expected.&lt;/p&gt;  &lt;p&gt;The problem is that when ASP.NET serves the App_offline.htm file, the HTTP Response code it passes out is 404.&amp;#160; Chrome will display the page shown above for 404 errors.&amp;#160; In IE, you can actually avoid that generic error page shown above if you turn off HTTP Friendly errors.&lt;/p&gt;  &lt;p&gt;But I obviously cannot expect IE visitors to my site to have HTTP Friendly errors turned off.&lt;/p&gt;  &lt;p&gt;The way ASP.NET has implemented app_offline.htm by passing out a 404 HTTP status code is not well designed, in my opinion.&amp;#160; A much better implementation would be for ASP.NET to return a normal 200 HTTP status code.&lt;/p&gt;  &lt;p&gt;To accomplish this, for this site, I created a simple HTTP Module that processed the beginning of each request.&amp;#160; It checks an “offline” appSetting in web.config to see if the application should be offline.&amp;#160; If the offline setting is turned on, the module will do a server transfer to my own app offline HTML file.&lt;/p&gt;  &lt;p&gt;One thing I found on an IIS7 server is requests for items such as JPG, GIF, CSS files, etc. will also go through this HTTP module.&amp;#160; This is normally a great benefit of IIS7’s integrated mode pipeline.&amp;#160; However, if the application offline HTML file includes an IMG tag for an image on the same site, or a link to a CSS file on the same site, the HTTP module is also going to do a server transfer for these other files (JPG, CSS, etc).&amp;#160; This will result in the image not displaying on the application offline page, or the CSS file not loading in the browser, etc.&lt;/p&gt;  &lt;p&gt;A simple filter in the HTTP module to only do a server transfer for actual pages is all that is required.&amp;#160; The fairly simple HTTP Module I ended up creating is below.&lt;/p&gt;  &lt;div class="showcode"&gt;   &lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System;
&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Collections.Generic;
&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Linq;
&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Web;
&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Configuration;
&lt;span class="kwrd"&gt;using&lt;/span&gt; System.IO;

&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; &lt;span class="type"&gt;AppOffline&lt;/span&gt; : &lt;span class="type"&gt;IHttpModule&lt;/span&gt;
{
    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Dispose()
    {
    }

    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Init(&lt;span class="type"&gt;HttpApplication&lt;/span&gt; context)
    {
        context.BeginRequest += &lt;span class="kwrd"&gt;new&lt;/span&gt; &lt;span class="type"&gt;EventHandler&lt;/span&gt;(context_BeginRequest);
    }

    &lt;span class="kwrd"&gt;void&lt;/span&gt; context_BeginRequest(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, &lt;span class="type"&gt;EventArgs&lt;/span&gt; e)
    {
        &lt;span class="type"&gt;HttpContext&lt;/span&gt; context = ((&lt;span class="type"&gt;HttpApplication&lt;/span&gt;)sender).Context;

        &lt;span class="kwrd"&gt;if&lt;/span&gt; (&lt;span class="type"&gt;ConfigurationManager&lt;/span&gt;.AppSettings[&lt;span class="str"&gt;&amp;quot;offline&amp;quot;&lt;/span&gt;] == &lt;span class="str"&gt;&amp;quot;true&amp;quot;&lt;/span&gt;)
        {
            &lt;span class="kwrd"&gt;string&lt;/span&gt; extension = &lt;span class="type"&gt;Path&lt;/span&gt;.GetExtension(context.Request.Path);

            &lt;span class="rem"&gt;// Don't server transfer for extensions like .JPG, .CSS, etc.&lt;/span&gt;
            &lt;span class="kwrd"&gt;string&lt;/span&gt; targetedExtensions = &lt;span class="str"&gt;&amp;quot;.aspx.ashx.asmx&amp;quot;&lt;/span&gt;;
            &lt;span class="kwrd"&gt;if&lt;/span&gt; (targetedExtensions.IndexOf(extension, &lt;span class="type"&gt;StringComparison&lt;/span&gt;.OrdinalIgnoreCase) == -1)
                &lt;span class="kwrd"&gt;return&lt;/span&gt;;
            
            context.Server.Transfer(&lt;span class="str"&gt;&amp;quot;~/application_offline.html&amp;quot;&lt;/span&gt;);
        }
    }
}&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;It’s a pretty simple, but effective HTTP module.&amp;#160; When the server transfer is done to my own application offline HTML file, the HTTP status code returned to the client is 200.&amp;#160; No more Page Not Found problems with browsers like IE and Chrome.&lt;/p&gt;</description>
      <link>http://allben.net/post/2009/09/05/App_offlinehtm-e28093-Page-Not-Found.aspx</link>
      <author>ben</author>
      <comments>http://allben.net/post/2009/09/05/App_offlinehtm-e28093-Page-Not-Found.aspx#comment</comments>
      <guid>http://allben.net/post.aspx?id=ade5b8ee-960e-4e66-9f0e-4d31d53e762f</guid>
      <pubDate>Sat, 05 Sep 2009 18:11:00 -0700</pubDate>
      <category>Development</category>
      <dc:publisher>ben</dc:publisher>
      <pingback:server>http://allben.net/pingback.axd</pingback:server>
      <pingback:target>http://allben.net/post.aspx?id=ade5b8ee-960e-4e66-9f0e-4d31d53e762f</pingback:target>
      <slash:comments>6</slash:comments>
      <trackback:ping>http://allben.net/trackback.axd?id=ade5b8ee-960e-4e66-9f0e-4d31d53e762f</trackback:ping>
      <wfw:comment>http://allben.net/post/2009/09/05/App_offlinehtm-e28093-Page-Not-Found.aspx#comment</wfw:comment>
      <wfw:commentRss>http://allben.net/syndication.axd?post=ade5b8ee-960e-4e66-9f0e-4d31d53e762f</wfw:commentRss>
    </item>
    <item>
      <title>Performance: Compiled vs. Interpreted Regular Expressions</title>
      <description>&lt;p&gt;When a regular expression in .NET will be used multiple times, it’s common to create that Regex with the Compiled flag, e.g. RegexOptions.Compiled.&amp;#160; Compiled regexp’s take a bit more time to create initially, but will run faster than a regexp created without the Compiled flag.&amp;#160; At least that’s what the documentation states!&lt;/p&gt;  &lt;p&gt;Without the Compiled flag, your regexp will be interpreted.&amp;#160; There’s even &amp;quot;precompiled” regular expressions.&amp;#160; You need to compile these regular expressions into an assembly before runtime.&amp;#160; This might be a good option if you have constant regexps that don’t change.&amp;#160; If your regexps are subject to change, pre-compiled is not a good option.&amp;#160; These three types of regexp’s (interpreted, compiled and pre-compiled) are explained with a few more technical details in &lt;a href="http://blogs.msdn.com/bclteam/archive/2004/11/12/256783.aspx" target="_blank"&gt;this&lt;/a&gt; somewhat dated MS blog article.&lt;/p&gt;  &lt;p&gt;Theory is great, but real benchmarks are more meaningful.&amp;#160; I’ve assembled some code that benchmarks the difference in speed it takes to create and run 5,000 regular expressions.&amp;#160; There’s actually a big difference in the time taken to run a compiled regular expression the first time, versus subsequent times.&amp;#160; So the results shown here will include the first run time as well as the subsequent run times.&lt;/p&gt;  &lt;p&gt;Here’s some code to get us started:&lt;/p&gt;  &lt;div class="showcode"&gt;   &lt;pre class="csharpcode"&gt;    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="type"&gt;List&lt;/span&gt;&amp;lt;&lt;span class="type"&gt;Regex&lt;/span&gt;&amp;gt; _expressions;
    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;object&lt;/span&gt; _SyncRoot = &lt;span class="kwrd"&gt;new&lt;/span&gt; &lt;span class="kwrd"&gt;object&lt;/span&gt;();

    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="type"&gt;List&lt;/span&gt;&amp;lt;&lt;span class="type"&gt;Regex&lt;/span&gt;&amp;gt; GetExpressions()
    {
        &lt;span class="kwrd"&gt;if&lt;/span&gt; (_expressions != &lt;span class="kwrd"&gt;null&lt;/span&gt;)
            &lt;span class="kwrd"&gt;return&lt;/span&gt; _expressions;

        &lt;span class="kwrd"&gt;lock&lt;/span&gt; (_SyncRoot)
        {
            &lt;span class="kwrd"&gt;if&lt;/span&gt; (_expressions == &lt;span class="kwrd"&gt;null&lt;/span&gt;)
            {
                &lt;span class="type"&gt;DateTime&lt;/span&gt; startTime = &lt;span class="type"&gt;DateTime&lt;/span&gt;.Now;

                &lt;span class="type"&gt;List&lt;/span&gt;&amp;lt;&lt;span class="type"&gt;Regex&lt;/span&gt;&amp;gt; tempExpressions = &lt;span class="kwrd"&gt;new&lt;/span&gt; &lt;span class="type"&gt;List&lt;/span&gt;&amp;lt;&lt;span class="type"&gt;Regex&lt;/span&gt;&amp;gt;();
                &lt;span class="kwrd"&gt;string&lt;/span&gt; regExPattern =
                    &lt;span class="str"&gt;@&amp;quot;^[a-zA-Z0-9]+[a-zA-Z0-9._%-]*@{0}$&amp;quot;&lt;/span&gt;;

                &lt;span class="kwrd"&gt;for&lt;/span&gt; (&lt;span class="kwrd"&gt;int&lt;/span&gt; i = 0; i &amp;lt; 5000; i++)
                {
                    tempExpressions.Add(&lt;span class="kwrd"&gt;new&lt;/span&gt; &lt;span class="type"&gt;Regex&lt;/span&gt;(
                        &lt;span class="kwrd"&gt;string&lt;/span&gt;.Format(regExPattern,
                        &lt;span class="type"&gt;Regex&lt;/span&gt;.Escape(&lt;span class="str"&gt;&amp;quot;domain&amp;quot;&lt;/span&gt; + i.ToString() + &lt;span class="str"&gt;&amp;quot;.&amp;quot;&lt;/span&gt; +
                        (i % 3 == 0 ? &lt;span class="str"&gt;&amp;quot;.com&amp;quot;&lt;/span&gt; : &lt;span class="str"&gt;&amp;quot;.net&amp;quot;&lt;/span&gt;))),
                        &lt;span class="type"&gt;RegexOptions&lt;/span&gt;.IgnoreCase | &lt;span class="type"&gt;RegexOptions&lt;/span&gt;.Compiled));
                }

                _expressions = &lt;span class="kwrd"&gt;new&lt;/span&gt; &lt;span class="type"&gt;List&lt;/span&gt;&amp;lt;&lt;span class="type"&gt;Regex&lt;/span&gt;&amp;gt;(tempExpressions);
                &lt;span class="type"&gt;DateTime&lt;/span&gt; endTime = &lt;span class="type"&gt;DateTime&lt;/span&gt;.Now;
                &lt;span class="kwrd"&gt;double&lt;/span&gt; msTaken = endTime.Subtract(startTime).TotalMilliseconds;
            }
        }

        &lt;span class="kwrd"&gt;return&lt;/span&gt; _expressions;
    }&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;We’re storing 5,000 regular expressions in a static list.&amp;#160; Notice the RegexOptions.Compiled flag is being used.&amp;#160; The regexp’s are just looking for email addresses with specific domain names – domain1.net, domain2.net, domain3.com, etc.&amp;#160; Not very useful, but I just wanted the regexp’s to vary.&amp;#160; You can see we’re also recording the number of milliseconds taken to create the regular expressions.&amp;#160; Now here’s the code that calls GetExpressions() and actually invokes the IsMatch function on each regexp.&lt;/p&gt;

&lt;div class="showcode"&gt;
  &lt;pre class="csharpcode"&gt;    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; CheckForMatches(&lt;span class="kwrd"&gt;string&lt;/span&gt; text)
    {
        &lt;span class="type"&gt;List&lt;/span&gt;&amp;lt;&lt;span class="type"&gt;Regex&lt;/span&gt;&amp;gt; expressions = GetExpressions();
        &lt;span class="type"&gt;DateTime&lt;/span&gt; startTime = &lt;span class="type"&gt;DateTime&lt;/span&gt;.Now;

        &lt;span class="kwrd"&gt;foreach&lt;/span&gt; (&lt;span class="type"&gt;Regex&lt;/span&gt; e &lt;span class="kwrd"&gt;in&lt;/span&gt; expressions)
        {
            &lt;span class="kwrd"&gt;bool&lt;/span&gt; isMatch = e.IsMatch(text);
        }

        &lt;span class="type"&gt;DateTime&lt;/span&gt; endTime = &lt;span class="type"&gt;DateTime&lt;/span&gt;.Now;
        &lt;span class="kwrd"&gt;double&lt;/span&gt; msTaken = endTime.Subtract(startTime).TotalMilliseconds;
    }&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;And we call CheckForMatches like so:&lt;/p&gt;

&lt;div class="showcode"&gt;
  &lt;pre class="csharpcode"&gt;    CheckForMatches(&lt;span class="str"&gt;&amp;quot;some random text with email address, address@domain200.com&amp;quot;&lt;/span&gt;);&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;How much time does it take to create and run these 5,000 compiled expressions?&amp;#160; Here’s what I get:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;font color="#0000ff"&gt;Compiled Regular Expressions&lt;/font&gt;&lt;/strong&gt; 

  &lt;br /&gt;&lt;strong&gt;CREATION TIME:&lt;/strong&gt; 1662 ms 

  &lt;br /&gt;&lt;strong&gt;FIRST RUN TIME:&lt;/strong&gt; 25137 ms 

  &lt;br /&gt;&lt;strong&gt;SUBSEQUENT RUN TIMES:&lt;/strong&gt; 41 ms&lt;/p&gt;

&lt;p&gt;Subsequent runs of all 5,000 expressions is very fast.&amp;#160; However, look how much time it takes the first time these 5,000 expressions are run in CheckForMatches() – &lt;u&gt;25 seconds&lt;/u&gt;!!!&lt;/p&gt;

&lt;p&gt;Let’s make ONE change.&amp;#160; Remove the RegexOptions.Compiled flag.&amp;#160; By doing this, our regular expressions will be interpreted.&amp;#160; Here’s what we get:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;font color="#0000ff"&gt;Interpreted Regular Expressions&lt;/font&gt; 

    &lt;br /&gt;CREATION TIME:&lt;/strong&gt; 493 ms 

  &lt;br /&gt;&lt;strong&gt;FIRST RUN TIME:&lt;/strong&gt; 22 ms 

  &lt;br /&gt;&lt;strong&gt;SUBSEQUENT RUN TIMES:&lt;/strong&gt; 20 ms&lt;/p&gt;

&lt;p&gt;Interpreted regexp’s beat compiled in every category!&amp;#160; Running these tests several times produces similar results.&amp;#160; The BIG difference here is obviously the First Run Time.&amp;#160; 25 seconds versus .022 seconds.&lt;/p&gt;

&lt;p&gt;I’ve seen some benchmarks showing static regexp’s performing a little slower than instance regexp’s.&amp;#160; I ran the same tests without the static modifier on the fields and methods above.&amp;#160; Same results – using the Compiled flag takes around 25 seconds for the regular expressions to run the first time.&amp;#160; Without the Compiled flag, they run in hundredths of a second.&lt;/p&gt;

&lt;p&gt;Clearly, interpreted regexps are the winner.&amp;#160; Granted, if you’re only dealing with a small number of regular expressions, and you use the compiled flag, the first run time isn’t going to be anywhere near what I’ve shown here with 5,000 regexps.&amp;#160; However, even with just a few regular expressions, in .NET, you’ll see me sticking with interpreted regular expressions!&lt;/p&gt;</description>
      <link>http://allben.net/post/2009/08/06/Performance-Compiled-vs-Interpreted-Regular-Expressions.aspx</link>
      <author>Ben</author>
      <comments>http://allben.net/post/2009/08/06/Performance-Compiled-vs-Interpreted-Regular-Expressions.aspx#comment</comments>
      <guid>http://allben.net/post.aspx?id=41984cfd-90a9-4a94-b6e7-1dc1c3c3ac76</guid>
      <pubDate>Thu, 06 Aug 2009 22:33:00 -0700</pubDate>
      <category>Development</category>
      <dc:publisher>Ben</dc:publisher>
      <pingback:server>http://allben.net/pingback.axd</pingback:server>
      <pingback:target>http://allben.net/post.aspx?id=41984cfd-90a9-4a94-b6e7-1dc1c3c3ac76</pingback:target>
      <slash:comments>2</slash:comments>
      <trackback:ping>http://allben.net/trackback.axd?id=41984cfd-90a9-4a94-b6e7-1dc1c3c3ac76</trackback:ping>
      <wfw:comment>http://allben.net/post/2009/08/06/Performance-Compiled-vs-Interpreted-Regular-Expressions.aspx#comment</wfw:comment>
      <wfw:commentRss>http://allben.net/syndication.axd?post=41984cfd-90a9-4a94-b6e7-1dc1c3c3ac76</wfw:commentRss>
    </item>
    <item>
      <title>Show the MAX length Requirement Too!</title>
      <description>&lt;p&gt;When creating an account online and you need to enter your desired username and password, it's common for there to be a note regarding the minimum number of characters required for a username/password.&amp;#160; But the maximum character limit is often omitted in this note.&amp;#160; Particularly for passwords, when allowed, I try to make 36 character passwords.&amp;#160; My passwords are just random characters of letters, numbers and special characters.&amp;#160; Every time I spend 30 seconds creating one of these passwords, I always tell myself I need to get one of those random password generators -- but end up never getting one! &lt;/p&gt;  &lt;p&gt;Back to my point -- all sites should be making use of the &amp;quot;maxlength&amp;quot; attribute on a text input element.&amp;#160; Not just for usernames/passwords, but for every piece of data that is accepted through a text input.&amp;#160; At the very very least, indicating the maximum length in a note next to the input field would be appreciated. &lt;/p&gt;  &lt;p&gt;On numerous sites I've registered at (including large sites), there is no note, and there is no maxlength on the input field.&amp;#160; I spend my 30 seconds typing in a 36 character password to find out when I click the Submit button that the password is too long.&amp;#160; In a couple of cases, the site doesn't even tell you the password is too long.&amp;#160; They just report &amp;quot;Invalid Password&amp;quot;.&amp;#160; I'll try shortening it in chunks until they take it. &lt;/p&gt;  &lt;p&gt;This may just be a small user experience point, but adding a maxlength attribute takes no time at all and provides immediate knowledge you've reached the max character limit.&lt;/p&gt;</description>
      <link>http://allben.net/post/2009/07/12/Show-the-MAX-length-Requirement-Too!.aspx</link>
      <author>Ben</author>
      <comments>http://allben.net/post/2009/07/12/Show-the-MAX-length-Requirement-Too!.aspx#comment</comments>
      <guid>http://allben.net/post.aspx?id=ac7b52a2-24de-4a9a-a15e-c7ecf2fb82fd</guid>
      <pubDate>Sun, 12 Jul 2009 02:08:00 -0700</pubDate>
      <category>Opinion</category>
      <dc:publisher>Ben</dc:publisher>
      <pingback:server>http://allben.net/pingback.axd</pingback:server>
      <pingback:target>http://allben.net/post.aspx?id=ac7b52a2-24de-4a9a-a15e-c7ecf2fb82fd</pingback:target>
      <slash:comments>3</slash:comments>
      <trackback:ping>http://allben.net/trackback.axd?id=ac7b52a2-24de-4a9a-a15e-c7ecf2fb82fd</trackback:ping>
      <wfw:comment>http://allben.net/post/2009/07/12/Show-the-MAX-length-Requirement-Too!.aspx#comment</wfw:comment>
      <wfw:commentRss>http://allben.net/syndication.axd?post=ac7b52a2-24de-4a9a-a15e-c7ecf2fb82fd</wfw:commentRss>
    </item>
    <item>
      <title>Logging &amp; Improved Error Reporting in BlogEngine</title>
      <description>&lt;p&gt;There’s two new features in the latest build of BE, 1.5.1.11.&amp;#160; These features, Logging and Improved Error Reporting, are separate but related features.&amp;#160; I think both features will turn out to be very helpful – especially when trying to diagnose a problem.&amp;#160; I’ll explain both features and how they can be used independently of each other, as well as with each other.&lt;/p&gt;  &lt;p&gt;&lt;span style="color: #00f; font-size: 20px"&gt;Logging&lt;/span&gt;&lt;/p&gt;  &lt;p&gt;There’s a new event handler in BE that any extension or other component can subscribe to: &lt;strong&gt;Utils.OnLog&lt;/strong&gt;.&amp;#160; It can be subscribed to in an extension, like:&lt;/p&gt;  &lt;div class="showcode"&gt;   &lt;pre class="csharpcode"&gt;&lt;span class="type"&gt;Utils&lt;/span&gt;.OnLog += &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;&lt;span class="type"&gt;EventArgs&lt;/span&gt;&amp;gt;(OnLog);&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;In this case, there’s an OnLog event handler that will fire every time any piece of code in BE.NET logs a message.&amp;#160; I created a simple Logger extension that is now included in BE 1.5.1.11 that subscribes to log notifications, and writes the log message to a logger.txt file in the App_Data folder.&amp;#160; Anyone can write a similar extension that will save log messages to a database.&amp;#160; The code for the this new Logger extension can be viewed below.&lt;/p&gt;

&lt;p class="toggleCodeLink"&gt;&lt;a onclick="return toggleCode(200906131);" href="#"&gt;Logger Extension 1.0 (view code)&lt;/a&gt;&lt;/p&gt;

&lt;div id="divCode200906131" class="showcode collapseCode"&gt;
  &lt;pre class="csharpcode"&gt;&lt;span class="preproc"&gt;#region&lt;/span&gt; using

&lt;span class="kwrd"&gt;using&lt;/span&gt; System;
&lt;span class="kwrd"&gt;using&lt;/span&gt; BlogEngine.Core;
&lt;span class="kwrd"&gt;using&lt;/span&gt; BlogEngine.Core.Web.Controls;
&lt;span class="kwrd"&gt;using&lt;/span&gt; System.IO;
&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Text;

&lt;span class="preproc"&gt;#endregion&lt;/span&gt;

&lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
&lt;span class="rem"&gt;/// Subscribes to Log events and records the events in a file.&lt;/span&gt;
&lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
[&lt;span class="type"&gt;Extension&lt;/span&gt;(&lt;span class="str"&gt;&amp;quot;Subscribes to Log events and records the events in a file.&amp;quot;&lt;/span&gt;, &lt;span class="str"&gt;&amp;quot;1.0&amp;quot;&lt;/span&gt;, &lt;span class="str"&gt;&amp;quot;BlogEngine.NET&amp;quot;&lt;/span&gt;)]
&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; &lt;span class="type"&gt;Logger&lt;/span&gt;
{
    &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="type"&gt;Logger&lt;/span&gt;()
    {
        &lt;span class="type"&gt;Utils&lt;/span&gt;.OnLog += &lt;span class="kwrd"&gt;new&lt;/span&gt; &lt;span class="type"&gt;EventHandler&lt;/span&gt;&amp;lt;&lt;span class="type"&gt;EventArgs&lt;/span&gt;&amp;gt;(OnLog);
    }

    &lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class="rem"&gt;/// The event handler that is triggered every time there is a log notification.&lt;/span&gt;
    &lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; OnLog(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, &lt;span class="type"&gt;EventArgs&lt;/span&gt; e)
    {
        &lt;span class="kwrd"&gt;if&lt;/span&gt; (sender == &lt;span class="kwrd"&gt;null&lt;/span&gt; || !(sender &lt;span class="kwrd"&gt;is&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt;))
            &lt;span class="kwrd"&gt;return&lt;/span&gt;;

        &lt;span class="kwrd"&gt;string&lt;/span&gt; logMsg = (&lt;span class="kwrd"&gt;string&lt;/span&gt;)sender;

        &lt;span class="kwrd"&gt;if&lt;/span&gt; (&lt;span class="kwrd"&gt;string&lt;/span&gt;.IsNullOrEmpty(logMsg))
            &lt;span class="kwrd"&gt;return&lt;/span&gt;;

        &lt;span class="kwrd"&gt;string&lt;/span&gt; file = GetFileName();

        &lt;span class="type"&gt;StringBuilder&lt;/span&gt; sb = &lt;span class="kwrd"&gt;new&lt;/span&gt; &lt;span class="type"&gt;StringBuilder&lt;/span&gt;();

        &lt;span class="kwrd"&gt;lock&lt;/span&gt; (_SyncRoot)
        {
            &lt;span class="kwrd"&gt;try&lt;/span&gt;
            {
                &lt;span class="kwrd"&gt;using&lt;/span&gt; (&lt;span class="type"&gt;FileStream&lt;/span&gt; fs = &lt;span class="kwrd"&gt;new&lt;/span&gt; &lt;span class="type"&gt;FileStream&lt;/span&gt;(file, &lt;span class="type"&gt;FileMode&lt;/span&gt;.Append))
                {
                    &lt;span class="kwrd"&gt;using&lt;/span&gt; (&lt;span class="type"&gt;StreamWriter&lt;/span&gt; sw = &lt;span class="kwrd"&gt;new&lt;/span&gt; &lt;span class="type"&gt;StreamWriter&lt;/span&gt;(fs))
                    {
                        sw.WriteLine(&lt;span class="str"&gt;@&amp;quot;*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*&amp;quot;&lt;/span&gt;);
                        sw.WriteLine(&lt;span class="str"&gt;&amp;quot;Date: &amp;quot;&lt;/span&gt; + &lt;span class="type"&gt;DateTime&lt;/span&gt;.Now.ToString());
                        sw.WriteLine(&lt;span class="str"&gt;&amp;quot;Contents Below&amp;quot;&lt;/span&gt;);
                        sw.WriteLine(logMsg);

                        sw.Close();
                        fs.Close();
                    }
                }
            }
            &lt;span class="kwrd"&gt;catch&lt;/span&gt;
            {
                &lt;span class="rem"&gt;// Absorb the error.&lt;/span&gt;
            }
        }
    }

    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; _FileName;
    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;object&lt;/span&gt; _SyncRoot = &lt;span class="kwrd"&gt;new&lt;/span&gt; &lt;span class="kwrd"&gt;object&lt;/span&gt;();

    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; GetFileName()
    { 
        &lt;span class="kwrd"&gt;if&lt;/span&gt; (_FileName != &lt;span class="kwrd"&gt;null&lt;/span&gt;)
            &lt;span class="kwrd"&gt;return&lt;/span&gt; _FileName;

        _FileName = System.Web.Hosting.&lt;span class="type"&gt;HostingEnvironment&lt;/span&gt;.MapPath(&lt;span class="type"&gt;Path&lt;/span&gt;.Combine(&lt;span class="type"&gt;BlogSettings&lt;/span&gt;.Instance.StorageLocation, &lt;span class="str"&gt;&amp;quot;logger.txt&amp;quot;&lt;/span&gt;));
        &lt;span class="kwrd"&gt;return&lt;/span&gt; _FileName;
    }
}&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
  &lt;br /&gt;Any code within BE.NET, a widget, extension, etc. can now log any message like:&lt;/p&gt;

&lt;div class="showcode"&gt;
  &lt;pre class="csharpcode"&gt;&lt;span class="type"&gt;Utils&lt;/span&gt;.Log(&lt;span class="str"&gt;&amp;quot;some message to log&amp;quot;&lt;/span&gt;);&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
  &lt;br /&gt;If more than one event handler is subscribed to the OnLog notifications, each event handler will of course fire.&amp;#160; It’s worth noting that Utils.Log() accepts a parameter of type object.&amp;#160; The Logger extension is designed to receive messages of a string type (it actually casts the object type parameter to a string type).&amp;#160; If Logger receives a non-string type, it doesn’t log the message – because the Logger extension is designed to receive simple string-based messages.&amp;#160; If an extension or other piece of code wants to pass a non-string type message to a logger, a different extension could be created that is equipped to handle log messages that are of a type different than string.&lt;/p&gt;

&lt;p&gt;If you don’t want logging to take place, either because you don’t want to worry about having a log file that keeps growing, or because you prefer not to store data in the App_Data folder (as the Logger extension does), you can simply disable the Logger extension on the Extensions tab in the control panel.&amp;#160; As of right now, even if you leave the Logger extension enabled, there’s going to be virtually no messages logged as there isn’t any code that currently calls Utils.Log().&lt;/p&gt;

&lt;p&gt;This new logging feature is going to help keep track of events going on.&amp;#160; Although logging can be used for many purposes, one of the reasons I wanted to have this in BE.NET was to be able to record unhandled errors.&lt;/p&gt;

&lt;p&gt;&lt;span style="color: #00f; font-size: 20px"&gt;Error Handling &amp;amp; Reporting&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;There’s now an event handler in the Global.asax file that catches all unhandled exceptions.&amp;#160; Up till now, if a 500 server error occurred, you would be redirected to error404.aspx, as defined by the &amp;lt;customErrors&amp;gt; tag in the web.config file.&amp;#160; While this is a nice catch-all method to handling errors, people just getting started with BE.NET are often confused why they are seeing a “Page cannot be found” message when they are trying to do something like save changes and an unhandled error occurs -- which they don’t know has occurred when all they see is a “Page cannot be found” message.&amp;#160; To be fair, most people just getting started with BE.NET are not getting errors.&amp;#160; But some do, and adjusting folder permissions or other settings fixes the errors they are seeing.&amp;#160; In order to fix the problem, one must first know an error is actually occurring, and they need to know what the actual error is!&lt;/p&gt;

&lt;p&gt;The new Application_Error event handler in Global.asax does up to three things for non-404 errors:&lt;/p&gt;

&lt;ol class="list"&gt;
  &lt;li&gt;It generates a summary, including details, of the unhandled error that just occurred. &lt;/li&gt;

  &lt;li&gt;If error logging is turned on (a new option, explained below), it makes a call to Utils.Log(), passing the error summary to any event handlers registered to receive log notifications. &lt;/li&gt;

  &lt;li&gt;It does a Server.Transfer() to a new error.aspx page in the root of the BE.NET web folder. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For #1, the summary generated includes a stack trace, inner exceptions, and the URL the page occurred on (both Request.Url and Request.RawUrl).&lt;/p&gt;

&lt;p&gt;The summary that is generated is stored in the Items collection of HttpContext.Current.&amp;#160; Why?&amp;#160; When Application_Error in Global.asax does a Server.Transfer to the new error.aspx page, the error.aspx page checks to see if the person is logged in (i.e. if they’re authenticated).&amp;#160; If they are logged in, error.aspx will display the error summary generated within Global.asax.&amp;#160; The error summary is retrieved out of the HttpContext.Current.Items collection.&amp;#160; If the person isn’t logged in, they just see a message similar to error404.aspx, indicating an “unexpected error has occurred”, and the developer will be tortured, blah blah. :-)&lt;/p&gt;

&lt;p&gt;Displaying the error details directly on error.aspx for logged in users is helpful for two reasons.&amp;#160; (a) Immediate knowledge of the error, and (b) even if the new error logging option is turned off, you still can see the error message in your browser when you’re transferred to error.aspx.&lt;/p&gt;

&lt;p&gt;I’ve mentioned this new error logging option twice now.&amp;#160; On the Settings tab in the control panel, in the Advanced Settings section, there is a new checkbox labeled “Enable error logging”.&amp;#160; By default, it’s turned off.&amp;#160; While this is turned off, the only notifications the Logger extension will receive (if you leave the Logger extension enabled) will be messages coming from some piece of code that explicitly makes a call to Utils.Log().&amp;#160; If you turn this new Enable Error Logging feature on, then when an unhandled exception occurs, Global.asax will pass the error details to Utils.Log() for any registered event handlers to deal with.&lt;/p&gt;

&lt;p&gt;&lt;span style="color: #00f; font-size: 20px"&gt;Result &amp;amp; Extensibility Possibilities&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;From version 1.5.1.11, any extension, widget or even BE.NET itself can now simply make a call to Utils.Log() to have any message logged.&amp;#160; Logging can be done with the built-in Logger extension, or messages can be logged to a database, sent via email, etc. by any other extension someone creates.&amp;#160; I’m also very excited about the new error handling mechanism which will give administrators a lot more information about errors that may be occurring in their blog.&lt;/p&gt;

&lt;p&gt;Please &lt;a href="http://blogengine.codeplex.com/SourceControl/ListDownloadableCommits.aspx" target="_blank"&gt;download&lt;/a&gt; and test out the latest build.&amp;#160; If any problems show up or you have any ideas for improvements, you can leave a comment here, or post a message on the CodePlex &lt;a href="http://blogengine.codeplex.com/Thread/List.aspx" target="_blank"&gt;discussion boards&lt;/a&gt;.&lt;/p&gt;</description>
      <link>http://allben.net/post/2009/06/13/Logging-Improved-Error-Reporting-in-BlogEngine.aspx</link>
      <author>Ben</author>
      <comments>http://allben.net/post/2009/06/13/Logging-Improved-Error-Reporting-in-BlogEngine.aspx#comment</comments>
      <guid>http://allben.net/post.aspx?id=b11777f3-f539-4914-8943-82b8e713ae44</guid>
      <pubDate>Sat, 13 Jun 2009 18:26:00 -0700</pubDate>
      <category>Development</category>
      <dc:publisher>Ben</dc:publisher>
      <pingback:server>http://allben.net/pingback.axd</pingback:server>
      <pingback:target>http://allben.net/post.aspx?id=b11777f3-f539-4914-8943-82b8e713ae44</pingback:target>
      <slash:comments>2</slash:comments>
      <trackback:ping>http://allben.net/trackback.axd?id=b11777f3-f539-4914-8943-82b8e713ae44</trackback:ping>
      <wfw:comment>http://allben.net/post/2009/06/13/Logging-Improved-Error-Reporting-in-BlogEngine.aspx#comment</wfw:comment>
      <wfw:commentRss>http://allben.net/syndication.axd?post=b11777f3-f539-4914-8943-82b8e713ae44</wfw:commentRss>
    </item>
    <item>
      <title>Virtual Machine Technology Means More Legacy Software</title>
      <description>&lt;p&gt;The advances in VM technology for both client and server operating systems is obviously a great thing.&amp;#160; It saves money, time and makes life much easier when you can just copy an image to a different machine, make backup copies of an image and go back to an old image in a very small amount of time.&amp;#160; It's great too for having software testing environments. &lt;/p&gt;  &lt;p&gt;One significant downside to this technology is going to be the amount of legacy software that will stick around.&amp;#160; By software, I mean operating systems and applications.&amp;#160; Upgrading or moving to a different OS or app is always a hassle.&amp;#160; However, one common opportunity when switching to a newer OS or app makes sense is when replacing old hardware.&amp;#160; Before VMs entered the scene, you might traditionally buy a new workstation or server every 4 years, for example.&amp;#160; A new machine means re-installing the OS and applications.&amp;#160; What a perfect opportunity to start off the new machine by installing the latest OS and applications. &lt;/p&gt;  &lt;p&gt;With VM technology, when a physical machine needs to be replaced, if the OS on it is already a VM, you just copy that VM to the new hardware.&amp;#160; Even if the old system isn't a VM, no problem.&amp;#160; There's tools available to create a VM from a physical instance of an OS.&amp;#160; Once the old physical instance of an OS is a VM, you just copy that VM over to the new hardware.&amp;#160; So you end up with a brand new machine, but the old OS and old applications are still running on that machine. &lt;/p&gt;  &lt;p&gt;For software companies, this means their customers may demand they support older versions of their software for a longer period.&amp;#160; When building new versions of software, it may also be necessary to include support for older operating systems based on the number of existing or potential customers who are still running an old OS. &lt;/p&gt;  &lt;p&gt;A client of mine just recently needed to replace their 8 year old server.&amp;#160; It's a Windows 2000 terminal server that several employees work out of.&amp;#160; I've convinced some of the employees to start using Firefox, but others are still stuck on IE6.&amp;#160; The client ended up converting the physical Win2K server to a virtual server and copied that over to the new hardware.&amp;#160; Ack!&amp;#160; There's that growing movement in the community to persuade people to get off IE6.&amp;#160; I think the statistics show the percentage of IE6 users out there is still in the 15% - 25% range.&amp;#160; That's much too high a demographic to desert and not support when building a website.&amp;#160; Unfortunately, IE7/IE8 isn't available for Windows 2000.&amp;#160; For this particular client, I just need to convince everyone there to start using Firefox.&amp;#160; Maybe I can just hide the IE6 icon on their desktops :) &lt;/p&gt;  &lt;p&gt;IE6 is a classic example of software you want everyone to get off of, and bury as deep as possible.&amp;#160; It's definitely not the only software out there that should be moved away from as newer versions of subpar software are released.&amp;#160; This increase in legacy software still being used out there is one of the few downsides to this overall great virtualization technology.&lt;/p&gt;</description>
      <link>http://allben.net/post/2009/05/17/Virtual-Machine-Technology-Means-More-Legacy-Software.aspx</link>
      <author>Ben</author>
      <comments>http://allben.net/post/2009/05/17/Virtual-Machine-Technology-Means-More-Legacy-Software.aspx#comment</comments>
      <guid>http://allben.net/post.aspx?id=26148bfb-019b-4d6b-a332-c0d747432441</guid>
      <pubDate>Sun, 17 May 2009 16:28:00 -0700</pubDate>
      <category>Opinion</category>
      <dc:publisher>Ben</dc:publisher>
      <pingback:server>http://allben.net/pingback.axd</pingback:server>
      <pingback:target>http://allben.net/post.aspx?id=26148bfb-019b-4d6b-a332-c0d747432441</pingback:target>
      <slash:comments>4</slash:comments>
      <trackback:ping>http://allben.net/trackback.axd?id=26148bfb-019b-4d6b-a332-c0d747432441</trackback:ping>
      <wfw:comment>http://allben.net/post/2009/05/17/Virtual-Machine-Technology-Means-More-Legacy-Software.aspx#comment</wfw:comment>
      <wfw:commentRss>http://allben.net/syndication.axd?post=26148bfb-019b-4d6b-a332-c0d747432441</wfw:commentRss>
    </item>
    <item>
      <title>Web Farm Extension 1.0</title>
      <description>&lt;p&gt;The caching of data in BlogEngine.NET becomes a problem when BE.NET is installed in a web farm.&amp;nbsp; When you add, edit or delete a post, that change is occurring on one machine within the farm as well as within the data store (App_Data or DB).&amp;nbsp; But the other servers within the farm are unaware there&amp;rsquo;s been a change in data.&amp;nbsp; Not until the data loaded in memory on these other servers clears out anywhere from minutes to hours to days later, the old set of data will continue to be shown to visitors hitting one of these other servers.&lt;/p&gt;
&lt;p&gt;I created a WebFarm extension which may help some people in this situation.&amp;nbsp; I haven&amp;rsquo;t worked much with web farms, so I&amp;rsquo;m not sure how well this extension will work.&amp;nbsp; Any feedback is appreciated.&amp;nbsp; I was able to test this extension on Vista/IIS7 where I had two separate web applications pointing to the same physical BE.NET location on my machine.&amp;nbsp; Even in this situation, creating a new post within one application would normally result in the post NOT showing up in the other application.&amp;nbsp; This new Web Farm extension did solve the caching problem for this scenario.&amp;nbsp; I&amp;rsquo;m hoping this success will carry over to a Web Farm scenario.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s two files in the ZIP file download.&amp;nbsp; WebFarm.cs should go in the App_Code\Extensions folder.&amp;nbsp; The other file, webfarm_data_update_listener.ashx, should go in the root of your blog.&lt;/p&gt;
&lt;p&gt;Once those files are in their correct locations, if you go to the Extensions tab in the control panel, you&amp;rsquo;ll want to click &amp;lsquo;Edit&amp;rsquo; for this new WebFarm extension.&lt;/p&gt;
&lt;p&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="WebFarm Extension" src="http://allben.net/image.axd?picture=webfarm_extension_3_1.png" border="0" alt="WebFarm Extension" width="721" height="447" /&gt;&lt;/p&gt;
&lt;p&gt;I wanted the help box on the right side to include as much information as possible.&amp;nbsp; But as you can see, the Extensions page doesn&amp;rsquo;t currently handle long description boxes very well :)&lt;/p&gt;
&lt;p&gt;The idea behind this extension is that if each server within the farm has a unique internal Ip address, this extension can notify each server that a change in data has occurred.&amp;nbsp; Not knowing a lot about web farms, this is the part I&amp;rsquo;m unsure is possible.&amp;nbsp; But it does some reasonable each server would have its own unique IP address.&amp;nbsp; If you&amp;rsquo;re using host headers, this extension may not work for you &amp;ndash; unless you have a unique URL to each server within the farm.&lt;/p&gt;
&lt;p&gt;The extension currently notifies the servers in the web farm when a new post or page has been created, updated or deleted.&amp;nbsp; Other data such as Settings, Profiles, Categories, Comments, etc. is not handled by this extension.&amp;nbsp; Or at least not in this version.&lt;/p&gt;
&lt;p&gt;The webfarm_data_update_listener.ashx file you placed in the root of the blog is the handler that receives notifications when a change in Post or Pages has occurred.&amp;nbsp; The data passed to the handler includes the type of data changed (Post, Page), the type of change (Insert, Update, Deletion) and the ID of the Post or Page that has been inserted/updated/deleted.&amp;nbsp; Rather than the handler re-loading all the Post/Page data, it will insert, update or delete just the one piece of data that changed.&amp;nbsp; This is more efficient than re-loading all the data which could be taxing for those with a lot of blog data.&lt;/p&gt;
&lt;p&gt;As described in the help area when adding the server Ip/Urls in the WebFarm extension, make sure the &amp;ldquo;Shared Key&amp;rdquo; you enter matches the key in the webfarm_data_update_listener.ashx handler file.&amp;nbsp; The default key in the handler is &amp;ldquo;blogengine&amp;rdquo;.&amp;nbsp; For security purposes, you may change the key in the ASHX handler.&amp;nbsp; But be sure the ASHX key matches the key you enter for each Ip/Url.&lt;/p&gt;
&lt;p&gt;Also, because of this same data caching issue in web farms, after you enter all the web farm server Ip addresses into the WebFarm extension, you&amp;rsquo;ll want to re-start BE.NET so the extension data you just entered is detected by all the servers in your farm.&amp;nbsp; Updating the web.config file with any meaningless change will accomplish re-starting the blog application.&amp;nbsp; This is just a one-time requirement so all servers have the list of servers they need to notify when a post or page change has occurred.&lt;/p&gt;
&lt;p&gt;I realize this extension has its limitations and isn&amp;rsquo;t a comprehensive solution to undesired caching in a web farm scenario.&amp;nbsp; But it does handle propagating changes in posts across the servers in the farm &amp;ndash; one of the more important areas.&amp;nbsp; This extension is also easy to get started with in contrast to making various changes within BE.NET itself.&amp;nbsp; Again, any feedback on this extension is appreciated.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Download:&lt;/strong&gt; &lt;a href="http://allben.net/file.axd?file=2009%2f5%2fWebFarmExtension_1.0.zip"&gt;WebFarmExtension_1.0.zip (3.26 kb)&lt;/a&gt;&lt;/p&gt;</description>
      <link>http://allben.net/post/2009/05/10/Web-Farm-Extension-10.aspx</link>
      <author>Ben</author>
      <comments>http://allben.net/post/2009/05/10/Web-Farm-Extension-10.aspx#comment</comments>
      <guid>http://allben.net/post.aspx?id=470a2b90-cb86-421b-9f71-af807e5539f0</guid>
      <pubDate>Sun, 10 May 2009 00:13:00 -0700</pubDate>
      <category>Development</category>
      <dc:publisher>Ben</dc:publisher>
      <pingback:server>http://allben.net/pingback.axd</pingback:server>
      <pingback:target>http://allben.net/post.aspx?id=470a2b90-cb86-421b-9f71-af807e5539f0</pingback:target>
      <slash:comments>1</slash:comments>
      <trackback:ping>http://allben.net/trackback.axd?id=470a2b90-cb86-421b-9f71-af807e5539f0</trackback:ping>
      <wfw:comment>http://allben.net/post/2009/05/10/Web-Farm-Extension-10.aspx#comment</wfw:comment>
      <wfw:commentRss>http://allben.net/syndication.axd?post=470a2b90-cb86-421b-9f71-af807e5539f0</wfw:commentRss>
    </item>
    <item>
      <title>Multiple WidgetZones in BE.NET</title>
      <description>&lt;p&gt;One of the commonly asked for features in BlogEngine.NET has just made it into the codebase.&amp;nbsp; This feature is Multiple WidgetZones.&lt;/p&gt; &lt;p&gt;To use multiple widget zones in your BE.NET blog, there's not too much you need to do.&amp;nbsp; I'll explain how it works and a couple of minor changes you may need to make to your blog -- even if you won't be using more than one widgetzone.&lt;/p&gt; &lt;p&gt;&lt;span style="font-size: 20px; color: #00f"&gt;Stylesheet Changes&lt;/span&gt;&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;When there was only one widgetzone, the widgetzone &amp;lt;div&amp;gt; and the widget selector dropdown list had a fixed ID on their HTML elements.&amp;nbsp; So, there was these two elements:&lt;/p&gt; &lt;div class="showcode"&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;div&lt;/span&gt; &lt;span class="attr"&gt;id&lt;/span&gt;&lt;span class="kwrd"&gt;="widgetzone"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;select&lt;/span&gt; &lt;span class="attr"&gt;id&lt;/span&gt;&lt;span class="kwrd"&gt;="widgetselector"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Because there can now be multiple widget zones and multiple widget selectors, and because there cannot legally be more than one element with the same ID, the widgetzone &amp;lt;div&amp;gt; now uses "widgetzone" for its class, and the selector uses "widgetselector" for its class.&amp;nbsp; Both the widgetzone and the selector also have a unique ID based on the new ZoneName property (see below).&amp;nbsp; So, example markup of what the &amp;lt;div&amp;gt; and &amp;lt;select&amp;gt; elements now look like are:&lt;/p&gt;
&lt;div class="showcode"&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;div&lt;/span&gt; &lt;span class="attr"&gt;class&lt;/span&gt;&lt;span class="kwrd"&gt;="widgetzone"&lt;/span&gt; &lt;span class="attr"&gt;id&lt;/span&gt;&lt;span class="kwrd"&gt;="widgetzone_PageBottom"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;select&lt;/span&gt; &lt;span class="attr"&gt;class&lt;/span&gt;&lt;span class="kwrd"&gt;="widgetselector"&lt;/span&gt; &lt;span class="attr"&gt;id&lt;/span&gt;&lt;span class="kwrd"&gt;="widgetselector_PageBottom"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There's a good chance you have #widgetzone or #widgetselector styles defined in your theme's CSS file.&amp;nbsp; Since there no longer are elements with those IDs, &lt;u&gt;&lt;strong&gt;you'll want to do a search-and-replace in your CSS file&lt;/strong&gt;&lt;/u&gt;.&amp;nbsp; Replacing #widgetzone with .widgetzone and #widgetselector with .widgetselector is all that is required.&amp;nbsp; Fortunately, because each widgetzone &amp;lt;div&amp;gt; and &amp;lt;select&amp;gt; element now have their own unique IDs, you can use those IDs to style individual widgetzones if they need to be styled differently in your blog.&lt;/p&gt;
&lt;p&gt;&lt;span style="font-size: 20px; color: #00f"&gt;ZoneName&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style="font-size: 20px; color: #00f"&gt;&lt;/span&gt;Each widgetzone you add to your blog needs to have it's own unique ZoneName.&amp;nbsp; The ZoneName can be added as a simple property to the widgetzone's control markup.&amp;nbsp; For example:&lt;/p&gt;
&lt;div class="showcode"&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;blog:WidgetZone&lt;/span&gt; &lt;span class="attr"&gt;runat&lt;/span&gt;&lt;span class="kwrd"&gt;="server"&lt;/span&gt; &lt;span class="attr"&gt;ZoneName&lt;/span&gt;&lt;span class="kwrd"&gt;="PageBottom"&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Although the ZoneName property is new to BE.NET, the existing widgetzone you've had in your blog up till now will start off with a ZoneName of "be_WIDGET_ZONE".&amp;nbsp; This was the name used to store widgetzone data in either the App_Data folder, or in a database.&amp;nbsp; Although not required, it wouldn't hurt to add that ZoneName to your existing widgetzone for clarity:&lt;/p&gt;
&lt;div class="showcode"&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;blog:WidgetZone&lt;/span&gt; &lt;span class="attr"&gt;runat&lt;/span&gt;&lt;span class="kwrd"&gt;="server"&lt;/span&gt; &lt;span class="attr"&gt;ZoneName&lt;/span&gt;&lt;span class="kwrd"&gt;="be_WIDGET_ZONE"&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you don't explicitly state a ZoneName, that is okay for one widgetzone, as BE.NET will default to using "be_WIDGET_ZONE" when a ZoneName is not provided.&amp;nbsp; But, you MUST give each widgetzone its own unique ZoneName if you will use more than one widgetzone in your blog.&lt;/p&gt;
&lt;p&gt;&lt;span style="font-size: 20px; color: #00f"&gt;App_Data and Database - Both OK&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Sometimes designing a flexible, well structured application pays off.&amp;nbsp; It's great that BE.NET was designed as such.&amp;nbsp; There was very little change required to enable multiple widget zones to be saved in either the App_Data folder or in a database.&amp;nbsp; No database changes are needed either since the data for all the widgetzones will be stored in the existing be_DataStoreSettings table.&amp;nbsp; Whether you're using XML storage in the App_Data folder or a DB backend, multiple widget zones work in both scenarios.&lt;/p&gt;
&lt;p&gt;&lt;span style="font-size: 20px; color: #00f"&gt;Moving Widgets&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Drag-and-drop is out, and dropdown is in!&amp;nbsp; Rearranging widgets within a widgetzone has always been done by dragging and dropping a widget to a new location within the zone.&amp;nbsp; While initially implementing multiple widgetzones, I had a zone on the left side of the page, and another on the right side.&amp;nbsp; While trying to drag-and-drop a widget on the right side to a new location within the same right side zone, I was having troubles with the drag-and-drop library trying to drop the widget into the zone on the left side of the page!&amp;nbsp; Instead of spending time trying to weed through the logic of the drag-and-drop library, it occurred to me a simpler interface would be the better route to go.&amp;nbsp; Drag-and-drop may be sexy, but getting widgets to move without any hassles or confusion is a better goal.&lt;/p&gt;
&lt;p&gt;There is now a 'Move' link for each widget, next to the Edit and X (remove) links.&lt;/p&gt;
&lt;p&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="72" alt="New Move Link" src="http://allben.net/image.axd?picture=widgets_move_1.png" width="309" border="0"&gt; &lt;/p&gt;
&lt;p&gt;Clicking the Move link will display a dropdown list and Move button above the Move link.&lt;/p&gt;
&lt;p&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="124" alt="Move Dropdown List and Button" src="http://allben.net/image.axd?picture=widgets_move_2.png" width="299" border="0"&gt; &lt;/p&gt;
&lt;p&gt;In this example, I have 3 widget zones in my blog.&amp;nbsp; Opening the dropdown list shows all 3 zones and the widgets within each zone.&lt;/p&gt;
&lt;p&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="218" alt="Move Widget Dropdown List Contents" src="http://allben.net/image.axd?picture=widgets_move_3.png" width="244" border="0"&gt; &lt;/p&gt;
&lt;p&gt;The 3 zones appear in the list with brackets around them.&amp;nbsp; They are the HeaderZone, be_WIDGET_ZONE and the PageBottom zone.&amp;nbsp; Under each zone name is the list of widgets within that zone.&amp;nbsp; You can either select a widget to move the current widget in front of, or you can select a zone which will move the current widget to the bottom of that zone.&lt;/p&gt;
&lt;p&gt;As you can tell, &lt;strong&gt;&lt;u&gt;moving a widget from one zone to another zone is fully supported&lt;/u&gt;&lt;/strong&gt;.&amp;nbsp; There's lots of possibilities with this capability.&lt;/p&gt;
&lt;p&gt;&lt;span style="font-size: 20px; color: #00f"&gt;Few More Stylesheet Changes&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In addition to the search-and-replace stylesheet modification mentioned above, there's a couple of other additions you may want to make to your stylesheet in support of the new Move link and dropdown list.&amp;nbsp; These changes are already in the stylesheets of the themes included with BE.NET.&amp;nbsp; So you know what they are, I've listed them below.&amp;nbsp; The first style below was a modification of an existing style.&lt;/p&gt;
&lt;div class="showcode"&gt;&lt;pre class="csharpcode"&gt;&lt;span class="html"&gt;div.widget a.edit, div.widget a.move{&lt;/span&gt;
	&lt;span class="attr"&gt;font-size&lt;/span&gt;: &lt;span class="kwrd"&gt;10px&lt;/span&gt;;
	&lt;span class="attr"&gt;font-weight&lt;/span&gt;: &lt;span class="kwrd"&gt;normal&lt;/span&gt;;
	&lt;span class="attr"&gt;float&lt;/span&gt;: &lt;span class="kwrd"&gt;right&lt;/span&gt;;
	&lt;span class="attr"&gt;z-index&lt;/span&gt;: &lt;span class="kwrd"&gt;1&lt;/span&gt;;
	&lt;span class="attr"&gt;margin-left&lt;/span&gt;: &lt;span class="kwrd"&gt;5px&lt;/span&gt;;
&lt;span class="html"&gt;}&lt;/span&gt;

&lt;span class="html"&gt;.widgetzone div#moveWidgetToContainer {&lt;/span&gt;
	&lt;span class="attr"&gt;text-align&lt;/span&gt;: &lt;span class="kwrd"&gt;right&lt;/span&gt;;
	&lt;span class="attr"&gt;margin&lt;/span&gt;: &lt;span class="kwrd"&gt;3px&lt;/span&gt;;
&lt;span class="html"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span style="font-size: 20px; color: #00f"&gt;Credits&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Thanks to &lt;a href="http://www.codeplex.com/site/users/view/McZosch" target="_blank"&gt;McZosch&lt;/a&gt; who posted the initial code changes for multiple widgetzones in the Issue Tracker at CodePlex.&amp;nbsp; I was able to use mostly everything he contributed.&amp;nbsp; Integration of his code went smoothly.&amp;nbsp; Most of the time I spent with adding this feature was with moving widgets.&amp;nbsp; Switching from drag-and-drop to dropdown and enabling widgets to move from one zone to another took some time altogether.&lt;/p&gt;
&lt;p&gt;&lt;span style="font-size: 20px; color: #00f"&gt;Give it a Try!&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Since it's of course not required to fill up a widgetzone with many widgets in it, multiple widgetzones will allow you to add a zone anywhere in your blog with just a single widget in it.&amp;nbsp; There's a lot of possibilities with this, and for many BE.NET bloggers out there, I think multiple widgetzones are going to be very useful.&lt;/p&gt;
&lt;p&gt;Everything seems to work well after testing out the new feature in a few scenarios.&amp;nbsp; You can download the latest build of BE.NET in the &lt;a href="http://blogengine.codeplex.com/SourceControl/ListDownloadableCommits.aspx" target="_blank"&gt;Source Code section&lt;/a&gt; at CodePlex.&amp;nbsp; If you run into any issues, please post them in the &lt;a href="http://blogengine.codeplex.com/Thread/List.aspx" target="_blank"&gt;Discussions area&lt;/a&gt; at CodePlex.&lt;/p&gt;</description>
      <link>http://allben.net/post/2009/04/18/Multiple-WidgetZones-in-BENET.aspx</link>
      <author>Ben</author>
      <comments>http://allben.net/post/2009/04/18/Multiple-WidgetZones-in-BENET.aspx#comment</comments>
      <guid>http://allben.net/post.aspx?id=d3a787a9-4338-4293-a238-281ac16ee6e3</guid>
      <pubDate>Sat, 18 Apr 2009 14:35:21 -0700</pubDate>
      <category>Development</category>
      <dc:publisher>Ben</dc:publisher>
      <pingback:server>http://allben.net/pingback.axd</pingback:server>
      <pingback:target>http://allben.net/post.aspx?id=d3a787a9-4338-4293-a238-281ac16ee6e3</pingback:target>
      <slash:comments>6</slash:comments>
      <trackback:ping>http://allben.net/trackback.axd?id=d3a787a9-4338-4293-a238-281ac16ee6e3</trackback:ping>
      <wfw:comment>http://allben.net/post/2009/04/18/Multiple-WidgetZones-in-BENET.aspx#comment</wfw:comment>
      <wfw:commentRss>http://allben.net/syndication.axd?post=d3a787a9-4338-4293-a238-281ac16ee6e3</wfw:commentRss>
    </item>
    <item>
      <title>BlogEngine.NET 1.5 Ships!</title>
      <description>&lt;p&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; margin: 0px 0px 5px 15px; border-right-width: 0px" height="233" alt="BlogEngine.NET 1.5 Ships!" src="http://allben.net/image.axd?picture=blogengine_version_1.5_ships.gif" width="290" align="right" border="0"&gt; The next version of &lt;a href="http://www.dotnetblogengine.net/" target="_blank"&gt;BlogEngine.NET&lt;/a&gt; has been released and is available for download.&amp;nbsp; This new version features several new upgrades including support for IIS7, a much newer version of the tinyMce WYSIWYG editor, support for WLW 2009, compiled extension support, and nested comments to name a few.&lt;/p&gt; &lt;p&gt;Arguably even more important for BE.NET users since the last release is the growing number of available themes, widgets and extensions for BlogEngine.&amp;nbsp; A few noteworthy places to get themes is from &lt;a href="http://ambay.com/web/page/BlogEngineNET-Themes.aspx" target="_blank"&gt;this site&lt;/a&gt; that has aggregated themes from several locations (live previews available), from another &lt;a href="http://www.blogenginetheme.com/Category/Themes.aspx" target="_blank"&gt;theme aggregator&lt;/a&gt; with live previews, or directly from &lt;a href="http://www.onesoft.dk/category/-BlogEngine-Themes.aspx" target="_blank"&gt;onesoft&lt;/a&gt; who has adapted several themes for BE.NET.&lt;/p&gt; &lt;p&gt;The documentation has also been updated and moved with this release.&amp;nbsp; The new location is now at &lt;a href="http://blogengine.codeplex.com/Wiki/View.aspx?title=Documentation" target="_blank"&gt;CodePlex&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;If you're already using BE.NET, upgrading should be pretty painless -- especially if you haven't done too many customizations to files outside your theme.&amp;nbsp; If you're looking to start blogging, BlogEngine.NET is a great blogging platform to go with.&amp;nbsp; It runs right out of the box, and since it's open source, you can optionally customize any part of it.&lt;/p&gt; &lt;p&gt;BlogEngine.NET 1.5 can be downloaded &lt;a href="http://blogengine.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=26080" target="_blank"&gt;here&lt;/a&gt;.&lt;/p&gt;</description>
      <link>http://allben.net/post/2009/04/13/BlogEngineNET-15-Ships!.aspx</link>
      <author>Ben</author>
      <comments>http://allben.net/post/2009/04/13/BlogEngineNET-15-Ships!.aspx#comment</comments>
      <guid>http://allben.net/post.aspx?id=490e3b3f-5f33-489f-9d31-2d8972fd5998</guid>
      <pubDate>Mon, 13 Apr 2009 20:59:56 -0700</pubDate>
      <category>General</category>
      <dc:publisher>Ben</dc:publisher>
      <pingback:server>http://allben.net/pingback.axd</pingback:server>
      <pingback:target>http://allben.net/post.aspx?id=490e3b3f-5f33-489f-9d31-2d8972fd5998</pingback:target>
      <slash:comments>0</slash:comments>
      <trackback:ping>http://allben.net/trackback.axd?id=490e3b3f-5f33-489f-9d31-2d8972fd5998</trackback:ping>
      <wfw:comment>http://allben.net/post/2009/04/13/BlogEngineNET-15-Ships!.aspx#comment</wfw:comment>
      <wfw:commentRss>http://allben.net/syndication.axd?post=490e3b3f-5f33-489f-9d31-2d8972fd5998</wfw:commentRss>
    </item>
    <item>
      <title>Multiple Forms in Master Page Site</title>
      <description>&lt;p&gt;When integrating a website with 3rd party services such as search providers or payment processors, it's not an uncommon need to have a submit button that posts to that 3rd party's site. &lt;/p&gt;  &lt;p&gt;ASP.NET 2.0 introduced the PostBackUrl property for controls that can initiate a post -- Buttons, ImageButtons, LinkButtons.&amp;#160; With this property, you can have your page post to any URL.&amp;#160; This is a somewhat decent solution to this problem.&amp;#160; The major downside to PostBackUrl is it requires the visitor to have JavaScript enabled in their browser.&amp;#160; If JavaScript is disabled, you end up with a normal postback to your own page.&amp;#160; To me, this makes PostBackUrl not a very good choice to turn to. &lt;/p&gt;  &lt;p&gt;The other day, I needed to send a visitor to a payment processor's site.&amp;#160; My site was using master pages.&amp;#160; I was collecting some preliminary information from the visitor on a Content page.&amp;#160; Once collected, I was going to show them a confirmation of what they would be paying for and give them a button to move onto the payment processor.&amp;#160; I decided to use the PostBackUrl property on this button. &lt;/p&gt;  &lt;p&gt;The payment processor needed a few hidden fields included in the form submission.&amp;#160; I put those hidden input fields including the values into non-server hidden input fields since I didn't want the Name or Ids of the input fields mangled by ASP.NET.&amp;#160; The form looked good, and clicking the button took me to the payment processor's website -- but as soon as I got there, the only thing on the page was some error message complaining about incoming data (a very non-specific error message). &lt;/p&gt;  &lt;p&gt;I knew their site and this particular landing page worked when I tested posting data in a non-ASP.NET environment.&amp;#160; I confirmed the correct hidden input fields were being sent in the POST through Fiddler, but still no dice.&amp;#160; The only explanation I could come up with is when using PostBackUrl, not only are my custom hidden fields being passed to this other site, but so are all the other standard ASP.NET hidden fields -- ViewState, EventValidation, etc.&amp;#160; It's possible the payment processor's site was not expecting other form values to be passed to it. &lt;/p&gt;  &lt;p&gt;So I decided to ditch PostBackUrl ... which was fine since I wasn't a big fan of PostBackUrl to begin with.&amp;#160; I came up with a pretty simple solution to having multiple forms in this master page environment.&amp;#160; The typical master page setup is where a &amp;lt;form runat=&amp;quot;server&amp;quot;&amp;gt; tag in the master page surrounds the ContentPlaceHolder.&lt;/p&gt;  &lt;div class="showcode"&gt;   &lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;body&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;form&lt;/span&gt; &lt;span class="attr"&gt;id&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;form1&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;runat&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;server&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;asp:ContentPlaceHolder&lt;/span&gt; &lt;span class="attr"&gt;id&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;ContentPlaceHolder1&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;runat&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;server&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;asp:ContentPlaceHolder&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;form&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;body&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Now if you put your own &amp;lt;form&amp;gt; tag in the Content page, you'll end up with a form nested in another form.&amp;#160; Nested forms are not valid.&amp;#160; Here's what I did to avoid nested forms, but still end up with multiple forms.&lt;/p&gt;

&lt;div class="showcode"&gt;
  &lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;body&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;    
    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;form&lt;/span&gt; &lt;span class="attr"&gt;id&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;form1&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;runat&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;server&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;asp:ContentPlaceHolder&lt;/span&gt; &lt;span class="attr"&gt;id&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;cphForm&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;runat&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;server&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;asp:ContentPlaceHolder&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;form&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
    
    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;asp:ContentPlaceHolder&lt;/span&gt; &lt;span class="attr"&gt;id&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;cphNoForm&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;runat&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;server&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;asp:ContentPlaceHolder&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;body&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;The master page now has 2 content place holders.&amp;#160; One is wrapped in a &amp;lt;form runat=&amp;quot;server&amp;quot;&amp;gt; tag, and the other isn't.&amp;#160; The type of controls allowed when not wrapped in a &amp;lt;form runat=&amp;quot;server&amp;quot;&amp;gt; tag is limited.&amp;#160; For instance, you cannot have ASP.NET Buttons, TextBoxes and a number of other controls outside a &amp;lt;form runat=&amp;quot;server&amp;quot;&amp;gt; tag.&amp;#160; You can however use Literals, PlaceHolders and basically any HTML controls with a runat=&amp;quot;server&amp;quot; tag.&amp;#160; Here's the content page markup.&lt;/p&gt;

&lt;div class="showcode"&gt;
  &lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;asp:Content&lt;/span&gt; &lt;span class="attr"&gt;ID&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;cntForm&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;ContentPlaceHolderID&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;cphForm&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;Runat&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;Server&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
    
    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;asp:PlaceHolder&lt;/span&gt; &lt;span class="attr"&gt;ID&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;phForm&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;runat&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;server&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
    
        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;h2&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;Select an Item to Purchase&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;h2&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;asp:RadioButtonList&lt;/span&gt; &lt;span class="attr"&gt;ID&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;rblMenu&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;runat&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;server&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;asp:ListItem&lt;/span&gt; &lt;span class="attr"&gt;Text&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;Item 1&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;Value&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;item1&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;Selected&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;True&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;asp:ListItem&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;asp:ListItem&lt;/span&gt; &lt;span class="attr"&gt;Text&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;Item 2&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;Value&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;item2&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;asp:ListItem&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;asp:ListItem&lt;/span&gt; &lt;span class="attr"&gt;Text&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;Item 3&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;Value&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;item3&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;asp:ListItem&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;asp:RadioButtonList&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;asp:Button&lt;/span&gt; &lt;span class="attr"&gt;ID&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;btnContinue&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;runat&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;server&amp;quot;&lt;/span&gt;
                    &lt;span class="attr"&gt;Text&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;Continue&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;OnClick&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;continuePurchase&amp;quot;&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;asp:PlaceHolder&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
    
&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;asp:Content&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;asp:Content&lt;/span&gt; &lt;span class="attr"&gt;ID&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;cntNoForm&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;ContentPlaceHolderID&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;cphNoForm&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;Runat&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;Server&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;asp:PlaceHolder&lt;/span&gt; &lt;span class="attr"&gt;ID&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;phConfirmation&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;runat&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;server&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;Visible&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;false&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
    
        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;h2&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
           Your Selected Item:
           &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;asp:Literal&lt;/span&gt; &lt;span class="attr"&gt;ID&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;litSelectedItem&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;runat&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;server&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;asp:Literal&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;h2&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
    
        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;form&lt;/span&gt; &lt;span class="attr"&gt;method&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;post&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;action&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;http://www.example.com/&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;input&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;hidden&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;myId&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;value&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;someID&amp;quot;&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;input&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;hidden&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;itemCode&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;value&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;&amp;lt;%= itemCode %&amp;gt;&amp;quot;&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;input&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;hidden&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;itemAmount&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;value&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;&amp;lt;%= itemAmt %&amp;gt;&amp;quot;&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;input&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;submit&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;payNow&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;value&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;Pay Now&amp;quot;&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;form&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
    
    &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;asp:PlaceHolder&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;asp:Content&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;The content page is making use of both content place holders.&amp;#160; The top Content control contains a place holder, which contains a simple order form.&amp;#160; The second Content control contains a place holder with its visibility set to false.&amp;#160; So when the page is first pulled up, nothing in the second Content control is yet visible.&lt;/p&gt;

&lt;p&gt;Once an item is selected, and the Continue button is clicked, a postback is done.&amp;#160; During the postback, the placeholder in the first Content control is made invisible, and the placeholder in the second Content control is made visible.&amp;#160; Because the second Content control (and all of its contents) are outside of the &amp;lt;form runat=&amp;quot;server&amp;quot;&amp;gt; tag, we've achieved having two different, non-nested &amp;lt;form&amp;gt; tags on the same page.&amp;#160; Here's what the rendered HTML looks like after the person has selected their item and is ready to be sent to the payment processor:&lt;/p&gt;

&lt;div class="showcode"&gt;
  &lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;body&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;    
   &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;div&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;form&lt;/span&gt; &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;aspnetForm&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;method&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;post&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;action&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;Content1.aspx&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;id&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;aspnetForm&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
         &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;div&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;input&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;hidden&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;__VIEWSTATE&amp;quot;&lt;/span&gt;
                   &lt;span class="attr"&gt;id&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;__VIEWSTATE&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;value&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;some long value&amp;quot;&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;
         &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;div&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;form&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
     
      &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;h2&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;Your Selected Item: Item 2&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;h2&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
    
      &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;form&lt;/span&gt; &lt;span class="attr"&gt;method&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;post&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;action&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;http://www.example.com/&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
         &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;input&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;hidden&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;myId&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;value&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;someID&amp;quot;&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;
         &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;input&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;hidden&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;itemCode&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;value&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;item2&amp;quot;&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;
         &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;input&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;hidden&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;itemAmount&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;value&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;30&amp;quot;&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;
         &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;input&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;submit&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;payNow&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;value&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;Pay Now&amp;quot;&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;form&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;div&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;    
&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;body&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;With this approach, I was able to post the form to the payment processor, and the form contained hidden input fields only relevant to the processor.&amp;#160; This approach will work in most master page situations.&amp;#160; It may be difficult to implement if you have server side controls requiring a &amp;lt;form runat=&amp;quot;server&amp;quot;&amp;gt; tag in your master page -- outside of the main content place holder.&amp;#160; In this case, it may still be possible to implement this two ContentPlaceHolder approach, if you do some juggling around of your controls and/or layout.&amp;#160; However, in a lot of situations, this is a practical way to achieve multiple form tags in an ASP.NET site.&lt;/p&gt;</description>
      <link>http://allben.net/post/2009/03/30/Multiple-Forms-in-Master-Page-Site.aspx</link>
      <author>Ben</author>
      <comments>http://allben.net/post/2009/03/30/Multiple-Forms-in-Master-Page-Site.aspx#comment</comments>
      <guid>http://allben.net/post.aspx?id=15ee39a5-9afb-4c40-b4a2-b04ac2a9bba9</guid>
      <pubDate>Mon, 30 Mar 2009 23:14:20 -0700</pubDate>
      <category>Development</category>
      <dc:publisher>Ben</dc:publisher>
      <pingback:server>http://allben.net/pingback.axd</pingback:server>
      <pingback:target>http://allben.net/post.aspx?id=15ee39a5-9afb-4c40-b4a2-b04ac2a9bba9</pingback:target>
      <slash:comments>3</slash:comments>
      <trackback:ping>http://allben.net/trackback.axd?id=15ee39a5-9afb-4c40-b4a2-b04ac2a9bba9</trackback:ping>
      <wfw:comment>http://allben.net/post/2009/03/30/Multiple-Forms-in-Master-Page-Site.aspx#comment</wfw:comment>
      <wfw:commentRss>http://allben.net/syndication.axd?post=15ee39a5-9afb-4c40-b4a2-b04ac2a9bba9</wfw:commentRss>
    </item>
    <item>
      <title>View Source improvements in IE8</title>
      <description>&lt;p&gt;Compared to Firefox and Chrome, IE has always had a very plain rendering when viewing the source of an HTML page.&amp;#160; The source just shows up as plain text in Notepad.&amp;#160; HTML is of course just plain text, but Firefox and Chrome add coloring for matching HTML tags which makes looking at the source a little more pleasant. &lt;/p&gt;  &lt;p&gt;IE8 now does what these others browsers have been doing.&amp;#160; The HTML source no longer is displayed in Notepad, but in an IE source viewing pop-up window.&amp;#160; This IE source viewer now does tag coloring and even includes line numbers.&amp;#160; This is a nice little improvement.&lt;/p&gt;  &lt;p&gt;&lt;img style="border-right: 0px; border-top: 0px; margin: 0px 0px 0px 30px; border-left: 0px; border-bottom: 0px" height="180" alt="IE8 Source Viewer" src="http://allben.net/image.axd?picture=WindowsLiveWriter/ViewSourceimprovementsinIE8_13FD7/ie8_source_viewer_3.jpg" width="553" border="0" /&gt; &lt;/p&gt;  &lt;p&gt;I did notice one other interesting feature when doing a View Source in IE8.&amp;#160; On the File menu of the source viewer, if you select 'Save', you have a choice to save the HTML Source (nothing special here) or save the 'Formatted HTML View' (screenshot below).&amp;#160; This &amp;quot;save formatted HTML view&amp;quot; will create an HTML file of how IE8's source viewer is displaying the source -- including the tag coloring.&amp;#160; You can then open up that saved HTML file in any browser to have the source display exactly as it does in IE8's source viewer. &lt;/p&gt;  &lt;p&gt;&lt;img style="border-right: 0px; border-top: 0px; margin: 0px 0px 0px 30px; border-left: 0px; border-bottom: 0px" height="180" alt="IE8 Source Viewer - Save as Formatted HTML View" src="http://allben.net/image.axd?picture=WindowsLiveWriter/ViewSourceimprovementsinIE8_13FD7/ie8_source_viewer_save_as_formatted_html_view_6.jpg" width="553" border="0" /&gt; &lt;/p&gt;  &lt;p&gt;The file size of the &amp;quot;formatted html view&amp;quot; is considerably larger than the size of the plain HTML source without the formatting.&amp;#160; For instance, for a particular 45 KB HTML page I tried this in, the formatted html view file is 452 KB. &lt;/p&gt;  &lt;p&gt;I'm guessing the new color tags and syntax highlighting in the new source viewer was done via HTML markup.&amp;#160; So it probably wasn't a big deal for the IE team to just include this new save as 'Formatted HTML View' option -- since the formatted HTML source was already there.&amp;#160; In any event, it's nice it's there for whenever the need of the formatted html source could be used.&lt;/p&gt;</description>
      <link>http://allben.net/post/2009/03/27/View-Source-improvements-in-IE8.aspx</link>
      <author>Ben</author>
      <comments>http://allben.net/post/2009/03/27/View-Source-improvements-in-IE8.aspx#comment</comments>
      <guid>http://allben.net/post.aspx?id=a2b10e75-0a8c-4015-956d-c4f50eea77f3</guid>
      <pubDate>Fri, 27 Mar 2009 22:38:21 -0700</pubDate>
      <category>General</category>
      <dc:publisher>Ben</dc:publisher>
      <pingback:server>http://allben.net/pingback.axd</pingback:server>
      <pingback:target>http://allben.net/post.aspx?id=a2b10e75-0a8c-4015-956d-c4f50eea77f3</pingback:target>
      <slash:comments>1</slash:comments>
      <trackback:ping>http://allben.net/trackback.axd?id=a2b10e75-0a8c-4015-956d-c4f50eea77f3</trackback:ping>
      <wfw:comment>http://allben.net/post/2009/03/27/View-Source-improvements-in-IE8.aspx#comment</wfw:comment>
      <wfw:commentRss>http://allben.net/syndication.axd?post=a2b10e75-0a8c-4015-956d-c4f50eea77f3</wfw:commentRss>
    </item>
  </channel>
</rss>
