# Thursday, 28 October 2010

ASP.NET applications run under an account with limited privileges that doesn’t have the right to connect to a network printer.  The normal answer for this is to give rights to the user running your application or impersonate a user during execution that does have the rights.

We found another way that doesn’t require changing the user or privileges of the existing account that ASP.NET apps run under.

I can hear someone asking “Why would you want to do this anyway?  Can’t the end user choose a printer?”  There might be situations where this isn’t sufficient enough.  We had to control the printer line by line in order to print on the form that is being used and a particular printer had to be used.  

We added the network printer as a printer on the server where our app was running.  When we setup the printer we chose local not networked but then on the options to use a printer port we didn’t use LPT1.  Instead we created a new port with the IP address of the device.  In our code we then call the printer by the name we gave it on our server.

port

printer 

The PrintDocument class in the .NET Framework allows for control of a printer.

VB Version:

Public Class SpecialReportPrintJob 
  Inherits Printing.PrintDocument 
 
Protected Overrides Sub OnBeginPrint(ByVal ev as Printing.PrintEventArgs) 
  MyBase.OnBeginPrint(ev) 
 
  Me.PrinterSettings.PrinterName = "SCLaser2" 
 
  'setup rest of stuff.... 
End Sub   
End Class 
'And we then call it like so 
Dim printSpecialReport as new SpecialReportPrintJob() 
printSpecialReport.Print()
C# Version:
public class SpecialReportPrintJob : Printing.PrintDocument
{

    protected override void OnBeginPrint(Printing.PrintEventArgs ev)
    {
        base.OnBeginPrint(ev);

        this.PrinterSettings.PrinterName = "SCLaser2";

        //setup rest of stuff.... 
    }
}
//And we then call it like so
SpecialReportPrintJob printSpecialReport = new SpecialReportPrintJob();
printSpecialReport.Print();

PrintDocument is pretty powerful.  It can control just about anything for your printer.  We passed in a text file of our data to print and our code setup the x, y coordinates of each item as it related to where on the form it needed to be.

All this without having to muck around with permissions.  Always a good thing!

Happy Coding!

Posted 10.28.2010  #    Comments [0]  | 
# Monday, 02 August 2010

Over the past week our team has been crunching out an asp.net webforms app to help manage our flood response situation that has been a very unfortunate side effect of the fire here in Coconino County.  We have been going pretty fast, releasing one to two updates a day in order to get functionality out quickly to those who need it.

Today, we had a breather and I decided to go back and spruce up our work.  One item on my plate was to help the users more easily enter dates.  The main entry form has over 10 date fields.  A little excessive but it’s what they need.  I could just use the AJAX calendar extender but that would mean quite a bit of clicking and properties to set manually.

I had a hunch that the jQuery way may have an easier solution.  Using Selectors, the solution ended up being one line of code.

<script type="text/javascript">
    $(function() {
        $("input[name*='dte']").datepicker();
    });
</script>

How awesome is that. All of our date inputs have a prefix of 'dte' allowing a simple selector over all inputs with 'dte' in the name attribute.  Normally, one would give all date inputs a class that can be use instead but they all already had this in common.

With the same idea in mind, I decided to change all of our buttons to use the jQuery button widget to get a nice and quick improvement on the look and feel.

<script type="text/javascript">
    $(function() {
        $(":submit, :button").button();
    });
</script>

In our case, we defined buttons two different ways in our code.  Again, with selectors we can handle this with one line of code.

Downloading and using jQuery UI couldn’t be simpler.  I customized the download (you don’t have to) to only use the date picker and button widgets and added the customized jQuery to a script tag.  jQuery UI depends on jQuery but instead of using the version downloaded I used the google CDN to help speed things up a bit.  Last, I added the theme for the controls to the app and referenced the stylesheet.

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript" src="scripts/jquery-ui-1.8.2.custom.min.js"></script>
<link href="~/stylesheets/jqueryUIcss/ui-lightness/jquery-ui-1.8.2.custom.css" rel="Stylesheet" type="text/css" /> 

