Passing multiple, comma-delimited, parameters to a stored procedure is tricky. I found the need for this trick while trying to pass multiple parameters from a web form to my Microsoft SQL Reporting Services report.

Let’s say, for instance, you would like to filter employee data by a series of employee numbers:

DECLARE @EmployeeIDS nvarchar(50)
SET @EmployeeIDS = ‘15000, 34587, 20989, 987665’

SELECT *
FROM Employees
WHERE EmployeeID IN (@EmployeeIDS)

One would think this would work. However, it does not. The IN operator cannot accept a comma-delimited string. Too bad. It would make things a lot easier if it did.

4GuysFromRolla came up with this solution around this problem.

Essentially, they create a user-defined function that receives a comma-delimited string and converts it into a table. The IN operator loves it some tables.

Here’s the function:

CREATE FUNCTION dbo.Split
(
@List nvarchar(2000),
@SplitOn nvarchar(5)
)
RETURNS @RtnValue TABLE
(

Id int identity(1,1),
Value nvarchar(100)
)
AS
BEGINWhile (Charindex(@SplitOn,@List)>0)
Begin

INSERT INTO @RtnValue (value)
SELECT
Value = ltrim(rtrim(Substring(@List,1,Charindex(@SplitOn,@List)-1)))
SET @List = Substring(@List,Charindex(@SplitOn,@List)+len(@SplitOn),len(@List))
End
INSERT INTO @RtnValue (Value)
SELECT Value = ltrim(rtrim(@List))
RETURN
END

You can now use this function like so:

DECLARE @EmployeeIDS nvarchar(50)
SET @EmployeeIDS = ‘15000, 34587, 20989, 987665’

SELECT *
FROM Employees
WHERE EmployeeID IN (SELECT convert(int, Value) FROM dbo.Split(@EmployeeIDS,,))

The Split function returns a table to the IN operator:

Value
15000
34587
20989
20989
987665

 

Furthermore, because you are using a SELECT statement with the Split function, you could apply additional filters to remove specified parameters that you would like to ignore:

DECLARE @EmployeeIDS nvarchar(50)
SET @EmployeeIDS = ‘15000, 34587, 20989, 987665’

