Damon Armstrong

Caffeine Induced Tirades about .NET and Life
And don't forget to check out my latest Simple-Talk articles
Add to Technorati Favorites      Add to Google     

  • Avoid Parenthesis in VS 2005/2008 Web Site Project Paths

    Posted Saturday, April 19, 2008 12:44 AM | 0 Comments

    I was developing against a web site project -- you know the ones in Visual Studio 2005/2008 where you can just point to a directory and do your thing (as compared to a web application project with an actual project file) -- and I noticed something a bit odd.  When I compiled the project the Error List was not showing file references, line numbers, columns, or any other information about the location of the compiler errors. 

    I figured Visual Studio was just getting tired since it had been open for days, so I restarted the application in hopes that it would straighten out.  No such luck.  Then I restarted the computer.  That's what tech support normally recommends anyway, so I figured I'd start with the basics.  Once again, no error location information. 

    At this point, I was starting to wonder if it was an issue with Visual Studio 2008 that nobody had noticed yet.  Of course, if VS 2008 wasn't showing error location information on web site projects then there probably would have been an outcry long before this.  So I hit the search engines. 

    It was a pain to find, which is why I'm trying to help publicize the issue, but there are a few references to the exact issue I was having.  Specifically on the following sites:

    http://www.developmentnow.com/g/51_2006_7_0_0_790825/Errors-in-Error-List-with-no-line-numbers.htm

    http://www.dotnetmonster.com/Uwe/Forum.aspx/vs-net-ide/4006/Error-List-Missing-the-Code-File-Reference

    What was the problem?  I was using parenthesis in the path to the web site project.  After removing the parenthesis, the problem was gone.

  • Disabling an ASP.NET Button when Clicked

    Posted Thursday, December 20, 2007 11:55 PM | 7 Comments

    I was answering a question in the ASP.NET forums on Simple-Talk.com (click here you want to check it out) about how to avoid the issue of users submitting multiple page requests when they click on buttons.  My first inclination was hey, just disable the button after they click it and everything will be just fine.  So I fired up Visual Studio and threw the following code into a page:

    <asp:Button runat="server" Text="My Button" OnClientClick="this.disabled=true;" />


    And naturally it failed miserably.  Why?  Because nothing works on the first try -- unless fate is trying to store up some catastrophe points to use on you later.  And apparently if you disable a button in the onclick it doesn't post back for reasons that I haven't dug into very far.  Not to be outwitted by HTML, Javascript, .NET, or whatever else you can blame for your problems, I tried a myriad of random ideas until I hit upon this -- instead of disabling the button when you click it, set the onclick event to fire a function that returns false.  So it looks something like this:

    <asp:Button runat="server" Text="My Button" OnClientClick="this.onclick=new Function('return false;');" />


    This approach effectively disables the button, but doesn't gray it out.  So there you go.  If you want a gray button then this isn't the blog for you, but it does keep people from clicking on it the button more than once.  Naturally, this means that some sticklers out there will whine because the button stays disabled if the request times out or the viewer stops the request manually.  Part of me says leave it disabled as vengeful tribute to the multitudes who quintuple-clicked your button and created a horde of duplicate records for you to delete.  Then there's the other part of me that likes the JavaScript setTimeout function and a bit of a challenge:

    <script type="text/javascript"
       
    function disableButton(button, resetDelay)
       
    {
            button.oldonclick
    = button.onclick;
           
    button.onclick=noClick; //new Function("return false;";
           
    setTimeout("enableButton('"+ button.id + "');" resetDelay);                                    
       
    }
       
    function noClick()
       
    {
            alert
    ("Chill - You already submitted this page once. "+
                   
    "Submitting it twice isn't going to make the "
                   
    "server go faster.  Quit hitting the "+
                   
    "freakin' button!";
           
    return false;
       
    }
       
    function enableButton(buttonId)
       
    {
            var button
    = document.getElementById(buttonId);
           
    if(button!=null)
           
    {
                button.onclick
    = button.oldonclick;
           
    }
        }
    </script>


    The disableButton function copies the "standard" onclick method into a fake property of the button for safe keeping, sets the new onclick to the noClick function (which displays a nice message to the user if they DO try to click), then sets up a delayed function call (read--asynchronous method) that executes after a delay that you set when calling the disableButton method (a value of 1000 for the resetDelay = 1 second, so multiply accordingly for your needs).  Here's an example of what it looks like in a button:

    <asp:Button runat="server" Text="My Button" OnClientClick="disableButton(this,30000)" />


    When you click this button, it gets disabled for 30 seconds.  And if a user clicks on that button in the interim, they even get a little feedback on what's going on.  Just remember, don't blame me when you get fired for pasting the script into your project and forgetting to change the message.

  • WPF: WindowsFormsHost Control Error

    Posted Tuesday, September 04, 2007 7:39 PM | 0 Comments

    I had the code base for my first WPF application ready to go, so I compiled a release version of software and tried to run it from the .exe instead of directly from Visual Studio.  I was promptly greeted with the following error:

    The tag 'WindowsFormsHost' does not exist in XML namespace 'http://schemas.microsoft.com/winfx/2006/xaml/presentation'.

    It ran flawlessly from the Visual Studio .NET debugger, which I found a bit odd.  I tracked the issue back to the following problem.  When I originally referenced the WindowsFormsIntegration.DLL from my project, the "Copy Local" option was set to false.  When I went back in and set that value to True, the application promptly broke when it ran in the debugger.  I don't mind when things break as long as they break consistently, so this was a step in the right direction.  I had found a few helpful forum entries informing me that you have to specify the xml namespace for the WindowsFormsIntegration assembly.  So I added the following xmlns entry:

    xmlns:wfi="clr-namespace:System.Windows.Forms.Integration;assembly=WindowsFormsIntegration"

    And changed my WindowsFormsIntegration tag from

    <WindowsFormsHost>
        ...
    </WindowsFormsHost>

    to

    <wfi:WindowsFormsHost>
        ...
    </wfi:WindowsFormsHost>

    and it started working just fine.

  • Simple Code Performance Testing

    Posted Friday, August 24, 2007 3:44 PM | 0 Comments

    After posting Performance: Caching vs. Reading from an In-Memory XML Document, there have been some questions about how I actually do the performance testing.  My approach to performance testing is really simple... I just write some code, run that code in a big for loop, and time how long it takes to run through all of those iterations.  Nothing too complicated.  Calculating the speed of the operation becomes a simple matter of (iterations / time).  I've packaged this testing routine into a class I call the PerformanceTimer class, which simplifies things even more.  The code for the class follows, and an example of how to use the class to test a routine follows even further down.


    using System;

    namespace Rebel.Performance
    {
        
    /// <summary>
        ///   Delegate to a testing method
        /// </summary>
        
    public delegate void TestingMethodDelegate(int iterations);

        
    /// <summary>
        ///   Executes a testing method and stores execution duration
        /// </summary>
        
    public class PerformanceTimer
        {
            
    /// <summary>
            ///   Property backer for the ExecutionSpan property
            /// </summary>
            
    private TimeSpan _propExecutionSpan;
            
            
    /// <summary>
            ///   Property backer for the Iterations property
            /// </summary>
            
    private int _propIterations;

            
    /// <summary>
            ///   Duration of testing method execution
            /// </summary>
            
    public TimeSpan ExecutionSpan
            {
                get { 
    return _propExecutionSpan}
                
    private set { _propExecutionSpan value}
            }

            
    /// <summary>
            ///   Number of iterations for the test
            /// </summary>
            
    public int Iterations
            {
                get { 
    return _propIterations}
                
    private set { _propIterations value}
            }

            
    /// <summary>
            ///   Executes testing method and determines execution duration
            /// </summary>
            /// <param name="testingMethod">Delegate to the testing method</param>
            
    public void Run(int iterationsTestingMethodDelegate testingMethod)
            
    {
                Iterations 
    iterations;
                
    long startTimeendTime;
                
    startTime DateTime.Now.Ticks;
                
    testingMethod.Invoke(Iterations);
                
    endTime DateTime.Now.Ticks;
                
    ExecutionSpan = new TimeSpan(endTime startTime);
            
    }

            
    /// <summary>
            ///   Number of iterations per second
            /// </summary>
            
    public double IterationsPerSecond
            {
                get
                {
                    
    return (Iterations ExecutionSpan.TotalSeconds);
                
    }
            }

            
    /// <summary>
            ///   Number of iterations per millisecond
            /// </summary>
            
    public double IterationsPerMillisecond
            {
                get
                {
                    
    return (Iterations ExecutionSpan.TotalMilliseconds);
                
    }
            }

        } 
    //class

    //namespace


    So that's the class, but how do you use it?  Here's a simple test application that uses the PerformanceTimer class to check the speed of concatenation operations.  You've always heard that using a StringBuilder to build a string is faster than repeatedly concatenating a string directly?  Here's a chance to actually prove it.   


    using System;
    using System.Text;
    using Rebel.Performance;

    namespace Rebel.PerformanceTest
    {
        
    /// <summary>
        ///   Console Application
        /// </summary>
        
    class Program
        {

            
    static void Main(string[] args)
            
    {
                PerformanceTimer timer 
    = new PerformanceTimer();
                
    int iterations 100000;

                
    //Run TestA
                
    timer.Run(iterationsnew TestingMethodDelegate(TestMethodA));
                
    Console.WriteLine(timer.IterationsPerMillisecond);
                
                
    //Run TestB
                
    timer.Run(iterationsnew TestingMethodDelegate(TestMethodB));
                
    Console.WriteLine(timer.IterationsPerMillisecond);

                
    Console.ReadLine();

            
    }
            
            
    /// <summary>
            ///   Concatenation using a StringBuilder
            /// </summary>
            
    static void TestMethodA(int iterations)
            
    {
                StringBuilder s 
    = new StringBuilder();
                
    for (int 0iterationsi++)
                
    {
                    s.Append
    ("A");
                
    }
            }

            
    /// <summary>
            ///   Direct concatenation
            /// </summary>
            
    static void TestMethodB(int iterations)
            
    {
                
    string "";
                
    for (int 0iterationsi++)
                
    {
                    s 
    +"A";
                
    }
            }

        } 
    //class

    //namespace
    WARNING: You will want to use as large of an iteration count as possible when testing.  This performance testing approach does the "best" it can when calculating exact start and end times, so some fluctuations are bound to occur when capturing the start and end times.  When they do, the fluctuations throw off the calculations.  Larger iteration counts spread out the effect of the fluctuations and minimize the error.  Think of it this way:  if you are off by 1 in 10, then you have a 10% error.  If you are off by 1 in 10000, you have a 0.01% error.


    On my machine, the StringBuilder operates at 14629 iterations / second and the direct concatenation approach runs at 13 iterations / second.  Pretty significant difference. 

  • Performance: Caching vs. Reading from an In-Memory XML Document

    Posted Wednesday, August 22, 2007 1:01 AM | 4 Comments

    I've been working with a number of XML documents recently.  Basically I'm trying to expose configuration values stored in XML configuration files in an object-oriented structure.  I'm using an in-memory XML document and just referencing values as needed from that document.  The XML reading does not need to be super-high performance because it does not occur very often, but it got me to thinking... what if it did?

    So I ran some performance tests to check the difference between accessing a value from an XML document vs. storing the value in cache and accessing it through a standard get property. 

    The results (at least on my machine) are as follows:

     

    Iterations / Millisecond

    Performance Difference
    XML (attribute value)

    8,648

    90% slower

    XML (inner text value)

    21,694

    76% slower

    Cached Property

    90,140

     

    I knew caching would beat out the other two methods since it's very fast to access a direct variable, but I was surprised that there was such a difference.  I figured there would be a bit of overhead for the inner text property, and a bit more for the attribute lookup, but I was thinking it would be in the range of 10-20% slower.  Live and learn.  Of course, reading from an in-memory XML document is still pretty dang fast, it's just that reading from a cached value is faster.

  • ADFS - Cookie Error

    Posted Thursday, August 16, 2007 1:11 AM | 0 Comments

    I've been knee deep in Active Directory Federation Services for the past three months now, and when helping one our clients deploy a single-sign-on (SSO) application, we ran into a nasty error:

    The request has been rejected because it appears to be a duplicate of a request from this same client browser session within the last 20 seconds

    Here is a quick synopsis of the problem.  ADFS works by bouncing users back and forth between federation servers and the application, and somewhere along the way you end up with a cookie containing authentication information (a SAML token to be more precise).  One of the options in the web.config is the path to the cookie, which is found in

    <configuration>
         <web.config>
              ...
              <websso>
                   ...
                   <cookies writecookies="true">
                        ...
                        <path>/CookiePath</path>
                   </cookies>
              </websso>
         </web.config>
    </configuration>

    During the deployment, we placed the application in a directory that did not match the cookie path.  So, the user was going to the application, the application then sent the user to the federation server for authentication, the federation server issued the cookie, redirected the user back to the application, the cookie was not being sent when the user hit the application a second time, so the application sent the user back to the federation server for authentication, and the federation server doesn't like it when you try to login twice within 20 seconds.  So the moral of the story is to make sure you setup your cookie path correctly or you get weird ADFS errors.

  • Reclaim Microsoft Virtual PC Console that Displays Outside the Desktop

    Posted Wednesday, May 30, 2007 8:19 PM | 1 Comments

    I ran into a bit of a problem the other day with Microsoft Virtual PC staring outside of my viewable desktop area.  And it turns out that it wasn't just a fluke, because it's happened a couple of times since then.  And Robby, one of the guys I work with at Cogent, had the same problem.  Here's how you get around the issue (I assume this works for any window if you have this issue, not just VPC):

    1. Right click on the Microsoft Virtual PC Taskbar item and choose "Move" from the context menu
    2. Hold the CTRL button and hit your arrow keys a few times.  This makes the title bar of the window "stick" to the mouse.
    3. Wave your moue around wildly until you see the window
    4. Click the left mouse button and the title bar will "unstick"

    That should get your window back.

  • Wordpad may corrupt your SharePoint (and other ASP.NET app's) Web.confg

    Posted Friday, May 18, 2007 12:08 AM | 7 Comments

    I was remoting into a SharePoint 2007 (MOSS) box yesterday because I needed to update the configuration on a few of the servers in the farm.  Like most production systems, the server did not have Visual Studio, so I was using Notepad to edit the configuration.  As you may know, navigating through a configuration file in Notepad is a bit of pain compared to Visual Studio because it lacks the finesse of a full featured text editor.  My connection was also fairly slow so it was making my configuration task more irksome than normal.

    Hoping to get a bit more control, I made the switch to Wordpad.  After making a couple of simple configuration changes, I refreshed the SharePoint site to make sure everything was okay.  I was greeted with an error screen.  Thinking that I must have made a typo, I checked over the configuration changes but didn’t see anything unusual.  So I decided to start removing things until the error went away.  Each time I removed a change, I refreshed the screen hoping to identify the issue.  The error screen persisted. 

    Finally, I reverted back to the backup and the page popped back up no problem.  That’s when I started thinking something was really amiss.  I opened the working Web.config in Wordpad  and saved the file without making any changes.  The error was back.

    I don’t know why, but Word pad randomly places little questions marks at various intervals throughout the Web.config.  I saw no rhyme or reason to the madness, but I confirmed it on multiple Windows 2003 servers.  So, if you’re configuring SharePoint, or any .NET application for that matter, on a remote machine, make sure to shy away from Wordpad because you’ll save yourself a headache.

    Click on the following image to see the differences in the files:

  • Displaying Debugging Info for SharePoint Errors in Your Browser

    Posted Thursday, May 17, 2007 12:28 PM | 0 Comments

    SharePoint does a good job of hiding errors from users.  Out of the box, unhandled errors in SharePoint result in a fairly non-descript page that says "An Error Occurred" (or something to that effect).  Although a good practice for your end-users, it's pretty annoying when you're trying to get a piece of code working or a configuration setting correct.  Here's how you can display debugging information in your browser:

    • Find the following line in your web.config:

      <SafeMode MaxControls="200" CallStack="false" DirectFileDependencies="10" TotalFileDependencies="50" AllowPageLevelTrace="false">
    • Change the CallStack attribute to true
    • Change the AllowPageLevelTrace to true
    • Find the following line in your web.config:

      <customErrors mode="On" />
    • Change the mode to "Off"

    The next time you have an error, it displays the familiar ASP.NET error information page with exception details and the callstack information.  Much less frustrating than a blasé error message.

  • VS SDK Error Message: CTC : fatal error CTC2013: Can't start preprocessor (2)

    Posted Wednesday, April 11, 2007 8:42 AM | 0 Comments

    After watching some of the demo videos of the latest Visual Studio SDK, I was excited to get started and try out my own add-in.  I installed the SDK, fired up Visual Studio, created an integration package, compiled, and got a nasty error:

    CTC : fatal error CTC2013: Can't start preprocessor (2)

    What does this mean?  Apparently, part of the integration package includes something that needs to be compiled by a C++ compiler.  When originally looking through the options of my Visual Studio install, I noticed that the C++ compiler likes to take up 1-2 gigs of space, and not being a C++ developer, I choose to forgo that option.  Apparently C++ has come back to bite me years after I thought I was done with it forever.

    All you have to do to get around the error is go back and install the C++ compiler from your Visual Studio 2005 disc, then compile the project again.  It should work.

    If you get a "Requires elevation" error during the compilation, remember that you're running Vista and you need to fire up Visual Studio in administrator mode.

  • SharePoint Permission Names

    Posted Friday, March 23, 2007 1:01 PM | 1 Comments

    SharePoint 2007 has a new control called the SPSecurityTrimmedControl.  It's a container control that allows you to hide or display a section of your page depending on whether or not the currently logged-in user has the appropriate permissions.  To set the permissions, however, you need to know what SharePoint calls the permissions.  Took a little while to drudge it up, but here's the list:

    http://msdn2.microsoft.com/en-us/library/microsoft.sharepoint.spbasepermissions.aspx

  • Exciting New .NET Language Features

    Posted Friday, March 16, 2007 12:08 PM | 0 Comments

    If you haven't already, check out Scott Guthrie's blog about Extension Methods that will be available when the next version of the .NET framework.  It's always cool when they add a completely new feature because it opens up more options for really elegant solutions.  And it also opens up new ways for people to really abuse the feature too.  And for me, both are fun to watch =]

  • It's Called Capitalism, and You're Just as Guilty!

    Posted Thursday, March 15, 2007 2:35 PM | 5 Comments

    Action 19 News in Ohio was hot on the investigative trail of a Girl Scout cookie scalper today.  Apparently, the owner of a Valero gas station in Parma Heights, Ohio bought a LOT of Girl Scout cookie boxes and is selling them in his store for $4 dollars.  Girls Scouts, on the other hand, peddle the boxes boxes for $3 dollars door to door.  Kate Donahue of the Girl Scouts of Lake Erie called the Valero owner's actions dishonorable and dishonest. 

    Dishonorable?  Dishonest?  How so? 

    Now, I must concede that I am not in possession of all the facts surrounding the situation because the news clip was about two minutes long and didn't go into much detail.  There were only two piece of information that you can really get from it.  First, the owner was buying the boxes for $3 and selling them for $4.  Second, the boxes say that resale of the boxes is unauthorized.  So, I'm running with those two facts.

    From what I understand, the Girl Scouts sell boxes of cookies for $3, and the owner of the store purchased the boxes for $3 dollar a box.  Nobody was complaining about non-payment, so it was obviously not an issue.  The Girl Scouts received all that they desired for the boxes of cookies.  No dishonesty or dishonorable behavior there.

    In his store, he sold the cookies for $4.  People paid $4.  People got their cookies.  From what I understand, there was no misleading signage saying anything about giving every single last dollar to the Girl Scouts.  So, no dishonor or dishonesty there. 

    But what about making an extra dollar off the box?  Dawn Hendrick, the reporter on the case, brought up a good point when she asked why she had to spend $4 on a box of cookies at the Valero when the Girl Scouts were selling them for $3.  Here's why Dawn: you bought it from a Valero and not from a Girl Scout!

    There are only two types of people in a purchasing situation.  The first type of person is the type who does not know the going rate of an item.  This person has to rely on their own perception of value to determine if a price is correct.  If they want cookies, see a box, and determine that the prices is reasonable, then they purchase that box of cookies.  By purchasing the cookies, they are agreeing that the price is reasonable and correct.

    The second type of person is the type who does know the going rate of an item.  If they see a price that is above the going rate, they have a decision to make: is getting the item at the current location worth the additional cost?  If it's not, then the buyer can leave.  By purchasing the box, however, the buyer is agreeing that the extra cost is justified due to convenience. 

    If you can sell and item for more than you bought it for, then you have brought value to the person buying it.  It may be perceived value, or convenience value, but you have provided value none-the-less.  Pocketing the difference is called profit, and it's how the world works.  In fact, I can guarantee you that the Girl Scouts are not spending $3 dollars to MAKE the box of cookies that they are selling for $3.  So their stance against someone selling their cookies at a profit seems a bit irrational.  And considering that their sales force consists of tens of thousands of unpaid child laborers, I would say that they have a pretty good racket going on themselves.

    My favorite part of the newscast is when a Girl Scout says "We use our money for our troop and other stuff, and he just gets it for himself to spend."  It was meant to be a damning condemnation from the mouth of a child, and that's how it comes across in the interview. But the reality is that shows the absurdity of the outrage towards the Valero owner.  Basically, the Girl Scouts are out selling cookies because they need money to pay for Scouting related activities.  The Valero owner is selling products at his gas station because he needs money to pay for living related activities.  So why is the Valero owner being accused of perpetuating some great evil upon the world when the Girl Scouts are engaging in the same act? 

    Some would then argue that since boxes of Girls Scout Cookies say "Any Resale or Redistribution is Unauthorized" that the owner was in violation of some law.  As far as I know, the Girl Scouts do not regular commerce, and the printing on the side of the box does not constitute a binding contract.  Re-selling the item is not a legal offense.  Since he never claimed to be an "authorized" reseller, I see no dishonesty.  Some would go as far as to say that it was unethical to sell the boxes when such a message exists on the box, and thus dishonorable.  Ethics is far more subjective than law, but I would still say that this is not beyond the bound of ethics because no official agreement exists.  Unless there is an official agreement, the Girl Scouts have no right to dictate how someone uses what they bought.

    I will, however, concede that selling Girl Scout cookies for a profit has the potential to cut into Girl Scout's cookie revenue.  When someone pays $12 dollars for Girl Scout cookies from a Girl Scout, they get 4 boxes and $12 goes to the Girl Scouts.  At Valero, $12 gets you 3 boxes and only $9 dollars goes to the Girl Scouts.  But we are also talking about a luxury item, and most people are going to get however many boxes they want without much regard for the price.  As such, the lost revenue angle is not an overly compelling argument.  And remember, regardless of where you get the box from, $3 bucks DOES go to the Girl Scouts. 

    Sounds to me like the Girl Scouts are unhappy because they didn't think to raise the price to $4 a box first. 

    Perhaps the real solution to the problem would be for the owner to stockpile cookies until AFTER the Girl Scouts have done all their selling.  Then four months later when everyone's supply of Thin Mints, Tagalongs, and Somoas are depleted, put them out on the shelves for $6 per box.  I'm addicted to Somoas, so I know I'd be pretty tempted to pony up for a box.

  • Vengence via Google Ads

    Posted Thursday, January 25, 2007 11:03 PM | 4 Comments

    I was reading an article today about how GoDaddy removed a customer's domain name from their registry at the request of MySpace.com.  You can view it here:

    GoDaddy Pulls Security Site After MySpace Complaints

    Apparently someone had posted usernames and passwords of MySpace users to SecLists.org, and MySpace wanted the offending material to be removed immediately.  Instead of contacting the website owner, MySpace contact GoDaddy and requested the domain deletion.  This effectively took the entire site offline, not just the offending material.   I think there are going to be more than a few people who are a bit miffed at GoDaddy for going along with the request, but what can you do?

    And that's when I saw something interesting.  Immediately following the article was a "Sponsored Links" section containing Google ads.  And guess who was right at the top of the list?  GoDaddy.  Check out the article and scroll to the bottom, it will probably be there for you as well.

    This got me thinking.  Google Ads, from what I understand, work on a per-click basis.  You click on an ad, and Google charges the company for that click.  So you can exact some menial form of personal vengeance on companies that you feel deserve your wraith by clicking on their Google Ads with no intention of buying anything.

    Or you employ your friends, family, coworkers, and people on the web to wage an all out vengeance ad click war to broadside the advertising efforts of a company.

    I'm sure the day is comming.

  • Pro CSS Techniques

    Posted Saturday, December 23, 2006 1:13 AM | 0 Comments

    As a developer, I’ve had to endure my fair share of designers lecturing on the virtues of Cascading Style Sheets (CSS) and maintaining a strict divide between content elements and their visual layout.  It’s a noble pursuit, but for a developer with little design experience the task of getting a page to look just the way you want using CSS can be a difficult and frustrating undertaking that quickly regresses back to the form I know best:  using table after table for layouts and haphazardly slapping style strings into various elements until the page comes out looking right.  In other words, the approach designers hate.
    Instead of abiding in my ignorance of CSS, I decided to pick up a copy of Pro CSS Techniques by Jeff Croft, Ian Lloyd, and Dan Rubin.  It’s an intermediate to advanced-level book on CSS that assumes you have some basic knowledge of CSS.  If you feel comfortable using styles directly in your HTML, then you’ll be comfortable reading through this book.  The authors did a great job of covering the basics enough to inform you if you need it, but not dwelling on it to the point of boredom if you do not.  They also did a great job of explaining the “how” and “why” behind some of the most confusing aspects of CSS, which is why I definitely recommend picking up a copy.

    Pro CSS Techniques starts out with a great introduction to Semantic HTML, the concept that HTML should only define what is on the page, not what it should look like.  It also discusses why you need to write Semantic HTML if you want to use CSS effectively.  After understanding this concept in more detail, I understand why I ran into so many walls trying to use CSS to style my pages:  I was fighting an uphill battle against the layout and style information that was already in my HTML document. 
    After outlining some of the theory behind CSS, the authors discuss the CSS language and syntax.  If you are novice with CSS, this will bring you up to speed very quickly.  The section discussing CSS selectors was invaluable.  I’ve read a number of online tutorials for CSS and none of them ever got into the detail offered by this book.  And if you’ve ever wondered about the “Cascading” part of Cascading Style Sheets, then you’ll find a complete overview about the intricacies of that as well.

    You will also find an entire chapter containing valuable information on how the various browsers handle CSS and how to get your pages looking consistent across those browsers without hacks.  And when a traditional approach won’t work, you can turn to the chapter entirely dedicated to CSS Hacks that discusses what they are, what they do, when to use them, when to avoid them, and how to use them in your CSS.
    As you work with CSS, you can use the book as a guide because it breaks out common tasks into individual chapters.  You can easily thumb through to the applicable section as you build the layout, create common page elements (headers, footers, navigation, breadcrumbs, sidebars, etc), style text, tables, forms, and lists.  I remember the first time I saw a bulleted list turned into a graphical menu and I attributed it to dark magic.  Now I know how it was really done.

    And let’s not forget my favorite chapter, “Everything Falls Apart”, which describes most of my experience with CSS up to now.  It covers what to do when things aren’t working, and their solutions are much more elegant than returning to nested table layouts and forgoing the principals of CSS.

    I usually like to know what’s in a book, so there’s a chapter listing in case you’re curious:

    • Chapter 1 - The Promise of CSS
    • Chapter 2 - The Language of Style Sheets
    • Chapter 3 - Specificity and the Cascade
    • Chapter 4 - The Browsers
    • Chapter 5 - Managing CSS Files
    • Chapter 6 - Hacks and Workarounds
    • Chapter 7 - CSS Layouts
    • Chapter 8 - Creating Common Page Elements
    • Chapter 9 - Typography
    • Chapter 10 - Styling Tables
    • Chapter 11 - Styling Forms
    • Chapter 12 - Styling Lists
    • Chapter 13 - Styling for Print and Other Media
    • Chapter 14 - Everything Falls Apart
    • Appendix A – CSS Reference
    • Appendix B – CSS Specificity Chart
    • Appendix C – Browser Grading Chart

    All in all, I would highly recommend this book if you have a bit of experience with CSS and really want to solidify your understanding of the technology.  It also does well as a reference book, so once you’ve got the concepts down, I would still keep it close to your computer.

More Posts Next page »

















<October 2008>
SuMoTuWeThFrSa
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678
On the Trail of the Expanding Databases
 It is sometimes difficult for other IT people to understand the constraints that DBAs have to work... Read more...

SQL Server 2008: The New Data Types
 Brad continues his helicopter-level view of the most interesting new features of SQL Server 2008 with a... Read more...

Reporting on Mobile Device Activity Using Exchange 2007 ActiveSync Logs
 In this new column giving practical advice on all things Sys Admin related, Ben Lye takes on the often... Read more...

The Bejeweled Puzzle in SQL
 Alex Kozak provides another SQL puzzle to hone your SQL Skills with.  Read more...