The ability to do this so easily and quickly with jQuery is a big WIN.  I wonder if this is easily replicated with MS AJAX?  My hunch is it’s probably doable but can’t possibly be easier and I imagine it’s not nearly as easy.  I could be wrong though…

Happy Coding!

Posted 08.02.2010  #    Comments [0]  | 
# Saturday, 27 March 2010

I need to take a CSV file of logins with their emails and verify if these logins exist in our Active Directory.  This can be done several different ways including with powershell but for one reason or another I want/need to do it with .Net.

I will be using an open source library called FileHelpers to make the parsing of the CSV file a snap.  I’m going to skip over the code that uses FileHelpers and focus on the AD lookup.  Check out my previous post on FileHelpers to see how easy FileHelpers makes parsing.

I’m going to use the System.DirectoryServices namespace to lookup AD information.

Let’s look at the AD Lookup class I created to compare the login in the CSV file to AD.

using System;
using System.Collections.Generic;
using System.DirectoryServices;

namespace VerifyUsers
{
    public class ADLookup
    {
        private DirectorySearcher _directoryToSearch;

        public ADLookup()
        {
            DirectoryEntry root = new DirectoryEntry("LDAP://rootDSE");
            string rootName = "LDAP://" + root.Properties["defaultNamingContext"].Value.ToString();
            DirectoryEntry searchRoot = new DirectoryEntry(rootName);
            _directoryToSearch = new DirectorySearcher(searchRoot);
            _directoryToSearch.PageSize = 100;
            _directoryToSearch.SearchScope = SearchScope.Subtree;
            _directoryToSearch.PropertiesToLoad.Add("displayName");
        }

        public bool IsUserInAD(string login)
        {
            _directoryToSearch.Filter = string.Format("(&(sAMAccountName={0})(objectClass=Person))", login);
            SearchResultCollection personFound = _directoryToSearch.FindAll();

            return personFound.Count > 0;
        }
    }
}

I’m using the DirectorySearcher class to look in AD for user information.  In the class constructor I setup the connection information to the LDAP store and set some of the basic properties.

The SearchScope property is important to set to what you need.  I set it to Subtree (which is the default) to have search include the base and it’s children.  If for example, your AD environment has subfolders then you want this set in order to search the subfolders.

In the IsUserInAD method call I setup the query to look for the login name better known as the sAMAccountName in LDAP and I only want it to search Person objects.

Note – If you are creating your own project you will need to add a reference to System.DirectoryServices.

Now let’s get the code running in a console:

using System;
using System.Collections.Generic;

namespace VerifyUsers
{
    class Program
    {
        static void Main(string[] args)
        {
            Parser userParser = new Parser("Users.csv");
            DisplayUsersNotInAD(userParser.Parse());

            Console.ReadLine();
        }

        static void DisplayUsersNotInAD(User[] list)
        {
            ADLookup lookup = new ADLookup();
            foreach (User u in list)
                if (lookup.IsUserInAD(u.login) == false)
                    Console.WriteLine("{0} was not found in AD.", u.login);
        }
    }
}

First, we parse the CSV file using the FileHelpers library.  Then we iterate over the list and display the entries not found in our directory.

The CSV file I am using has three entries and the first two are bogus accounts that don’t exist in AD.  The third entry does.  If the code works correctly we should only have the first two entries reported as not found.

When you run the code you should see:

image

The last entry, kdog, was found in AD.  We can do whatever we need to with the two entries that aren’t in AD.

Download the C# source or VB Source.

Read my previous post on using the open source FileHelpers library to parse CSV files.

Happy Coding!

Posted 03.27.2010  #    Comments [0]  | 
# Friday, 05 March 2010

Want to learn ASP.Net MVC?  Perhaps you already started learning it.  I decided to take a side project of mine that I started in Web Forms and completely start over with .Net MVC.  I wanted to document what I did and the resources I found in the hopes that it may help you.  I still have much to learn and master with .Net MVC but so far I am impressed and am happy I decided to branch out and learn something new.  It has made me a better developer and a more well informed developer.  Both good things!

Not sure what ASP.Net MVC is?  Check out this intro video and this overview and then hurry on back.