SELECT *
FROM Employees
WHERE EmployeeID IN (SELECT convert(int, Value) FROM dbo.Split(@EmployeeIDS,,) WHERE Value NOT IN (SELECT EmployeeID FROM [Table_Terminated_Employees])

October 9th, 2009Dealing with an IT Person

This is a departure from the typical things that I blog about.  However, given some recent dealings I’ve had supporting users, I think this could serve as being, somewhat, cathartic.

And yes, I realize this topic has probably been covered a million different ways on other blogs.

The audience for this post would be the average user that has to come to us for help.  I understand that, at times, this can be an arduous process.  I would like to clarify what, I think, are some misconceptions that users often have.

Just because you think it’s easy, it’s not

I know that, at times, the solution to the problem seems so easy.  I’ll hear users say things like:

“Well all I need is…” or

“It can’t be that hard to add…” or

“I don’t understand why it’s so hard to…”

Believe us when we say that it will be difficult to do what it is you’re asking us to do.  Keep in mind that most IT folks support a multitude of systems and keeping them all straight in our heads can be a challenge in of itself.

Your request, as small as it might seem, may just be the one thing that blows everything up or ruins the experience for everyone else.  We’d like to try and prevent that.

We love tackling challenges, just not everywhere we go

I’ve been asked to offer IT support in some of the oddest places.  A co-worker I used to work with was actually asked about a laptop issue while giving birth to her son!  We understand that your issues are troubling, but please respect our time.

I know that I would never ask a doctor to look at my bum shoulder at a Christmas party.  I wouldn’t ask a mechanic to look at my car while visiting my house for a BBQ.

I would hope that you would not do the same to an IT person.

It’s hard to say no and, more often than not, we won’t.  So don’t use this as an indication of our willingness to help.

We get frustrated when things don’t work too

We know that when things don’t work, it’s frustrating to a user.  But believe me when I say that it’s much more frustrating for us.

Here’s the big secret.

Often times, we don’t know what’s causing your issue.

Granted, we could do a better job of just admitting that, but we want you to have confidence that we’re going to fix your problem.

Moreover, a lot of times, you’re not the only one that’s experiencing the issue.  By the time you’ve called us, outraged by the impact of the problem, we’ve already heard ten other users irate about the same thing.

We don’t like when you’re unhappy

I sometimes get the impression that users think I live to make their lives difficult.  Nothing could be further from the truth.

Most of us got into this business because we have an insane desire to improve the way people do things.  Often times, we have a hard time standing idly by while someone struggles with an antiquated way of doing things.

If you’re still doing something on paper, chances are, one of us is going to show you a way to do it faster, better, cheaper, etc.  We just can’t help ourselves.

Nothing pleases us more than someone saying: “Wow, this makes my life so much easier” or “I can’t imagine how I lived without this!” Those statements are nerd nirvana!

Myself and a lot of people I work with obsess when something is going wrong for you.  We will talk about the problem at lunch, with our spouses, and even our dogs (if they’ll listen).

It’s all we can think about.

When we’re in the shower, at the movies, at church, we think about the problem you’re having and how to fix it.

And as developer, I have another piece to be unhappy about.  I built the thing!  Trust me, most developers are way more critical of themselves than you could ever be.  So please be gentle with your criticisms.

Sometimes, things are just plain out of our control

Most IT departments don’t have control over policy decisions.  We just offer tools to help support those decisions.

I can’t count the number of times I’ve been placed in a position to have to explain to a user why we were implementing a new piece of technology.  A nasty trick your boss will often use is blaming technology as the reason why you’re going to have to do things differently.  It’s okay.  We understand.  However, this often puts us in an awkward spot.

Keep in mind that, sometimes, we are doing things because we have been asked by the higher-ups.  Or that new piece of software you hate?  That might not have been our decision either.

Just please remember that, more often than not, we are just doing what we were asked to do.  Sure we offer advice and council but a lot times,in the end,it’s not our decision.

So when you tell us you HATE this @*^%$@! program.

What we won’t tell you is…We do too.

We would appreciate some perspective

Finally, I would suggest that you keep things in perspective.

Is the problem you’re having really THAT big of a deal?  Sometimes it REALLY is.  I’ve found that most times, it’s not.

A problem is a problem, so don’t think I’m saying this to be cavalier.  But try to keep your problem in perspective.  Imagine all the other issues an IT person might be dealing with that are of a higher priority.

And sometimes, your problem is not really a problem at all.

Sure, you don’t like the color of the window or having to click that extra button is annoying.  But try to focus on all the things that the technology IS doing for you.  Or focus on the things that are faster instead of the one piece that makes you wait.

I think, in general, having this perspective in dealing with technology will grant you less frustration and foster a more productive relationship with your IT people.

September 28th, 2009Sequencing Dates in SQL

“What were the dates of all the Mondays in the last 5 months?”
“How many Fridays were there between this date range?”
“What were the dates of the paydays in the last 10 months?”

Do any of these types of questions sound familiar? If you’ve ever worked with these types of date time questions in SQL, you would know that it can be VERY challenging.

At the hospital we use Cisco Unified Communications Manager (a.k.a CallManager).

The directors desired a report to break down various call statistics to one of their clinics. They were hoping this information would give them insight into how to handle call load.

One particular question was a tricky one: “What is the average call per days of the week, per hour, for the last three months?”.

For example: “What was the average call count, per hour, for all the Mondays in the last 3 months?”

Thanks to newly discovered dimensional tables, I had the breakdown per hour figured out, however, I was stumped on how to group it for average Mondays, Tuesdays, Wednesdays, etc.

That’s why I was thrilled to discover this solution by Peter Larsson, titled How Many More Mondays Until I Retire?

Essentially, Peter suggests creating a scalar SQL function that returns a table of dates between a specified date range. This opens a range of possibilities as far as dealing with tricky date queries.

Here is Peter’s function:

CREATE FUNCTION dbo.fnSeqDates
(
    @LowDate DATETIME,
    @HighDate DATETIME
)
RETURNS @Dates TABLE
        (
            SeqDate DATETIME
        )
AS

BEGIN
    DECLARE @Temp DATETIME

    IF @LowDate > @HighDate
        SELECT    @Temp = @LowDate,
                  @LowDate = DATEADD(day, DATEDIFF(day, 0, @HighDate), 0),
                  @HighDate = DATEADD(day, DATEDIFF(day, 0, @Temp), 0)
    ELSE
        SELECT    @LowDate = DATEADD(day, DATEDIFF(day, 0, @LowDate), 0),
                  @HighDate = DATEADD(day, DATEDIFF(day, 0, @HighDate), 0)

    INSERT    @Dates
              (
                  SeqDate
              )
    VALUES    (
                  @LowDate
              )

    WHILE @@ROWCOUNT > 0
        INSERT     @Dates
                   (
                       SeqDate
                   )
        SELECT     DATEADD(dd, n.Items, d.SeqDate)
        FROM       @Dates d
        CROSS JOIN (
                        SELECT COUNT(SeqDate) Items
                        FROM   @Dates
                   ) n
        WHERE      DATEADD(dd, n.Items, d.SeqDate) <= @HighDate

    RETURN
END

I used the function in my stored procedure like this:

	@StartDate datetime,
	@EndDate datetime,
	@Extensions varchar(50)
AS
BEGIN
	-- SET NOCOUNT ON added to prevent extra result sets from
	-- interfering with SELECT statements.
	SET NOCOUNT ON;

DECLARE @Time_Value TABLE (
RangeName varchar(18) UNIQUE,
Value int UNIQUE
)
-- This section is for setting up the dimensional table for the hours breakdown
INSERT INTO @Time_Value VALUES ('00',0)
INSERT INTO @Time_Value VALUES ('01',1)
INSERT INTO @Time_Value VALUES ('02',2)
INSERT INTO @Time_Value VALUES ('03',3)
INSERT INTO @Time_Value VALUES ('04',4)
INSERT INTO @Time_Value VALUES ('05',5)
INSERT INTO @Time_Value VALUES ('06',6)
INSERT INTO @Time_Value VALUES ('07',7)
INSERT INTO @Time_Value VALUES ('08',8)
INSERT INTO @Time_Value VALUES ('09',9)
INSERT INTO @Time_Value VALUES ('10',10)
INSERT INTO @Time_Value VALUES ('11',11)
INSERT INTO @Time_Value VALUES ('12',12)
INSERT INTO @Time_Value VALUES ('13',13)
INSERT INTO @Time_Value VALUES ('14',14)
INSERT INTO @Time_Value VALUES ('15',15)
INSERT INTO @Time_Value VALUES ('16',16)
INSERT INTO @Time_Value VALUES ('17',17)
INSERT INTO @Time_Value VALUES ('18',18)
INSERT INTO @Time_Value VALUES ('19',19)
INSERT INTO @Time_Value VALUES ('20',20)
INSERT INTO @Time_Value VALUES ('21',21)
INSERT INTO @Time_Value VALUES ('22',22)
INSERT INTO @Time_Value VALUES ('23',23)

SELECT
CASE
	WHEN DATEPART(weekday,ReportShortDateTime) = 1 THEN 'Sunday'
	WHEN DATEPART(weekday,ReportShortDateTime) = 2 THEN 'Monday'
	WHEN DATEPART(weekday,ReportShortDateTime) = 3 THEN 'Tuesday'
	WHEN DATEPART(weekday,ReportShortDateTime) = 4 THEN 'Wednesday'
	WHEN DATEPART(weekday,ReportShortDateTime) = 5 THEN 'Thursday'
	WHEN DATEPART(weekday,ReportShortDateTime) = 6 THEN 'Friday'
	WHEN DATEPART(weekday,ReportShortDateTime) = 7 THEN 'Saturday'
END AS Day_Name, RangeName AS Hour_Range,
COUNT(ReportShortDateTime) AS Calls,
(ROUND((CAST(COUNT(ReportShortDateTime) AS Float) / CAST((SELECT COUNT(SeqDate) FROM dbo.fnSeqDates(@StartDate, @EndDate) WHERE DATEPART(dw, SeqDate) = DATEPART(weekday,ReportShortDateTime)) AS Float)), 1)) AS Average_Calls
FROM [CALLMANAGER] INNER JOIN
@Time_Value ON DATEPART(hour, ReportDateTimeOrigination) = Value
WHERE ReportShortDateTime BETWEEN @StartDate AND @EndDate AND
originalCalledPartyNumber IN (Select Convert(varchar(50),Value) FROM dbo.fnSplit(@Extensions, ','))
GROUP BY DATEPART(weekday,ReportShortDateTime), RangeName, Value
ORDER BY DATEPART(weekday,ReportShortDateTime), Value
END

As you can see in the query, I use the function like this:

SELECT COUNT(SeqDate) FROM dbo.fnSeqDates(@StartDate, @EndDate) WHERE DATEPART(dw, SeqDate) = DATEPART(weekday,ReportShortDateTime)

In other words, I’m saying: “Give me the count of days, between this specified date range, that are Mondays, Tuesdays, Wednesdays, etc.”

September 21st, 2009Grouping Data Into Time Ranges

Today, on the Interwebs, I found a great way to group data into ranges. I modified it quite a bit to fit my needs, however, I imagine you could apply this to any type of ranges you might want to use.

In this case, I wanted to group a dataset of information into time ranges (4 hour groups).

Essentially, I create a temporary dimensional table and join to it. Simply brilliant! I’m ashamed to admit I’ve never used these types of dimensional temp tables in my queries. However, I’m learning it won’t be my last.

DECLARE @Time_Ranges TABLE (
RangeName varchar(18) UNIQUE,
Minimum int UNIQUE,
Maximum int UNIQUE
)

INSERT INTO @Time_Ranges VALUES ('12:00 AM - 3:00 AM',0,3)
INSERT INTO @Time_Ranges VALUES ('4:00 AM - 7:59 AM',4,7)
INSERT INTO @Time_Ranges VALUES ('8:00 AM - 11:59 AM',8,11)
INSERT INTO @Time_Ranges VALUES ('12:00 PM - 3:59 PM',12,15)
INSERT INTO @Time_Ranges VALUES ('4:00 PM - 7:59 PM',16,19)
INSERT INTO @Time_Ranges VALUES ('8:00 PM - 11:59 PM',20,23)

SELECT RangeName, COUNT(*) AS Total
FROM [YOURSQLTABLE] INNER JOIN
@Time_Ranges ON DATEPART(hour, DateTimeField) >= Minimum AND
DATEPART(hour, DateTimeField) <= Maximum
GROUP BY RangeName, Minimum
ORDER BY Minimum

I wish I could say that my code is always bug free.

Developing applications within the health care environment doesn’t afford myself or my users this luxury.
Often times, I test as much as I can and release my little baby into the wild.

Sure, I would love to implement a rigorous SDLC, but if I did that, my projects would take well over a year each.  I’m required to be far more agile than that.

So I would do what any other good programmer would do.  I wrote the best code I could and when users complained I’d pull out my box of Band-Aids (they have “try/catch” written on them).

Unfortunately, users aren’t always the best people to troubleshoot with.  It would go something like this:

User: “The program isn’t working.”
Me: “Okay, I’m sorry about that.  Can you describe the problem you’re having?”
User: “It won’t work when I click the button.”
Me: “Okay.  What page are you on?”
User: “It’s the blue one.  You know the button on the bottom of the blue page?”
Me: ” Well….They’re…kinda…all blue.”
User: “This program is slow…”

During my studies for the 70-562 exam I discovered the proper use of the Global.asax file and how to deal with unhandled exceptions globally within my applications.

I’m not above admitting that any ASP.NET developer, worth his/her salt, should know all about this.  But, then again, I’m not a developer worth my salt.

<shame>I was concerned about logging errors, but most often I would be doing it from code-behind files; storing errors in a database or the Event Viewer</shame>.

So I have now crafted some code to be used in the Application_Error event of the global.asax file.

This will generate a nice HTML and plain-text e-mail detailing all the information about the unhandled error.  In this case, I have these e-mails sent directly to me.

Finally, the code clears the server error so that it can redirect the user to a nice error page, telling them I screwed up.

Most often, the errors that I receive are minor.  It’s been fun to call users and let them know I fixed an error they had only moments ago.  Their reaction is, needless to say, positive.

Here is a sample of what the HTML version of the e-mail looks like:

Screenshot of the HTML e-mail

Screenshot of the HTML e-mail

And here is the code to be placed in the code-behind of your Global.asax file (I’ve omitted the other events for brevity):

using System;
using System.Net.Mail;
using System.Net.Mime;
using System.Web;

/// <summary>
/// Summary description for Global
/// </summary>
namespace YourProjectName
{
        protected void Application_Error(object sender, EventArgs e)
        {
            MailMessage iMsg = new MailMessage();
            SmtpClient SMTP = new System.Net.Mail.SmtpClient("<your Email Server>");
            iMsg.From = new MailAddress("<yourApplicationName>_500_Error@yourdomain.com");
            iMsg.Priority = MailPriority.High;
            iMsg.Subject = "There was a Error Message with <yourApplicationName>";
            //Create the plain text version of the e-mail
            iMsg.Body = "An Unhandled Exception Error occurred with <yourApplicationName> on: " + DateTime.Now.ToString() + "\n\n" +
                "====================\n" +
                "Inner Exception \n" +
                "====================\n\n" +
                 Server.GetLastError().InnerException + "\n\n" +
                "====================\n" +
                "Error Message \n" +
                "====================\n\n" +
                 Server.GetLastError().Message + "\n\n" +
                "====================\n" +
                "Source \n" +
                "====================\n\n" +
                 Server.GetLastError().Source + "\n\n" +
                "====================\n" +
                "Stack Trace \n" +
                "====================\n\n" +
                 Server.GetLastError().StackTrace + "\n\n" +
                "====================\n" +
                "Target Site \n" +
                "====================\n\n" +
                 Server.GetLastError().TargetSite;

            //Create the alternate HTML view
            String HTMLBody = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">" +
                "<html><head><meta http-equiv=Content-Type content=\"text/html; charset=iso-8859-1\">" +
                "</head><body style='font-family:Arial; background-color:#FFFEF5;'>" +
                "<table style='font-family:Arial'><tr><td style='color:Red; font-size:16pt;'>There was a 500 error with *YourApplicationName* on: <td>" + "<td><b>" + HttpUtility.HtmlEncode(DateTime.Now.ToString()) + "</b></td></tr></table><br />" +
                "<div style='font-weight:bold; font-size:14pt; font-family:Tahoma; width:100%; background-color:#077BAD; border-bottom:solid 2px #044062; border-top:solid 2px #044062; color:White'>Inner Exception</div><br />" +
                "<div style='font-family:Courier; font-size:11pt;'>" + HttpUtility.HtmlEncode(Server.GetLastError().InnerException.ToString()) + "</div><br />" +
                "<div style='font-weight:bold; font-size:14pt; font-family:Tahoma; width:100%; background-color:#077BAD; border-bottom:solid 2px #044062; border-top:solid 2px #044062; color:White'>Error Message</div><br />" +
                "<div style='font-family:Courier; font-size:11pt;'>" + HttpUtility.HtmlEncode(Server.GetLastError().Message.ToString()) + "</div><br />" +
                "<div style='font-weight:bold; font-size:14pt; font-family:Tahoma; width:100%; background-color:#077BAD; border-bottom:solid 2px #044062; border-top:solid 2px #044062; color:White'>Source</div><br />" +
                "<div style='font-family:Courier; font-size:11pt;'>" + HttpUtility.HtmlEncode(Server.GetLastError().Source.ToString()) + "</div><br />" +
                "<div style='font-weight:bold; font-size:14pt; font-family:Tahoma; width:100%; background-color:#077BAD; border-bottom:solid 2px #044062; border-top:solid 2px #044062; color:White'>Stack Trace</div><br />" +
                "<div style='font-family:Courier; font-size:11pt;'>" + HttpUtility.HtmlEncode(Server.GetLastError().StackTrace.ToString()) + "</div><br />" +
                "<div style='font-weight:bold; font-size:14pt; font-family:Tahoma; width:100%; background-color:#077BAD; border-bottom:solid 2px #044062; border-top:solid 2px #044062; color:White'>Target Site</div><br />" +
                "<div style='font-family:Courier; font-size:11pt;'>" + HttpUtility.HtmlEncode(Server.GetLastError().TargetSite.ToString()) + "</div><br />" +
                "</body></html>";

            //Add the alternate view
            iMsg.AlternateViews.Add(AlternateView.CreateAlternateViewFromString(HTMLBody, null, MediaTypeNames.Text.Html));

            //Finish sending the message
            iMsg.To.Add(new MailAddress("<your_email@your_domain.com>"));
            SMTP.Send(iMsg);
            //Clear the error so that you can redirect to your error page
            Server.ClearError();
            Response.Redirect("~/Error.aspx");
        }
    }
}