How I started:

ASP.Net MVC Tutorials and many, many blog posts from Scott Guthrie, Phil Haack, Rob Conery, Jimmy Bogard, Jeffrey Palermo, Eric Hexter, Steven Sanderson, Stephen Walther and these are just the ones I remember.

Books:

I started with ASP.Net MVC In Action and quickly realized that many of it’s topic were over my head.  So I put it down and read ASP.Net MVC Framework Unleashed and Pro ASP.Net MVC Framework and then went back to MVC In Action. 

TIP #1 - When I am learning a new technology I tend to do the examples in the book from scratch.  It takes longer than just downloading their sample code but it helps me absorb it more. 

TIP #2 – Almost all the material I read on ASP.Net MVC touch on or go into detail on Unit Testing, Test Driven Development, Dependency Injection, Inversion Of Control and many other design principles.  If you are not sure what these are you might want to research them a little bit.  The books I recommend do give overviews of these.  One of MVC’s main design goals is for giving control of the application back to the developer and ease of testing.  If you find this even the least bit interesting then keep going!

Samples/Tutorials:

ASP.Net MVC Tutorials – I watched these to get a feel for MVC.  They have a lot of video’s and sample code you can run through.  They did a great job of covering the basics.  Stay here a while, fire up your IDE and peck away on the keyboard with these tutorials.

Nerd Dinner – Perhaps a cult classic or a right of passage?  I for the most part skipped it…. Oooppsss.  No wonder I struggled so hard with MVC in the beginning.  Just kidding.    It was one of the first books and sample apps out.  I skipped it only because I didn’t have it in my Safari Books Online.  I did however take advantage of their free chapter and source code.

Rob Conery – Rob Conery published a set of video’s documenting the development of an eCommerce storefront called MVC StoreFront.  I watched these video’s carefully and read his code carefully.  He then renamed the project to Kona and added a few more screencasts.  Check these out.  I poured through his code to see some of the possibilities that MVC has.  I still don’t understand some of it but Rob’s a much smarter guy than I am. :-)

Starter kits – Check out their Northwind MVC project, period.

Tip #3 – Check out the ASP.Net MVC Tutorials and the MVC storefront screencasts at least! 

Cheat sheets – Elijah Manor published several cheat sheets on MVC that are great to put on your wall or at least keep close at hand while you are learning MVC.

50 ASP.Net MVC Tips – Stephen Walther, author of ASP.Net MVC Framework Unleashed published these nice tips on his blog.

MVC Contrib Project – A bit more advanced but the wizards over at Headspring created this little open source gem.  The ASP.Net MVC In Action’s sample project uses MVC Contrib.  The project is great and I am still digesting a lot of what is in the project.  I especially dig the TestHelper library.  It just makes my job of testing routes a breeze.

I mentioned I used a side project to learn ASP.Net MVC.  I know now that had I not done that I would still be sitting here thinking about learning it.  Why?  I found many things I took for granted with Web Forms were gone in ASP.Net MVC.  Viewstate for example.  I had certainly grown accustom to it.  Another gotcha for me was filling a dropdown list the right way (not directly in the controller).  I truly did have some frustrating points with ASP.Net MVC but only because I had to retrain myself to how the internet really works (Web Forms took care of this for me).  I really am better for it now.  

Tip #4 – Take a tangible project and do it in ASP.Net MVC.  Stick with it.  It is very different from Web Forms if this is where you are coming from.  Please read my post on Confessions of a Web Form Guy Learning ASP.Net MVC if you want a little insight into some of the "aha" moments I had. 

MVC is moving fast, as of this writing they are already on of MVC2 RC2.  A lot of new features are coming our way that make MVC that much sweeter. 

I hope this helps you in your journey!  If you know of some other good resources for ASP.Net MVC please comment on it.

Happy Coding!

Posted 03.05.2010  #    Comments [0]  | 
# Friday, 08 January 2010

In a couple of earlier posts I used jQuery to add a character counter to the ASP.Net AJAX HTML Editor.  I was contacted by a reader stating that when they modified the editor’s toolbar items the character counter stopped working.  After a small bit of troubleshooting I noticed the toolbar items drive the number of IFrames created.

The fix is small but requires some sleuthing that we can do with firebug to identify the correct IFrame we should be targeting.  With firebug console and our browser open to the url for our application we can type in $('#Editor1').contents().find('iframe').eq(0).  This grabs the first IFrame.  Be sure to change "Editor1" to the name of your control.

FirebugShot1

If we hover over the item in the brackets it will highlight the IFrame selected.  If the text area for the control isn’t highlighted then change the index value and try again until the text area is highlighted.

FirebugShot2

To be sure we have the right IFrame, click on the item in the brackets and it shows the HTML.  Look for the text you typed in to be sure.

FirebugShot3

Now that we’ve identified the correct index for the IFrame based on a customized editor we’ll need to change the GetEditor Function with this correct value.

function GetEditor() {
    return editor = $(Editor1).contents().find('iframe').eq(0);
}

You should be ready to go now! But wait! There’s more…

I was dissatisfied with having to hunt for the correct index value and thankfully there is a better way.  I noticed while trying this out on several scenarios that the IFrame we want is given the same ID.  At first, I didn’t think this would work since the ID is assigned by ASP.Net but it seems in this case we can trust it (could be my famous last words).  The IFrame is always given an ID of ControlName_ctl02_ctl00 or in this case Editor1_ctl02_ctl00.

Armed with this we can change the code to:

function GetEditor() {
    return editor = $(Editor1 + '_ctl02_ctl00')
}

That's a little better. We don't have to hunt for the correct index value for the IFrame used for the text.

Please download the code and give it a spin! AjaxHtmlEditorV3.zip (1.01 MB)

Happy Coding!

Read the earlier posts.

Posted 01.08.2010  #    Comments [2]  | 
# Tuesday, 22 December 2009

I made a small upgrade to an application that had a text box for date input to use the masked edit and calendar extender found in the asp.net control toolkit.  The two extenders are great together for allowing a user to select a date off the calendar or type it in without having to worry about the formatting.  The masked edit even has a nice auto complete feature that allows you to type the month and day without the year and the extender will add the year for you (if you want the current year of course).

Everything works well except for one small bug or feature when using these two extenders together.  If you set up your mask to be ''99/99/9999" but leave the calendar’s format to it’s default then on post backs with dates having one digit month or days the calendar control will reappear. 

In order to correct this, you have to set the format for the calendar to match your mask.  In the example above you would need to set the format to "MM/dd/yyyy" for the calendar extender.

Hopefully, someone will find this post before spending too long trying to figure out what is happening (like I did).

:-)

Posted 12.22.2009  #    Comments [0]  | 
# Saturday, 12 December 2009

Testing the routes in .Net MVC can be a bit tedious without some help.  MVCContrib’s TestHelper library eases the load and makes testing routes a breeze.

To demonstrate, I created an application to manage recipes with their ingredients and directions. Below is how I wanted the routing to work.

What I want the URL to look like What it will do
/recipe List all recipes
/recipe/blueberrypie Display blueberry pie recipe
/recipe/edit/blueberrypie Edit blueberry pie recipe
/recipe/blueberrypie/ingredients Show the ingredients for blueberry pie
/recipe/blueberrypie/directions Show directions for blueberry pie
/recipe/blueberrypie/ingredients/delete/1234 Delete ingredient with ID 1234 for blueberry pie & go back to the list of ingredients

I edited the RegisterRoutes method in the Global.asax.cs file to match the table:

// recipe/BlueberryPie/ingredients/edit/1234
routes.MapRoute(
    "Ingredients",
    "recipe/{nameKey}/ingredients/{action}/{id}",
    new { controller = "Ingredients", action = "Index" },
    new { id = @"\d+" }
    );

// recipe/BlueberryPie/ingredients/
routes.MapRoute(
    "IngredientsForRecipe",
    "recipe/{nameKey}/ingredients/{action}",
    new { controller = "Ingredients", action = "List" }
    );