This is one of those ideas I wish I was smart enough to come up with myself.  But my hat is off to Alex Arkhipov who came up with a nifty solution to prevent the dreaded double-postback.

You might remember that this issue has been a common theme for me.

It seems that no matter how quick and well designed my UI is, users still find ways to insert multiple records into my database by double-clicking.  It never used to be an issue when the entire page would post back, however, with the latest AJAX tools and functionality, it happens more and more.

In any event, Alex came up with this simple solution that appears to be working great!

Essentially, he adds client-side page event handlers that disable the calling button during an initializeRequest event and re-enables it during an endRequest event.  Simply brilliant!

I modified the code a bit to remove the styling from CSS. I just needed the button to be disabled.
Then, I placed this code inside of my master page so all of my child pages could take advantage of this functionality.


<script type="text/javascript">
function pageLoad(sender, args) {
     var rm = Sys.WebForms.PageRequestManager.getInstance();
     rm.add_initializeRequest(initializeRequest);
     rm.add_endRequest(endRequest);
}
function initializeRequest(sender, args) {
     //Disable button to prevent double submit
     var btn = $get(args._postBackElement.id);
     if (btn) {
          btn.disabled = true;
     }
}
function endRequest(sender, args) {
     //Re-enable button
     var btn = $get(sender._postBackSettings.sourceElement.id);
     if (btn) {
          btn.disabled = false;
     }
}
</script>