// recipe/BlueberryPie/directions/edit/1234
routes.MapRoute(
    "Directions",
    "recipe/{nameKey}/directions/{action}/{id}",
    new { controller = "directions", action = "Index" },
    new { id = @"\d+" }
    );

// recipe/BlueberryPie/directions/
routes.MapRoute(
    "DirectionsForRecipe",
    "recipe/{nameKey}/directions/{action}",
    new { controller = "directions", action = "List" }
    );

// recipe/edit/BlueberryPie
routes.MapRoute(
    null,
    "recipe/{action}/{nameKey}",
    new { controller = "Recipe", action = "List", nameKey = "" },
    new { action = "create|edit|index|list" }
    );

// recipe/BlueberryPie
routes.MapRoute(
    "recipe",
    "recipe/{nameKey}",
     new { controller = "Recipe", action = "Index" }
    );

Now for the magic with MVCContrib’s TestHelper library and the extension ShouldMapTo:

[TestFixture]
public class RouteTests
{
    [TestFixtureSetUp]
    public void SetUp()
    {
        RouteTable.Routes.Clear();
        MvcApplication.RegisterRoutes(RouteTable.Routes); 
    }

    [Test]
    public void RootMatchesHome()
    {
        "~/".ShouldMapTo(x => x.Index());
    }

    [Test]
    public void RecipeRootMatchesList()
    {
        "~/recipe".ShouldMapTo(x => x.List());
    }

    [Test]
    public void RecipeNameShouldDisplayIndex()
    {
        "~/recipe/BlueberryPie".ShouldMapTo(x => x.Index("BlueberryPie")); 
    }

    [Test]
    public void CreateRecipeShouldDisplayCreateAction()
    {
        "~/recipe/create/".ShouldMapTo(x => x.Create());
    }

    [Test]
    public void EditRecipeShouldDisplayEditAction()
    {
        "~/recipe/edit/BlueberryPie".ShouldMapTo(x => x.Edit("BlueberryPie"));
    }
    [Test]
    public void DeleteRecipeShouldDisplayDeleteAction()
    {
        "~/recipe/delete/1".ShouldMapTo(x => x.Delete(1));
    }
    [Test]
    public void IngredientsForRecipeShouldDisplayIngredientsListAction()
    {
        "~/recipe/BlueberryPie/ingredients".ShouldMapTo(i => i.List("BlueberryPie")); 
    }
    [Test]
    public void DirectionsForRecipeShouldDisplayIngredientsListAction()
    {
        "~/recipe/BlueberryPie/directions".ShouldMapTo(i => i.List("BlueberryPie"));
    }
    [Test]
    public void DeleteIngredientShouldDisplayDeleteAction()
    {
        "~/recipe/BlueberryPie/ingredients/delete/1".ShouldMapTo(i => i.Delete(1));
    }
    [Test]
    public void DeleteDirectionShouldDisplayDeleteAction()
    {
        "~/recipe/BlueberryPie/directions/delete/1".ShouldMapTo(i => i.Delete(1));
    }
}

No need to mock/fake any context, setup route data, etc.  The code is short and sweet, easy to read, understand and matches the URL that would be in the browser making it very intuitive.

Please download the code.  The sample is using NUnit 2.5.

MVCContribTestRoutes.zip (603.49 KB)

Happy coding!

Posted 12.12.2009  #    Comments [0]  | 
# Friday, 11 December 2009

In an earlier post I added character counting functionality to the ASP.Net AJAX HTML Editor with jQuery.  Soon after posting the entry I noticed that cutting and pasting text into the editor via the context menu did not change the character count immediately.  I enhanced the jQuery code in my last post to do this.

var Editor1 = '#Editor1';
var Editor1CountLimit = 50
var Editor1InfoArea = '#Info';

$(document).ready(function() {
    TrackCharacterCount(Editor1, Editor1CountLimit, Editor1InfoArea);
});

function TrackCharacterCount(ctl, limit, info) {
    var editor = GetEditor();
    $(editor).load(function() {

    var body = $(this).contents().find('body');
    var txt = $(body).text();
    $(info).html(txt.length); //set initial value 

    $(body).bind('cut paste', function(e) {
            setTimeout(function() {
                var txt = $(body).text();
                UpdateTextCounter(txt,limit,info);
            }, 150)
        });
        
        $(this).contents().keyup(function() {
            var txt = $(this).contents().find('body').text();
            UpdateTextCounter(txt,limit,info);
        });
    });
}

function UpdateTextCounter(txt,limit,counter) {
    if (txt.length > limit)
        $(counter).html(txt.length).css("color", "red");
    else
        $(counter).html(txt.length).css("color", "");
} 

I added the bind code on line 17 to handle the cut and paste functionality.  I went through a couple iterations of the cut & paste functionality to count the characters correctly.  I was running into an issue where the textbox value was not being updated immediately with what was pasted.  Then I stumbled upon a nice stackoverflow answer that used a timeout to wait for the textbox to be updated.  This little jewel allowed me to simplify the code to what it is now.

I refactored the code to decrease duplication of code by creating the UpdateTextCounter function. 

I also noticed a subtle difference between IE7 and IE8 that stopped the counter from working and have corrected that in this version with a browser check.

function GetEditor() {
    if ($.browser.msie == 'true' && $.browser.version == '7.0')
        return editor = $(Editor1).contents().find('iframe').eq(1);
    else
        return editor = $(Editor1).contents().find('iframe').eq(2);
}

I verified the code runs on IE7, IE8, FF 3.5.5 & Chrome 3.0.

Enjoy!

AjaxHtmlEditorV2.zip (1.28 MB)

Read the first post.

Read the next post where I fix a bug when using a customized toolbox with the editor and find a better way to locate the IFrame that our text editor is in.

Posted 12.11.2009  #    Comments [0]  | 
# Wednesday, 25 November 2009

I believe strongly in teams being families.  We spend eight hours a day, five days a week with each other.  A great team is able to take everyone’s ups and downs, pluses and minuses, strengths and weaknesses with dignity, respect and healthy doses of humor.  A great team is a team that isn’t all alike and embraces and remains open to diversity of ideas, backgrounds and ways of thinking even when it’s hard for them to do so.

Lindy White was one of the most unique team members I have had the pleasure to work with and go on to lead.  His candor was unmatched and so was his personality.  His tenacity and intensity for development was infectious.  He was an extremely valuable team member.  He made us all better.

Many years back we moved our website to a CMS system.  Over the years, the site grew to have around eighty content liaisons (users who could write content).  Unfortunately, strewn throughout the website were email addresses.  Our website was responsible for a large amount of email cherry picking by bots.  The team discussed how we could handle this and Lindy was given the lead to develop the solution.

As time went on, Lindy being Lindy was unsatisfied with the solution being developed and struck out to find a better way.  What he came up with was pure gold.  He wrote an HTTP Module that intercepted the outgoing HTML and converted all email addresses to a format bots could not use/read. With zero changes to our end users and next to zero maintenance on our part the web bots can no longer steal email addresses from the website.  I nominated him for an IT Pro Hero award through Windows IT Pro magazine.  Here’s the article:

http://windowsitpro.com/article/articleid/100073/lock-out-spambots.html

Lindy knew of the award but sadly he passed away before he could see the article with his own eyes. 

It’s been a little over a year since he left us and I still think about him all the time.  Lindy, I miss you and you will always have a special place in my heart.  Thanks for the memories!

Posted 11.25.2009  #    Comments [0]  | 

I have been developing with ASP.Net since it debuted.  Before that I was doing windows form development with VB and classic ASP web development.  The advent of ASP.Net was phenomenal for me and my team.  The web forms model allowed our team to continue using the Windows event driven model over the web and allowed us to take our web development to a new level than classic ASP.  We quickly dispatched of our windows development in favor of the simple deploy / update model and gladly left the dreaded PC install / update nightmare.

Fast forward many years and ASP.Net matured quite a bit, Windows Forms had click once and all sorts of other goodies in the framework.  Now MVC.  My experience with MVC up until this point had only been my reading in the Gang of Four book and the occasional blog entry.  While all this was going on I had (and still am) been on a mission to improve my programming skills.  The majority of this was and has been reading, reading, and more reading.  I started trying out test driven development with a side project of mine.  The project was web forms based.  I was greatly encouraged with TDD but had read all the difficulties with TDD and web forms.  I had just begun this side project and decided why not make the switch to MVC?  So I did.