I don’t know if you are like me, but I hate dealing with session variables. They look terrible in code, they are difficult to manage and organize, and overhauling them can be a nightmare.

I’m working on a nursing productivity tool here at work. It essentially assists nurse managers in scheduling resources to maintain ratios, provide superior care, and protect our financial bottom-line.

The challenge with this tool is that I’ve well over 20 session variables that I write to.  Additionally, I read from these variables in over seventy locations.

So, I did some Googling and discovered a blog post, written by raj, that details creating a SessionHandler class.

It’s a great way to manage and organize all of your session variables outside of your working code.  Plus, the class protects from NullReferenceException and type cast errors.

Here’s and example:

One of the session variables I’m using stores the user’s full name.  In this case, I create a SessionHandler class file (SessionHandler.cs) and add a public static property called “User Full Name”.

public static class SessionHandler

    {
        private static string _userfullnamekey = "UserFullName";

        // Gets or Sets the user's full name.
        public static string UserFullName
        {
            get
            {
                //Check for null first
                if (HttpContext.Current.Session[SessionHandler._userfullnamekey] == null)
                {
                    //Return an empty string if session variable is null
                    return string.Empty;
                }
                else
                {
                    return HttpContext.Current.Session[SessionHandler._userfullnamekey].ToString();
                }
            }
            set
            {
                HttpContext.Current.Session[SessionHandler._userfullnamekey] = value;
            }
        }

So now, if I want to access the value of this variable from my working code, I do this:


SessionHandler.UserFullName = "Travis Lowdermilk";

Label1.Text = SessionHandler.UserFullName;

You’ll notice that the SessionHandler class ensures that I do not get NullReferenceException or invalid cast errors.

No more repeatedly checking for null values or casting my session variables in working code.

Updated (9/2/2009): It appears that this is no longer an issue in the RadControls for ASP.NET AJAX Q2 2009 release. Read this post for a better solution to this problem.

An all too common situation us web developers face is the dreaded “double submit” issue.

Users, for some reason, love to double-click on every submit button you might create in your web application.  It’s like an impulse.  They MUST submit the record into the database twice.

Poor souls.  They just can’t help themselves.

There are a handful of ways to address this issue using javascript, SQL, and server-side code.

I wanted to use javascript because I felt it was the most “lightweight” way to handle this issue.

Plus, my submit button was nested inside of a FormView, that was nested inside of a ContentPanel, that was nested inside of a MasterPage.

Fun.

In any event, I created a javascript function to fire on the OnClientClick property of the submit button:

<asp:Button ID=”btnSubmit” runat=”server” Text=”Submit Assessment Form” ToolTip=”Submit the Assessment Form”

onclick=”btnSubmit_Click” OnClientClick=”DisableSubmit(this);” />

I then created a javascript function on the Masterpage:

<script type=”text/javascript”>

function DisableSubmit(buttonElement) {

document.getElementById(buttonElement.id).disabled = true;

}

</script>

Hmmmm, when I would click the button, nothing would happen.

So I put in an alert and discovered that the javascript would fire, but the button would not enter the “disabled” state.

Frustrating!

Long story longer, I discovered that this would not work if I was using the Telerik RadFormDecorator.

That’s because the RadFormDecorator renames the client id of each control that it skins.

Therefore, the javascript function DisableSubmit was receiving “ctl00_ContentPlaceHolder1_frmAssessment_btnSubmit”, but the actual id was “Skinnedctl00_ContentPlaceHolder1_frmAssessment_btnSubmit”.

The RadFormDecorator was placing “Skinned” at the front of the id.

So I amended the javascript function:

<script type=”text/javascript”>

function DisableSubmit(buttonElement) {

document.getElementById(‘Skinned’ + buttonElement.id).disabled = true;

}

</script>

Now, the button disables appropriately.  Yay me!

First, lets be clear.

I am, by far, the WORST test taker.  I have paralyzing anxiety when it comes to taking tests.  I have found, in my years in college, that the way I pass exams is by being prepared.  There really is no “short-cut” for me.

I am a bit embarrassed that after 3 months of intensive study for this exam, I received a passing score of 700.

That’s right.  I got just enough questions right to pass.

To be honest, I was a little dissapointed.  I believed that with the effort I put into my studies, I should’ve performed better.  But I digress.

Here are some things that DID help me prepare.

1.  Its a good idea to fully understand what you are studying for.  This page gives a pretty extensive overview of what to expect on the exam.

2. I purchased the MCTS Self-Paced Training Kit book by Tony Northrup, Shawn Wildermuth, and Bill Ryan.  This book has its flaws (which I’m sure you’ve seen in other forums).  Overall, its a comprehensive collection of what’s needed to pass.  I found the MeasureUp test, included with the book, to be absolutely useless.  The questions are poorly written and really just tell you what you didn’t memorize from the book.  It doesn’t focus on CONCEPTS.  Furthermore, if you select the wrong answer, MesureUp does not thoroughly explain why that is not the right answer.

3.  I purchased a Transcender practice exam.  There are different schools of thought on using “practice” exams.  I was a bit leary in using it, but Transcender did an AMAZING job of putting together practice questions.

I would recommend that you start with the book.  Read through each chapter and take notes.  I used Microsoft One Note to collect all of my notes.  This made it handy when I was finished.  I could print my entire notebook for review.  Plus, I could copy and paste elements from the ebook (diagrams, tables, etc.) that I wanted to remember.  The only problem I had was copying and pasting code from VS to One Note.  I had to paste it into Word first to keep the coloring and formatting.  C’mon Microsoft, can we fix this please!?

After completing the book, I went through and completed each lab in the book.  I think it was a better strategy to do it this way because, in a sense, it allows you to go through the book twice.

When I was finished with the book and the labs, I purchased the Transcender.  Each question that I got wrong, I printed out.  Then I would highlight the “keywords” or concepts that I was missing from that question.  Again, Transcender does such a GREAT job of really honing in on why the wrong answer is wrong.  I feel like I learned more about what I didn’t know from taking the Transcender and, upon taking the test, I found very few questions that were similar.  So, it didn’t feel like “cheating” to me.

Rinse and Repeat.

You can complete the exam in C#, Visual Basic, and (I think) C++?

I can write in both VB and C#, but I chose VB for readability.

I would say that if you are new to programming in general or haven’t developed a competincy for reading/writing code, this would be an ambitious starting point.

If you have any other questions, feel free to leave me a comment and I will do the best I can to answer.

My wife and I sporting SWEET T-Shirts from Telerik (Thanks Emily!)
My wife and I sporting SWEET T-Shirts from Telerik (Thanks Emily!)

My wife and family are happy.  That’s because I’m home in time for dinner.

Thanks to the hard working folks at Telerik, I can get things DONE.

Most importantly, I don’t have to sacrifice my design or UI strategies!

Let’s face it.  A lot of us developers (especially in health care) work on projects by ourselves.

We may be on a team of developers, but with so many projects coming in, we take one under our wing and run with it.

It becomes our baby.  We feed it, love it, and one day we let it leave our protection; hoping it does what we told it to do.

Having to work under these conditions means that we have to be the developer, tester, AND designer.

And it seems to me, the cool design elements always come last.  After we’ve ran out of time.

“Gee, I would really like to have this form AJAX-ifed, but I just don’t have the time.”

“It would be really cool to bring some color and design elements to this GridView, but alas, it’s due next week.”

“I spent all my time getting this INFERNAL thing to work; I don’t have time to make it pretty!”

Well, I am more than happy to suggest a FANTASTIC set of ASP.NET tools from Telerik.  I can’t tell you how much I love these tools.

Having these controls is like having a team of developers enhancing every aspect of your development.

For instance, the RadFormDecorator will allow you (with one line of code) to format all of your buttons, radio buttons, text boxes, list boxes, drop down lists, etc. to a skin of your choice.  Allowing you to instantly change the look and feel of your pages.

The RadEditor, with spell check, gives you a robust text editor that will make your boss think you SLAVED over the design.

The RadMenu, RadTabStrip, and RadPanel give you intuitive navigation with very little coding.

And the RadGrid?  RadGrid, alone, is worth the money spent.

I could go on and on.

Do not hesitate.  Download the trial today!


© 2007 travislowdermilk.com | iKon Wordpress Theme by TextNData | Powered by Wordpress | rakCha web directory