Along the way, I have had a lot of  "aha" moments.  Here’s some of them:

Web forms code behind faked me out. I thought I had good separation of concerns. Or I really abused the code behind file but I thought I was doing right.

For quite a while I knew large code behind files were not healthy.  I had been working as best as possible to decrease the code in them.  However, with my MVC journey I think I have come to see just how unhealthy large code behind files are and just because the code was not in the same file as my html did not mean I had a strong separation.  MVC by it’s nature encourages good separation and all the books and blog entries I have read show this by default.  In the web forms world I had to seek this out (not so much today but definitely in the early days and habits can be hard to change).

I’m having data source withdrawals and View State withdrawals.

I’m nearly over it now but this was a big hurdle for me.  It’s amazing how reliant I became on this but it shouldn’t be too much of a surprise since this is the magic of Web Forms. 

I'm loving having control back of the HTML and the HTML is actually readable now.

This is definitely has been a pleasant return to HTML for me.  With web forms I went on autopilot for much of the HTML / JavaScript that my apps produced.  I just lived with it even when I saw disgusting and bloated source.  Many took great pains to make web forms bend to their HTML will and I certainly understand why.  For me though, this is one of the reasons I like web forms so it didn’t bother me much.

So why would I want the control back.  Simple, because I can easily have it back now and the trade off to me doesn’t really feel like a trade off.  MVC is far more bare bones by design but I still don’t feel like I am back at the classic ASP spaghetti code.  It’s this bare bones nature that I am really digging.  I have a lot more control over everything and even though I may not want to control a lot I know I have the option to.  I do miss at times the "drag and drop a control unto the canvas and go" mentality but with jQuery and HTML helpers I am missing it less and less as I become more proficient.

Hello, JavaScript. It's been a while since we last worked together.

With web forms, I enjoyed the luxury of not having to really getting down and dirty with JavaScript.  I used everything at my disposal to use a control and/or post backs to handle what was needed and only used JavaScript when I needed to.  Mind you, I realized this came with a price of some nice user interactions but I was willing to pay that price.  JavaScript and the DOM specifically were always giving me fits. 

Unfortunately, this experience in the long ago past blinded me to the wonderful things going on with JavaScript over the years.  With JavaScript libraries like jQuery the capabilities that once required a JavaScript and DOM wizard are now at your fingertips.  I have fallen for JavaScript and am ashamed I had closed my mind off to it for so long. 

MVC by it’s nature encourages JavaScript for rich UI interaction much like controls did for Web Forms but so much more.

Testing? Ya, I did that. It's F5 right?

MVC was not my start on considering better ways to test but everything I read and worked with on MVC had testing involved all the way.  Every book I have read, every beginning MVC blog entry has testing intertwined.  My first attempts at unit testing was with a web forms project and I was absolutely convinced it would make me a better developer, faster developer and create more maintainable code.  However, with web forms, testing for me and many I suspect was changing some code and hitting F5 to run the application and manually test.  With a little unit testing / TDD under my belt I have come to see how much more efficient it is to "F5 testing". 

MVC permeates with the idea of unit testing and that’s by design.  I have much to learn on my testing journey but it has already paid dividends for me that has me convinced it’s an important tool in my toolset.

I am working in harmony with the HTTP protocol now.

I mentioned one of the reasons I liked web forms is that it allowed me a very easy transition from windows forms development.  As much as I would like to say I understood how the web really worked underneath the hood I know now that I didn’t.  Yes, I knew it was stateless and all that jazz but with web forms I was abstracted away from the way the web worked. 

When I started learning MVC it became painfully obvious how much I had forgotten and/or didn’t really understand about how the web works.  I am still tripping over this while I continue my MVC journey but I am happy to say that the knowledge gained in understanding more deeply how the HTTP protocol really works is making me a better web developer.  “Working in harmony” is a paraphrased quote from "Pro ASP.NET MVC Framework" by Steven Sanderson.

I am enjoying my MVC journey and look forward to becoming extremely proficient with it.  I will also admit that much of my 'aha' moments are not because of Web Forms, rather they were mainly a result of my own lack of exploration into what was possible and what was out there.  I was too busy keeping my head down and firing out widgets instead of really trying to hone my craft at all times while spitting out widgets. :-) This has changed for me now.  I have an extremely strong desire to be a real craftsman and not just someone who can bang on a keyboard.

Web Forms is a great tool and I imagine I will continue to have it as a tool in my toolset for a long time to come along with MVC.

Posted 11.25.2009  #    Comments [0]  | 
# Saturday, 14 November 2009

I’m using SyntaxHighlighter and it’s awesome!  It’s completely JavaScript based and can be put into dare I say any blog engine.  If your engine can take JavaScript that is.  I use dasBlog and found two posts on it that guided me nicely.  Give it a shot.  Spruce up your code snippets in your blog.  Everyone will thank you for it.

Scott Hanselman’s blog got me started and Justin Thirkell finished it off.

Anybody writing code on their blog should take a look at SyntaxHighlighter. 

Posted 11.14.2009  #    Comments [0]  | 

Recently, one of our ASP.Net WebForm app’s had a new requirement for formatted text entry. No problem, the HTML Editor control from the AJAX control toolkit would fit the bill nicely.

In this new version, we were asked to show the count of characters used as a user types and enforce a maximum character count allowed.  Unfortunately, the HTML Editor doesn't have anything built in to give this information and it's a conglomerate of many controls such as iframes and hidden text area's that make this a slightly less than trivial task (at least it felt that way initially).

After some thought on this and seeing a lot of great stuff from jQuery I figured it was up to the task. It didn't disappoint.  Here's what I came up with.

var Editor1 = '#Editor1';
var Editor1CountLimit = 50
var Editor1InfoArea = '#Info';

$(document).ready(function() {
  TrackCharacterCount(Editor1, Editor1CountLimit, Editor1InfoArea);
});

function TrackCharacterCount(ctl, limit, info) {
  var editor = $(ctl)contents().find('iframe').eq(1);
  $(editor).load(function() {
      var txt = $(this).contents().find('body').text();
      $(info).html(txt.length); //set initial value 
    $(this).contents().keyup(function() {
      var txt = $(this).contents().find('body').text();
      
      if (txt.length > limit)
        $(info).html(txt.length).css("color", "red");
      else
        $(info).html(txt.length).css("color", "");
     });  
   });
}

My HTML looks like this:

Info

When the document is ready we call the TrackCharacterCount function that takes the ID of the HTML Editor control, then the character limit, and last the element you want to have the character count displayed in.   Be sure to replace these parameters with your values.  

The second iframe contains the contents of the text typed.  The iframe contains a document and the text we are after is in the body tag.  I did some jQuery spelunking in firebug’s console to figure this out.  I added a little touch to have the div element turn red if the user goes over the limit passed into the function. 

In order to enforce the maximum length allowed, I set a custom validator to call the below function.

function ValidateEditor1Length(source, args) {
  var editor = $(Editor1).contents().find('iframe').eq(1);
  var txt = editor.contents().find('body').text();
  var isValid = txt.length > 0 && txt.length <= Editor1CountLimit;
    args.IsValid = isValid;
}

Some jQuery goodness I found along the way.  My first attempt at counting the characters retrieved the entire HTML (used .html()) and then removed the HTML with a regular expression.  It was a little later that I found .text() which simply returns all of the text without markup.  Thank you jQuery!!!!

Here’s a shot of what it looks like before they go over the limit:

HTMLEditor1

And after the go over the limit…

HTMLEditor2

Now, I’m not a jQuery wizard.  In fact, this is my first jQuery foray and I am extremely impressed with jQuery so far.

AjaxHtmlEditor.zip (1.28 MB)

Read my second post where I add functionality to capture the context menu’s cut and paste.

Posted 11.14.2009  #    Comments [0]  |