SQL Server Multi-Statement Table-valued UDFs (User Defined Functions)

There’s a special variation of the Table-valued UDF called the multi-statement table-value. As with the regular Table-Value, it can only return a single table. However, you can create a table on the fly, populate it, then return the results. Lets take a look at an example, based on our sample from yesterday.

create function dbo.f_LotsOfPeople(@lastNameA as nvarchar(50), @lastNameB as nvarchar(50))

        returns @ManyPeople table

          (PersonID int, FullName nvarchar(101), PhoneNumber nvarchar(25))

as

begin

 

  insert @ManyPeople (PersonID, FullName, PhoneNumber)

    select ContactID

        , FirstName + ‘ ‘ + LastName

        , Phone

      from Person.Contact

    where LastName like (@lastNameA + ‘%’);

 

  insert @ManyPeople (PersonID, FullName, PhoneNumber)

    select ContactID

        , FirstName + ‘ ‘ + LastName

        , Phone

      from Person.Contact

    where LastName like (@lastNameB + ‘%’);

 

  return

end

The first line declares the function and passes in two parameters. The next line declares the return type will be a table, named @ManyPeople. We need to give our table a name, as we’ll be using it inside the function.

The third line defines the layout of our in memory table, @ManyPeople. I’ve declared three columns, and given their names and data type. Next comes the as, followed by a begin/end construct to house our code.

Inside the code I add data to the @ManyPeople table by using traditional Insert syntax. Please note this isn’t quite like a scalar function, I’m restricted to statements that insert or update records in the @ManyPeople table. Complex calculations, etc are restricted unless they take the form of inserting / updating into our table.

To end the function I have to have a Return statement that will end the function and return the @ManyPeople table to the calling routine. And how do we call it? Pretty simple, just like we did yesterday.

select * from dbo.f_LotsOfPeople(‘Abe’, ‘Zie’)

PersonID FullName PhoneNumber
———– —————- ————-
2 Catherine Abel 747-555-0171
3 Kim Abercrombie 334-555-0137
1212 Kim Abercrombie 208-555-0114
1370 Kim Abercrombie 919-555-0100
988 Arvid Ziegler 398-555-0100

(5 row(s) affected)

Multi-Statement Table-valued UDFs can be handy when you need to assemble data from multiple places and return a single table.

SQL Server Table-valued UDFs (User Defined Functions)

Yesterday we talked about one type of UDF, the scalar. There is also a great comment that emphasizes a point I made, namely you need to be careful to test your UDFs for performance (take time to read it, it’s worth your time). Often a UDF will give you can get a nice performance boost, but sometimes they can negatively affect your queries. TEST!

Today we’ll cover the Table value type of UDF. Unlike the scalar type, which returns only one value, the table type can return multiple rows. They are similar to views, only they perform slightly better. Let’s look at an example.

create function dbo.f_People(@lastName as nvarchar(50))

        returns table

as

return

(

  select ContactID

      , FirstName + ‘ ‘ + LastName as FullName

      , Phone

    from Person.Contact

  where LastName like (@lastName + ‘%’)

)

As you can see, you are only allowed one statement inside the function, and it must be a select statement. It can be a complex one, or a simple one as I’ve done above. The return type of the function is declared as table, which flags this as a table valued UDF.

To use it, treat the UDF as if it were a table and place it in the from clause. You’ll notice that unlike a table though, you can pass in a parameter. Here I pass in a varchar string, and use it as part of the where clause inside the UDF. Here’s an example of using our UDF as part of a SQL statement.

select * from dbo.f_People(‘Ab’)

ContactID FullName Phone
———– —————- ————-
2 Catherine Abel 747-555-0171
3 Kim Abercrombie 334-555-0137
1012 Syed Abbas 926-555-0182
1212 Kim Abercrombie 208-555-0114
1268 Hazem Abolrous 869-555-0125
1370 Kim Abercrombie 919-555-0100
1557 Sam Abolrous 567-555-0100

 

So why would you want to use this instead of a view? Well as you can see from my example, you have the ability to pass a parameter to the function. With a view, SQL Server precompiles the SQL, then you have to limit the results with a where clause. With the function, SQL also precompiles the statement but this time we can use a parameter, which is precompiled into the select statement, to limit the results. That means you’ll get slightly better performance out of the UDF versus the view.

SQL Server Scalar UDFs (User Defined Functions)

OK, I admit it sometimes I’m a little late to the party. Even though they’ve been around since SQL Server 2000, I haven’t had the pleasure of getting acquainted with User Defined Functions (UDFs) inside SQL Server. But now that I know about them, I can see how useful they can be. There are several kinds of UDFs, today let’s chat about the scalar types.

A scalar UDF returns one, and only one value from the function. You can pass in parameters, have it do everything from simple to complex calculations, then return a result. Let’s take a look at how one is coded, using the AdventureWorks database.

create function dbo.f_ContactFullName(@id as int)

        returns varchar(101)

as

begin

  declare @FullName varchar(101);

 

  select @FullName = FirstName + ‘ ‘ + LastName

    from Person.Contact

  where ContactID = @id;

 

  return @FullName;

 

end;

Running the above code will create your UDF. To confirm it worked, in SQL Server Management Studios Object Explorer open the tree under AdentureWorks. Go down to Progammability, Functions, Scalar-valued Functions and you should see the new function.

The purpose of this function is to take the contact ID (the Primary Key) and look up the name in the Person.Contact table. It then concatenates the first and last names for us and returns the full name.

We begin with a create function command, followed by the name of the function. I would suggest you adopt some naming scheme to identify your functions, to separate them from stored procedures or other built in functions. Some folks use ufn, some fn, I chose f_ for my prefix. At the end of the function is the parameter list, enclosed in parenthesis. If there had been more than one parameter, I would have used commas to separate them.

Next comes the return type, in this case I’m returning a varchar. Your function will then be enclosed in a begin…end construct. I’ve declared a variable to hold the return value (@FullName), and then run a simple select statement to get the name, concatenate it, and store it in our variable. Finally I use the return command to return the value back to the caller. And how do we call it?

Well, one simple way is to simply say “select functionname()” as in

select dbo.f_ContactFullName(1);

————————-
Gustavo Achong
(1 row(s) affected)

I passed in a contact id from the table (1) and it returned the full name of that person. What might be more useful though is to include the UDF as part of a select statement.

select Person.Contact.ContactID

    , dbo.f_ContactFullName(Person.Contact.ContactID)

    , EmailAddress

  from Person.Contact

order by Person.Contact.ContactID

ContactID EmailAddress
——— —————– ——————————-
1 Gustavo Achong gustavo0@adventure-works.com
2 Catherine Abel catherine0@adventure-works.com
3 Kim Abercrombie kim2@adventure-works.com
4 Humberto Acevedo humberto0@adventure-works.com
5 Pilar Ackerman pilar1@adventure-works.com
6 Frances Adams frances0@adventure-works.com
7 Margaret Smith margaret0@adventure-works.com
8 Carla Adams carla0@adventure-works.com
9 Jay Adams jay1@adventure-works.com

On the second line of the select statement I call my small function, to comprise the full name. And under it you can see the results.

I should mention there are a few tradeoffs to using user defined functions. First, they are not portable to other databases. That means if you ever moved your data to another database type they would not transfer, and would have to be written as something else. But I mean, really now. How many times have you ever moved your data to another database. It happens so seldom that I would not worry about it.

What you should be concerned over though is the performance trade offs. For example, looking at my example above each row winds up making two calls to the same table, one to fetch the record and a second to fetch it again inside the function to get the name. Not very efficient. On the other hand, if I was reading another table, this might be just as efficient as joining the tables, and easier to use.

select SalesOrderID

    , dbo.f_ContactFullName(ContactID) as OurCustomer

    , OrderDate

    , PurchaseOrderNumber

  from Sales.SalesOrderHeader

order by SalesOrderID

SalesOrderID OurCustomer OrderDate PurchaseOrderNumber
———— ——————– ———– ——————–
43659 James Hendergart 2001-07-01 PO522145787
43660 Takiko Collins 2001-07-01 PO18850127500
43661 Jauna Elson 2001-07-01 PO18473189620
43662 Robin McGuigan 2001-07-01 PO18444174044
43663 Jimmy Bischoff 2001-07-01 PO18009186470
43664 Sandeep Katyal 2001-07-01 PO16617121983
43665   Richard Bready 2001-07-01 PO16588191572
43666 Abraham Swearengin  2001-07-01 PO16008173883


In this example I’ve created a very simple User Defined Function. However, you can get as complex as you need and create long, intricate functions. Given the right circumstances UDFs can add a new dimension to your code reuse libraries.

Brilliant!

Lately I’ve been delving more and more in to the SQL Server world, as it pertains to Business Intelligence. Coincidentally, our DBA complains of not sleeping at night, and seems to have developed a nervous twitch. I keep telling him to lay off the caffeine, but oh well I digress.

One of the things that I’ve found irritating is the inability for long running T-SQL scripts to be able to keep the user (in other words, ME) updated as it progress through. Instead it seems to save up any print or select messages until the entire job is over then prints them out in a big explosion, not unlike an episode of Mythbusters.

Mladen Prajdic on his “I want some Moore” blog came up with a brilliant solution. Use RAISERROR, with the NOWAIT option and a low severity, to flush the message buffers immediately. To quote those two guys from the Guinness ads, Brilliant! But hey, I don’t want to steal Mladen’s thunder, go read it for yourself:

http://weblogs.sqlteam.com/mladenp/archive/2007/10/01/SQL-Server-Notify-client-of-progress-in-a-long-running.aspx

True, I doubt I’d suggest this for production code that would run unattended, but for those long scripts we all wind up writing to do some tests or fix some bad records well… you can bet this is a handy tip I’ll be using over and over.

Installing Kubuntu 7.10 In Virtual PC 2007

After last weeks post on Ubuntu 7.10 (http://arcanecode.wordpress.com/2007/10/18/installing-ubuntu-710-under-virtual-pc-2007/), I had several requests for Kubuntu. Since I’m happy to please, here are the step by step instructions for Kubnutu. By the way, I’ve reduced the screen sizes a little to make them fit the flow of the blog, but you can click any of them to see them in full size should you need to make out any of the details.

If you haven’t already done so, you’ll need to download the latest image of Kubuntu, you can get it from http://www.kubuntu.com/download.php .

Kubuntu has some of the same issues as Ubuntu under Virtual PC when it comes to graphics and the mouse. When you fire up the VPC with the Kubuntu disk in the drive (or you’ve captured it’s ISO image), you’ll want to move the highlight down to “Start Kubuntu in safe graphics mode”. To fix the mouse, at least for this session, press F6 for boot options, and type in “i8042.noloop” after the –. Once your screen looks like the one below, press Enter to continue.

k710_001

Once Kubuntu boots, click the install icon on the desktop to begin the installation process.

k710_002

On the Welcome screen, just confirm your language, then press Next.

k710_003

In this step you get to play Carmen whats-her-name and do the “Where in the world are you” bit. Select a city in the time zone in which you live, then click Next.

k710_004

Now pick your keyboard, and click Next.

k710_005

On the disk space screen, just take the defaults and click next.

k710_006

OK, now we actually have to do some work, and give Kubuntu some info. Make sure to remember your password, not only will you need it to login but you’ll also need it for any commands that need super user privlidges.

k710_007

OK, Kubuntu finally knows everything it needs in order to install so just hit next.

k710_008

And wait. And wait. And wait. If you thought Ubuntu took a while to install, just wait for Kubuntu. My experience was a couple of hours, but to be fair I was also playing a couple of Quicktime videos (some of the cool shows from http://www.revision3.com) and testing an openSuse install in another VPC. And I have sloooooooooooow hard disks, so your milage (or kilometers) may vary. But it’ll still take a while.

About 84% or so into it I got this error. This is similar to the error I got with Ubuntu, just click OK to let it keep going. Oh, and wait some more.

k710_009

Yea! It finally finished the install.

k710_010

When you get to this screen, just press OK, then reboot Kubuntu. Don’t forget to eject the CD (or release the ISO) during the reboot. But before you press OK, make sure to read the next step!

OK, time for a tricky part. Pay close attention during the reboot. When you get to the screen that talks about the GRUB menu, press the ESCape key. You should see a screen like this:

k710_011

With the top line highlighted, press the e key to edit the command line.

On the next screen, press the down arrow once to highlight the line that begins in “kernel”, then press e again to edit that line. When the edit screen appears, we need to add the – i8042.noloop to the end of the line. It should look something like:

k710_012

Press Enter, then when you return to the screen with “kernel” on it, press ‘b’ (just the letter b) to boot Kubuntu. What this will do is enable the mouse for this session only! Once we get booted, we’ll fix the mouse permanently, so hang on.

When the login screen appears, enter your user name and password (the ones you entered on the “Who Are You” screen during the installation) and press enter. Now give it a minute while Kubuntu finishes loading.

OK, now it’s time to fix that pesky mouse issue once and for all. Click on the big K in the lower left (it’s like the Start button in Windows), and go to System, then bring up Konsole.

k710_013

Now in the Konsole window, type in

sudo kate /boot/grub/menu.lst

and press enter.

k710_014

Enter your password when prompted, and you should be in the kate editor. Note you may see a few errors on the terminal window. These can be ignored.

Once kate is up, scroll to the very bottom of the editor where you’ll find three sections of “title… kernel… “ etc. In the first section, which is the default, we need to edit the kernel line to add:

– i8042.noloop

to the end of the line, as you see here. Once done, save it by using File, Save on the menu or clicking the floppy disk. Then exit kate (File, Quit) and exit the terminal window (type exit and press enter, or close by just clicking the x button as you would in Windows).

k710_015

And that’s it, you should be good to go and enjoy Kubuntu 7.10 virtually.

PS If you found this useful, please give it a digg so others can find it too.

Arcane Links

I recently joined LinkedIn (http://www.linkedin.com/). Seems like a good way to get hooked up and stay connected. See my public profile at:

http://www.linkedin.com/in/arcanecode

Join yourself, you might be surprised how many people you know that are on Linked In.

Installing Ubuntu 7.10 Under Virtual PC 2007

Update April 24, 2008 – The newest version of Ubuntu, 8.04 is out. Look for complete install instructions here.

Update April 7 2008 – If you are interested in also playing with the 8.04 BETA, you can read my post here.

Ubuntu version 7.10 was just released. In keeping up with tradition I’d like to describe step by step instructions on how to install and get it running under Virtual PC 2007.

Before I begin though, I’d like to give a word of thanks to all the folks who have commented on my previous postings. It was their findings and efforts that helped to create this work, I owe them a big thanks.

OK, first thing you need is to download the Desktop install ISO from the Ubuntu site (http://www.ubuntu.com). You can skip right to the download mirrors page at http://www.ubuntu.com/getubuntu/downloadmirrors if you want to save a few mouse clicks.

Once you get it downloaded fire up Virtual PC, and create a new machine. If you are not familiar with VPC, see my step by step instructions for creating a machine at http://arcanecode.wordpress.com/2006/09/20/virtual-pc-step-by-step/ Make sure to pick “other” as the OS type. I used 512 meg of ram because my system has 2 gig, but if you have less you can get away with 256 meg of ram for the Ubuntu Virtual machine.

Fire up your new virtual machine, and use the option in the CD menu to “Capture ISO image”. Point the image at the desktop iso you just downloaded.When it starts, immediately press the down arrow, so that “Start Ubuntu in Safe Graphics Mode” is highlighted.

When 7.04 was released, the new kernel had issues with the mouse emulated by Virtual PC. To be blunt, the mouse just didn’t work. However, several work arounds were found. The easiest was brought to my attention via comments on the blog, the i8042.noloop option. That’s what we’ll implement, so we can use the mouse during the “live mode”.

Hit the F6 key, for Options. When the line appears, at the very end type in a space (if there’s not one after the two dashes) then i8042.noloop . Your screen should look something like this:

u710_001

Press Enter to start the launch process. Be patient, it takes quite a while. Once it’s finally up though, you’ll see this screen:

u710_002

Double click on the Install icon to begin the install.

On the first screen, below, you are welcomed and asked about a language. Pick your language and hit Forward.

u710_003

Now pick your time zone, since I’m in the Central zone I picked Chicago as a city in my time zone and clicked Forward.

u710_004

No it asks about keyboard layout, pick your keyboard if yours isn’t US English, then press Forward.

u710_005

Ubuntu will crank and grind for a minute, then you’ll see this dialog asking about your disks. Just take the defaults and click Forward.

u710_006

Time for a little personal info, give your name, a login id, enter the password you want to use, and what you want to name the “computer”. When done click Forward.

u710_007

OK, you’re almost ready to start the install process. Look this over, if everything looks good just press the Install button and we’re off to the races.

u710_008

Did I say races? Well, turtle race might be more like it, the install runs pretty slow, so get some coffee, or maybe a second bowl of ice cream if you’re doing a late night install.

u710_009

I did encounter one error during the install. You may see this as well, but you can go back later and correct this through the normal updates process.

u710_010

Now Ubuntu will finish, and ask if we want to reboot. Tell it no, then reboot by shutting down by pressing the red shut down icon in the very upper right of the Ubuntu window.

OK, you’ll have to be very quick with this next step. Remember the mouse issue? We’ll still need to fix it. First, boot the new machine, after clicking on CD and releasing the ISO if it’s still held. Now when you see the words “Grub loader” hit the Escape key. If you were fast enough, you’ll see this screen.

u710_012

With the line you see selected, press the “e” to edit the line. Now a new screen will appear.

u710_013

Move the highlight down one to the Kernal line, and press “e” to edit that line. When the new screen appears, you’ll need to add two dashes, then the i8042.noloop command. Your screen should look like this:

u710_014

Press Enter, then when you are returned to the screen with “kernel…” on it, make sure the kernel line is still highlighted and press b to boot.

Once booted, login using your user id and password. When Unbuntu is up, it’s time to fix the mouse issue once and for all. Click on Applications, Accessories, Terminal. When the terminal window appears, type in:

sudo gedit /boot/grub/menu.lst

u710_015

When you press Enter you’ll be prompted for your password, enter it. An editor should appear. Scroll down to the very bottom of the text and find the line that begins with “kernel”. Add the – i8042.noloop to the end of the line, as I’ve shown below. (Note I have highlighted the line to make it easy to see, yours won’t be normally highlighted in your session.)

u710_016

Save the file and exit the editor and the terminal window. When you next reboot, you should be able to just login normally, and the mouse should work.

And there you go, Ubuntu 7.10 up and running, complete with mouse, under Virtual PC 2007.

P.S. If you found this post useful, please give it a Digg so others can find the same happiness you did.

Go Sara Go

I think I may have just discovered the most useful blog on the internet for Visual Studio developers. Sara Ford, who just took over as Program Manager for Code Plex (http://www.codeplex.com) posts a “Visual Studio Tip of the Day”. They are short but wow are they useful. I can’t believe I haven’t discovered this gem before today, but hey better late than never.

Go read her blog at http://blogs.msdn.com/saraford/default.aspx and tell her “Go Sara Go!”

Conference Recovery

Whew. I got home from DevLink late last night, wired but tired as they say. I went to some really great sessions. Most of my time was spent in SQL Server 2005 sessions, I spend most of my time these days in the SQL Server Business Intelligence arena, so decided to focus my efforts there.

I have to hand it to those folks in Nashville, they did a great job. Registration was very smooth, things seemed well organized, and ran well. All in all I had a great time and can’t wait to go back next year.

Over the next few days I’ll comment on some of the more interesting sessions I attended and provide a few links. For tonight though I’m still in a state of shock, gotta get my “think meat” organized.

The Silverlight Match the Dot Net Rocks Hosts Game – Part 3 – The Javascript

Today I’ve posted the Javascript for my DNR game (see posts from last two days). As you can see, it’s very straight forward and doesn’t require much explanation.

When the app loads, it calls a routine that uses a random number generator to randomly select a layout for the photos.

The other large routine handles mouse clicks. The really tricky part was determining which images were displayed and which were not. I finally resorted to using the Tag property of each Canvas control. In the Tag I put two numbers, each being a 0 or a 1. The first number represents a Boolean that flags whether the images is visible or not. The second position notes whether the image has already been matched. There are a number of SetCanvas… helper routines to make the setting of these flags a bit easier.

I make a lot of use of Math.Random to generate random numbers. I then use these to determine various messages that get displayed across the middle of the screen, tha way the user won’t get bored.

OK, enough talk, here’s the code. I’ve documented it pretty well, but if you have questions send me an e-mail or post a comment.

 

if (!window.DNRMatch)

  window.DNRMatch = {};

 

DNRMatch.Scene = function()

{

}

 

// ****************************************************************************

// * Author: Robert C. Cain, Arcane Code, http://arcanecode.com

// *

// * Notes

// *

// * The heart of the system relies on the tag property of the Canvas controls

// * used to display the pictures. It was the only mechanism I could find to

// * easily provide persistance between calls.

// *

// * The tag currently is a two character string. The first character is 0

// * or 1 and indicates if the ? is showing (a 0) or the Answer image is

// * being displayed (value is 1).

// *

// * The second position is also a 0 or 1, and indicates if the image has been

// * matched yet by the user or not. (0=no, 1=yes)

// *

// ****************************************************************************

 

// I store this here as it gets loaded and used in several places

var imgList = new Array(10);

 

DNRMatch.Scene.prototype =

{

  // Runs when app is first loaded

  handleLoad: function(plugIn, userContext, rootElement)

  {

    // Get the inital list of images into imgList array

    RandomizeImages();

 

    this.plugIn = plugIn;

 

    // Can’t use SetImages function since we don’t have a sender

    for(i=0; i<10; i++)

    {     

      plugIn.content.findName(“imgAnswer0″ + i.toString()).Source=imgList[i];

    }

  }

}

 

//=============================================================================

// Main routine, activated when a user clicks on any image

//=============================================================================

function imgMouseLeftButtonDown(sender, mouseEventArgs)

{           

    var imgNumber = sender.Name.substr(3,2);

    var imgIndex = imgNumber.valueOf();

    var msg = sender.findName(“StatusArea”);

    var imgCurrentImage = sender.findName(“imgAnswer” + imgNumber);

 

    // Show the hidden image   

    if(CanvasVisible(sender, imgIndex)==“0″)

    {

      sender.findName(“img” + imgNumber + “RevealAnimation”).begin();

      sender.tag = “1″ + sender.tag.substr(1,1);

    }

 

    var msgText=“”;

    var foundMatch=false;

 

    // Now check to see if another image is also visible.

    // If so, we’ll want to check for a match.     

    for(i=0; i<10; i++)

    {

      // If we’re not dealing with the same image

      if(i != imgIndex)

      {

        var imgLoopAnswer = sender.findName(“imgAnswer” + imgNumber);

 

        // If the image is visible

        if(CanvasVisible(sender, i.toString()) == “1″)

        {

          // See if it’s same image

          if( GetSource(sender, i)==GetSource(sender, imgIndex) )

          {

            // Congratulate user, then set the matched tags

            msg.text=GoodJobMessage(sender, i);

            SetCanvasMatchedTag(sender, i);

            SetCanvasMatchedTag(sender, imgIndex);

            foundMatch=true;

          }

        }

      }

    } 

 

    // See if we need to show a “no match” message

    if(foundMatch==false// If we didn’t find a match…

    {

      // If there are two items visible

      if (VisibleButUnmatchedCount(sender)==2)

      {

        // Show the bad job message

        msg.text=BadJobMessage();

      }

      else

      {

        // Only one image visible, set message to empty

        msg.text=“”;

      }

    }

 

    // Reset non-matches

    if (VisibleButUnmatchedCount(sender)==2)

    {

      for(i=0; i<10; i++)  // for each canvas

      {

        if(CanvasMatched(sender, i) == “0″) // if it hasn’t been matched

        {

          if(CanvasVisible(sender, i) == “1″// but it’s visible

          {

            sender.findName(“img0″ + i.toString() + “HideAnimation”).begin(); // hide it

            SetCanvasInVisibleTag(sender, i);

            SetCanvasUnMatchedTag(sender, i);

          }

        }

      }     

    }

 

    // Check For Victory

    if(MatchCount(sender)==10)

    {

      msg.text=“You Won!!! Press Reset Game to play again.”;

    }

 

//    Just some debugging code, will leave it as it makes for good demo   

//    msg.text=ShowTags(sender);

//    msg.text=ShowSources(sender);

}

 

//=============================================================================

// Check the Tag property of the canvas to see if the image is visible

//=============================================================================

function CanvasVisible(sender, canvasNumber)

{

  var canvasNum = canvasNumber.toString();

  if(canvasNum.length > 1)

  {

    canvasNum = canvasNum.substr(canvasNum.length -  1, 1);

  }

  var currentCanvas = sender.findName(“img0″ + canvasNum);

  var isCanvasVisible = currentCanvas.tag.substr(0,1);

  return isCanvasVisible;

 

}

 

//=============================================================================

// Check the Tag property of the canvas to see if the image has been matched

//=============================================================================

function CanvasMatched(sender, canvasNumber)

{

  var canvasNum = canvasNumber.toString();

  if(canvasNum.length > 1)

  {

    canvasNum = canvasNum.substr(canvasNum.length -  1, 1);

  }

  var currentCanvas = sender.findName(“img0″ + canvasNum);

  var isCanvasMatched = currentCanvas.tag.substr(1,1);

  return isCanvasMatched;

}

 

//=============================================================================

// Set the Tag property of the canvas to indicate

// this canvas’s image is visible

//=============================================================================

function SetCanvasVisibleTag(sender, canvasNumber)

{

  var canvasNum = canvasNumber.toString();

  if(canvasNum.length > 1)

  {

    canvasNum = canvasNum.substr(canvasNum.length -  1, 1);

  }

  var currentCanvas = sender.findName(“img0″ + canvasNum);

  currentCanvas.tag = “1″ + currentCanvas.tag.substr(1,1);

}

 

//=============================================================================

// Set the Tag property of the canvas to indicate

// this canvas’s image is not visible

//=============================================================================

function SetCanvasInVisibleTag(sender, canvasNumber)

{

  var canvasNum = canvasNumber.toString();

  if(canvasNum.length > 1)

  {

    canvasNum = canvasNum.substr(canvasNum.length -  1, 1);

  }

  var currentCanvas = sender.findName(“img0″ + canvasNum);

  currentCanvas.tag = “0″ + currentCanvas.tag.substr(1,1);

}

 

//=============================================================================

// Set the Tag property of the canvas to indicate

// this canvas’s image has been matched

//=============================================================================

function SetCanvasMatchedTag(sender, canvasNumber)

{

  var canvasNum = canvasNumber.toString();

  if(canvasNum.length > 1)

  {

    canvasNum = canvasNum.substr(canvasNum.length -  1, 1);

  }

  var currentCanvas = sender.findName(“img0″ + canvasNum);

  currentCanvas.tag = currentCanvas.tag.substr(0,1) + “1″ ;

}

 

//=============================================================================

// Set the Tag property of the canvas to indicate

// this canvas’s image is not matched

//=============================================================================

function SetCanvasUnMatchedTag(sender, canvasNumber)

{

  var canvasNum = canvasNumber.toString();

  if(canvasNum.length > 1)

  {

    canvasNum = canvasNum.substr(canvasNum.length -  1, 1);

  }

  var currentCanvas = sender.findName(“img0″ + canvasNum);

  currentCanvas.tag = currentCanvas.tag.substr(0,1) + “0″ ;

}

 

//=============================================================================

// Count the number of items left that are visible, but not

// matched. In theory this should either be 0, 1 or 2.

//=============================================================================

function VisibleButUnmatchedCount(sender)

{

  var retVal=0;

 

  for(i=0; i<10; i++)

  {

    if(CanvasVisible(sender, i)==“1″)

    {

      if(CanvasMatched(sender, i)==“0″)

      {

        retVal++;

      }

    }

  }

 

  return retVal;

}

 

//=============================================================================

// Count the number of items that are matched.

// When it reached 10 we’ll know they won

//=============================================================================

function MatchCount(sender)

{

  var retVal=0;

 

  for(i=0; i<10; i++)

  {

    if(CanvasMatched(sender, i)==“1″)

    {

      retVal++;

    }

  }

 

  return retVal;

}

 

//=============================================================================

// Get the file name of the image used in the sender control

//=============================================================================

function GetSource(sender, imageNumber)

{

  var imgNum = imageNumber.toString();

  if(imgNum.length > 1)

  {

    imgNum = imgNum.substr(imgNum.length – 1, 1);

  }

 

  var img = sender.findName(“imgAnswer0″ + imgNum );

  return img.Source; 

}

 

//=============================================================================

// Create a “Bad Guess” message

//=============================================================================

function BadJobMessage()

{

  // Randomly select from the list of “Try again” messages

  var possibleMsgs = new Array(5);

  possibleMsgs[0] = “Nope, try again.”;

  possibleMsgs[1] = “I don’t think so!”;

  possibleMsgs[2] = “Dude, not even close!”;

  possibleMsgs[3] = “Don’t give up yet, keep trying!”;

  possibleMsgs[4] = “Ha! Don’t make me laugh.”;

  possibleMsgs[5] = “I’ve heard of wild guesses, but gee whiz. Try again.”;

 

  var randomNum = Math.floor(Math.random()*6);

 

  return possibleMsgs[randomNum];

 

}

 

//=============================================================================

// Create a Good Job message based on the image the user

// just matched.

//=============================================================================

function GoodJobMessage(sender, imageNumber)

{

  var retMsg = “Good Job.”;

  var possibleMsgs = new Array(9);

 

  if(GetSource(sender, imageNumber)==“Carl02.png”)

  {

    possibleMsgs[0] = “Carl says you da man!”;

    possibleMsgs[1] = “You must be that squirt king of C#. We will make a T-shirt for you.”;

    possibleMsgs[2] = “A catheter and somebody to bring you a sandwich once in a while. That’s living man.”;

    possibleMsgs[3] = “While you shampoo your hair… Wait I guess that rules out C++ programmers.”;

    possibleMsgs[4] = “Some people go to sleep listening to DNR.”;

    possibleMsgs[5] = “”;

    possibleMsgs[6] = “”;

    possibleMsgs[7] = “”;

    possibleMsgs[8] = “”;

    retMsg = possibleMsgs[Math.floor(Math.random()*5)];

  }

 

  if(GetSource(sender, imageNumber)==“Richard01.png”)

  {

    possibleMsgs[0] = “It’s Richard, the Toy Boy!”;

    possibleMsgs[1] = “Now we got emotional baggage.”;

    possibleMsgs[2] = “Ballmer is always great. He seems a lot less angry these days, I think they switched him to decaf.”;

    possibleMsgs[3] = “Everytime you say Web 2.0, a startup dies.”;

    possibleMsgs[4] = “My favorite Martian is Mark Miller!”;

    possibleMsgs[5] = “Don’t play with it, just look at it!”;

    possibleMsgs[6] = “You [Mark Miller] are the Jim Carey of the podcasting world.”;

    possibleMsgs[7] = “Welcome to the dark side baby!”;

    possibleMsgs[8] = “”;

    retMsg = possibleMsgs[Math.floor(Math.random()*8)];

  }

 

  if(GetSource(sender, imageNumber)==“Rory.png”)

  {

    possibleMsgs[0] = “Rory rewards you by allowing you to gaze upon his magnificence.”;

    possibleMsgs[1] = “You are testing my patience Franklin!”;

    possibleMsgs[2] = “You’re like some kid from a third world country…”;

    possibleMsgs[3] = “It’s a place where you can talk about anal leakage.”;

    possibleMsgs[4] = “I was pretending to tweak Mark Dunn’s nipples like they were radio knobs.”;

    possibleMsgs[5] = “I was talking about how weird it was to see Mark Dunn in the flesh.”;

    possibleMsgs[6] = “”;

    possibleMsgs[7] = “”;

    possibleMsgs[8] = “”;

    retMsg = possibleMsgs[Math.floor(Math.random()*5)];

  }

 

  if(GetSource(sender, imageNumber)==“Mark.png”)

  {

    possibleMsgs[0] = “Mark Dunn says it’s always sunny in the south!”;

    possibleMsgs[1] = “I gotta clean my underwear now Carl.”;

    possibleMsgs[2] = “I am higher than a California Condor on Ecstasy, that’s how excited I am to be here.”;

    possibleMsgs[3] = “”;

    possibleMsgs[4] = “”;

    possibleMsgs[5] = “”;

    possibleMsgs[6] = “”;

    possibleMsgs[7] = “”;

    possibleMsgs[8] = “”;

    retMsg = possibleMsgs[Math.floor(Math.random()*3)];

  }

 

  if(GetSource(sender, imageNumber)==“Miller.png”)

  {

    possibleMsgs[0] = “I got nuthin’ man.”;

    possibleMsgs[1] = “You guys are messing with me again!”;

    possibleMsgs[2] = “My golden ticket on the candy bar said it was about a new co-host!”;

    possibleMsgs[3] = “I want one of these!”;

    possibleMsgs[4] = “What about me Carl?”;

    possibleMsgs[5] = “I’m good lookin too!”;

    possibleMsgs[6] = “”;

    possibleMsgs[7] = “”;

    possibleMsgs[8] = “”;

    retMsg = possibleMsgs[Math.floor(Math.random()*6)];

  }

 

  return retMsg;

}

 

//=============================================================================

// Reset the game for another play

//=============================================================================

function ResetGame(sender, mouseEventArgs)

{

    sender.findName(“mainHideAnimation”).begin(); // hide it

 

    for(i=0; i<10; i++)  // for each canvas

    {

      SetCanvasUnMatchedTag(sender, i);

      if(CanvasVisible(sender, i) == “1″// but it’s visible

      {

        sender.findName(“img0″ + i.toString() + “HideAnimation”).begin(); // hide it

        SetCanvasInVisibleTag(sender, i);

      }

    }

 

    RandomizeImages();

    SetImages(sender);

 

    var msg = sender.findName(“StatusArea”);

    msg.text = “Play the game!”;

 

    sender.findName(“mainRevealAnimation”).begin(); // hide it

 

}

 

//=============================================================================

// Create a random number, then use it to pick and load the

// imgList array. Used to mix up the images.

//=============================================================================

function RandomizeImages()

{

    // Default set

    imgList[0] = “Carl02.png”;

    imgList[1] = “Richard01.png”;

    imgList[2] = “Rory.png”;

    imgList[3] = “Mark.png”;

    imgList[4] = “Miller.png”;

    imgList[5] = “Miller.png”;

    imgList[6] = “Mark.png”;

    imgList[7] = “Rory.png”;

    imgList[8] = “Richard01.png”;

    imgList[9] = “Carl02.png”;

 

    var randomNum = Math.floor(Math.random()*9);

 

    if(randomNum==0)

    {

      imgList[0] = “Carl02.png”;

      imgList[1] = “Richard01.png”;

      imgList[2] = “Rory.png”;

      imgList[3] = “Mark.png”;

      imgList[4] = “Miller.png”;

      imgList[5] = “Miller.png”;

      imgList[6] = “Mark.png”;

      imgList[7] = “Rory.png”;

      imgList[8] = “Richard01.png”;

      imgList[9] = “Carl02.png”;

    }

 

    if(randomNum==1)

    {

      imgList[0] = “Rory.png”;

      imgList[1] = “Mark.png”;

      imgList[2] = “Miller.png”;

      imgList[3] = “Richard01.png”;

      imgList[4] = “Carl02.png”;

      imgList[5] = “Rory.png”;

      imgList[6] = “Carl02.png”;

      imgList[7] = “Miller.png”;

      imgList[8] = “Richard01.png”;

      imgList[9] = “Mark.png”;

    }

 

    if(randomNum==2)

    {

      imgList[0] = “Rory.png”;

      imgList[1] = “Mark.png”;

      imgList[2] = “Miller.png”;

      imgList[3] = “Richard01.png”;

      imgList[4] = “Carl02.png”;

      imgList[5] = “Mark.png”;

      imgList[6] = “Rory.png”;

      imgList[7] = “Carl02.png”;

      imgList[8] = “Richard01.png”;

      imgList[9] = “Miller.png”;

    }

 

    if(randomNum==3)

    {

      imgList[0] = “Carl02.png”;

      imgList[1] = “Rory.png”;

      imgList[2] = “Richard01.png”;

      imgList[3] = “Carl02.png”;

      imgList[4] = “Miller.png”;

      imgList[5] = “Rory.png”;

      imgList[6] = “Miller.png”;

      imgList[7] = “Mark.png”;

      imgList[8] = “Richard01.png”;

      imgList[9] = “Mark.png”;

    }

 

    if(randomNum==4)

    {

      imgList[0] = “Mark.png”;

      imgList[1] = “Rory.png”;

      imgList[2] = “Rory.png”;

      imgList[3] = “Carl02.png”;

      imgList[4] = “Miller.png”;

      imgList[5] = “Carl02.png”;

      imgList[6] = “Richard01.png”;

      imgList[7] = “Mark.png”;

      imgList[8] = “Richard01.png”;

      imgList[9] = “Miller.png”;

    }

 

    if(randomNum==5)

    {

      imgList[0] = “Carl02.png”;

      imgList[1] = “Mark.png”;

      imgList[2] = “Richard01.png”;

      imgList[3] = “Richard01.png”;

      imgList[4] = “Rory.png”;

      imgList[5] = “Mark.png”;

      imgList[6] = “Rory.png”;

      imgList[7] = “Carl02.png”;

      imgList[8] = “Miller.png”;

      imgList[9] = “Miller.png”;

    }

 

    if(randomNum==6)

    {

      imgList[0] = “Mark.png”;

      imgList[1] = “Carl02.png”;

      imgList[2] = “Mark.png”;

      imgList[3] = “Richard01.png”;

      imgList[4] = “Rory.png”;

      imgList[5] = “Miller.png”;

      imgList[6] = “Rory.png”;

      imgList[7] = “Miller.png”;

      imgList[8] = “Richard01.png”;

      imgList[9] = “Carl02.png”;

    }

 

    if(randomNum==7)

    {

      imgList[0] = “Rory.png”;

      imgList[1] = “Carl02.png”;

      imgList[2] = “Mark.png”;

      imgList[3] = “Richard01.png”;

      imgList[4] = “Mark.png”;

      imgList[5] = “Miller.png”;

      imgList[6] = “Miller.png”;

      imgList[7] = “Richard01.png”;

      imgList[8] = “Carl02.png”;

      imgList[9] = “Rory.png”;

    }

 

    if(randomNum==8)

    {

      imgList[0] = “Richard01.png”;

      imgList[1] = “Rory.png”;

      imgList[2] = “Mark.png”;

      imgList[3] = “Carl02.png”;

      imgList[4] = “Miller.png”;

      imgList[5] = “Mark.png”;

      imgList[6] = “Carl02.png”;

      imgList[7] = “Richard01.png”;

      imgList[8] = “Rory.png”;

      imgList[9] = “Miller.png”;

    }

}

 

//=============================================================================

// Sets the image source property for all the images

// to the image name loaded in the imgList array

//=============================================================================

function SetImages(sender)

{

    for(i=0; i<10; i++)

    {     

      sender.findName(“imgAnswer0″ + i.toString()).Source=imgList[i];

    }

}

 

//=============================================================================

// Causes the job to wait a certain number of milliseconds.

// Has a drawback in that it does not pause animations,

// so if you use it while an animation is happening you won’t

// see the animation effect but it will ‘jerk’ to the final result.

//=============================================================================

function wait(msecs)

{

  var start = new Date().getTime();

  var cur = start

  while(cur – start < msecs)

  {

    cur = new Date().getTime();

  }

}

 

//=============================================================================

// For debugging only – Shows all the images.

//=============================================================================

function ShowSources(sender)

{

  var retVal=“”;

  for(i=0; i<10; i++)

  {

 

    retVal += i.toString() + “: “ + GetSource(sender, i);

  }

  return retVal;

}

 

 

//=============================================================================

// For debugging only – Shows the tags

//=============================================================================

function ShowTags(sender)

{

  retVal = “”;

  for(i=0; i<10; i++)

  {

    var currentCanvas = sender.findName(“img0″ + i.toString());

    retVal += currentCanvas.tag + ” “;

  }

  return retVal; 

}

Thanks for the Memory

I don’t normally do thinks like this, especially when I’m not making any money off of it, but I have to pass along a “hot deal” tip. As we all know, developers computers crave lots of memory, especially running Vista. Staples has memory on sale, including one specific type of PNY notebook memory. A 1 Gig PC2700 DDR333 is going for the out the door cost of 59.99 (US Dollars). That is a savings of well over one hundred (of those same US) dollars. I took advantage to upgrade my HP 8195 Laptop to 2 full Gig, maxing it out.  They also have several types of desktop ram on sale, check your paper. These are on sale until close of business Saturday 10/13.

Let me be clear, I am not making any money, kickbacks, or am in any way affiliated with Staples (other than shelling out my sixty bucks to them). I just wanted to pass along a good deal to my readers who may not have picked up the sale papers.

The Silverlight Match the Dot Net Rocks Hosts Game – Part 2 – The XAML

Because it’s quite long I’ve posted the XAML at the foot of this post. It’s pretty straight forward though. In Silverlight 1.0 XAML, you’re restricted to using a Canvas for your containers.

In this case I use 10 child canvases, one for each of the photos. At the start of the XAML for each image canvas I have two Storyboards in the Canvas.Resources section. One storyboard fades the question mark out then the photo in. The other reverses it, fading out the photo and in the question mark.

Next I put a rectangle around the entire game, then display the title bar. After that I display the instructions at the bottom using colors similar to the title bar.

A series of 10 canvases come next, each one holds two image controls, one for the question mark and one for the picture of the host. When the app starts, the Javascript replaces the Source property for each of the Answer images with a photo of the host, but you’ll see that tomorrow.

The only other thing to note is how mouse clicks are captured. There is no Click even as you might be used to with WPF, instead you have to capture the MouseLeftButtonDown. I route all 10 images’ MouseLeftButtonDown events to the same Javascript method, and use the Sender parameter to differentiate which of the 10 Canvases were clicked.

The final thing to note is the Reset Game button, which really isn’t a button but a canvas, which holds a rectangle with some cool gradients and a text block. Just like with the images, the canvas’ MouseLeftButtonDown is captured and the corresponding Javascript routine is called.

OK, enough explanation for today, here’s the code:

 

<Canvas xmlns=http://schemas.microsoft.com/client/2007

        xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml

        x:Name=mainCanvas

        Width=800 Height=500 Background=Black>

 

  <Canvas.Resources>

    <Storyboard x:Name=mainRevealAnimation >

      <DoubleAnimation Duration=00:00:01.5 From=0 To=1

                      Storyboard.TargetName=mainCanvas

                        Storyboard.TargetProperty=Opacity  />

    </Storyboard>

    <Storyboard x:Name=mainHideAnimation >

      <DoubleAnimation Duration=00:00:01.5 From=1 To=0

                      Storyboard.TargetName=mainCanvas

                        Storyboard.TargetProperty=Opacity  />

    </Storyboard>

 

    <Storyboard x:Name=img00RevealAnimation >

      <DoubleAnimation Duration=00:00:00.5 From=1 To=0

                      Storyboard.TargetName=imgQuestion00

                        Storyboard.TargetProperty=Opacity  />

      <DoubleAnimation Duration=00:00:00.5 From=0 To=1

                      Storyboard.TargetName=imgAnswer00

                        Storyboard.TargetProperty=Opacity  />

    </Storyboard>

    <Storyboard x:Name=img00HideAnimation >

      <DoubleAnimation Duration=00:00:00.5 From=1 To=0

                      Storyboard.TargetName=imgAnswer00

                        Storyboard.TargetProperty=Opacity  />

      <DoubleAnimation Duration=00:00:00.5 From=0 To=1

                      Storyboard.TargetName=imgQuestion00

                        Storyboard.TargetProperty=Opacity  />

    </Storyboard>

    <Storyboard x:Name=img01RevealAnimation >

      <DoubleAnimation Duration=00:00:00.5 From=1 To=0

                      Storyboard.TargetName=imgQuestion01

                        Storyboard.TargetProperty=Opacity  />

      <DoubleAnimation Duration=00:00:00.5 From=0 To=1

                      Storyboard.TargetName=imgAnswer01

                        Storyboard.TargetProperty=Opacity  />

    </Storyboard>

    <Storyboard x:Name=img01HideAnimation >

      <DoubleAnimation Duration=00:00:00.5 From=1 To=0

                      Storyboard.TargetName=imgAnswer01

                        Storyboard.TargetProperty=Opacity  />

      <DoubleAnimation Duration=00:00:00.5 From=0 To=1

                      Storyboard.TargetName=imgQuestion01

                        Storyboard.TargetProperty=Opacity  />

    </Storyboard>

    <Storyboard x:Name=img02RevealAnimation >

      <DoubleAnimation Duration=00:00:00.5 From=1 To=0

                      Storyboard.TargetName=imgQuestion02

                        Storyboard.TargetProperty=Opacity  />

      <DoubleAnimation Duration=00:00:00.5 From=0 To=1

                      Storyboard.TargetName=imgAnswer02

                        Storyboard.TargetProperty=Opacity  />

    </Storyboard>

    <Storyboard x:Name=img02HideAnimation >

      <DoubleAnimation Duration=00:00:00.5 From=1 To=0

                      Storyboard.TargetName=imgAnswer02

                        Storyboard.TargetProperty=Opacity  />

      <DoubleAnimation Duration=00:00:00.5 From=0 To=1

                      Storyboard.TargetName=imgQuestion02

                        Storyboard.TargetProperty=Opacity  />

    </Storyboard>

    <Storyboard x:Name=img03RevealAnimation >

      <DoubleAnimation Duration=00:00:00.5 From=1 To=0

                      Storyboard.TargetName=imgQuestion03

                        Storyboard.TargetProperty=Opacity  />

      <DoubleAnimation Duration=00:00:00.5 From=0 To=1

                      Storyboard.TargetName=imgAnswer03

                        Storyboard.TargetProperty=Opacity  />

    </Storyboard>

    <Storyboard x:Name=img03HideAnimation >

      <DoubleAnimation Duration=00:00:00.5 From=1 To=0

                      Storyboard.TargetName=imgAnswer03

                        Storyboard.TargetProperty=Opacity  />

      <DoubleAnimation Duration=00:00:00.5 From=0 To=1

                      Storyboard.TargetName=imgQuestion03

                        Storyboard.TargetProperty=Opacity  />

    </Storyboard>

    <Storyboard x:Name=img04RevealAnimation >

      <DoubleAnimation Duration=00:00:00.5 From=1 To=0

                      Storyboard.TargetName=imgQuestion04

                        Storyboard.TargetProperty=Opacity  />

      <DoubleAnimation Duration=00:00:00.5 From=0 To=1

                      Storyboard.TargetName=imgAnswer04

                        Storyboard.TargetProperty=Opacity  />

    </Storyboard>

    <Storyboard x:Name=img04HideAnimation >

      <DoubleAnimation Duration=00:00:00.5 From=1 To=0

                      Storyboard.TargetName=imgAnswer04

                        Storyboard.TargetProperty=Opacity  />

      <DoubleAnimation Duration=00:00:00.5 From=0 To=1

                      Storyboard.TargetName=imgQuestion04

                        Storyboard.TargetProperty=Opacity  />

    </Storyboard>

    <Storyboard x:Name=img05RevealAnimation >

      <DoubleAnimation Duration=00:00:00.5 From=1 To=0

                      Storyboard.TargetName=imgQuestion05

                        Storyboard.TargetProperty=Opacity  />

      <DoubleAnimation Duration=00:00:00.5 From=0 To=1

                      Storyboard.TargetName=imgAnswer05

                        Storyboard.TargetProperty=Opacity  />

    </Storyboard>

    <Storyboard x:Name=img05HideAnimation >

      <DoubleAnimation Duration=00:00:00.5 From=1 To=0

                      Storyboard.TargetName=imgAnswer05

                        Storyboard.TargetProperty=Opacity  />

      <DoubleAnimation Duration=00:00:00.5 From=0 To=1

                      Storyboard.TargetName=imgQuestion05

                        Storyboard.TargetProperty=Opacity  />

    </Storyboard>

    <Storyboard x:Name=img06RevealAnimation >

      <DoubleAnimation Duration=00:00:00.5 From=1 To=0

                      Storyboard.TargetName=imgQuestion06

                        Storyboard.TargetProperty=Opacity  />

      <DoubleAnimation Duration=00:00:00.5 From=0 To=1

                      Storyboard.TargetName=imgAnswer06

                        Storyboard.TargetProperty=Opacity  />

    </Storyboard>

    <Storyboard x:Name=img06HideAnimation >

      <DoubleAnimation Duration=00:00:00.5 From=1 To=0

                      Storyboard.TargetName=imgAnswer06

                        Storyboard.TargetProperty=Opacity  />

      <DoubleAnimation Duration=00:00:00.5 From=0 To=1

                      Storyboard.TargetName=imgQuestion06

                        Storyboard.TargetProperty=Opacity  />

    </Storyboard>

    <Storyboard x:Name=img07RevealAnimation >

      <DoubleAnimation Duration=00:00:00.5 From=1 To=0

                      Storyboard.TargetName=imgQuestion07

                        Storyboard.TargetProperty=Opacity  />

      <DoubleAnimation Duration=00:00:00.5 From=0 To=1

                      Storyboard.TargetName=imgAnswer07

                        Storyboard.TargetProperty=Opacity  />

    </Storyboard>

    <Storyboard x:Name=img07HideAnimation >

      <DoubleAnimation Duration=00:00:00.5 From=1 To=0

                      Storyboard.TargetName=imgAnswer07

                        Storyboard.TargetProperty=Opacity  />

      <DoubleAnimation Duration=00:00:00.5 From=0 To=1

                      Storyboard.TargetName=imgQuestion07

                        Storyboard.TargetProperty=Opacity  />

    </Storyboard>

    <Storyboard x:Name=img08RevealAnimation >

      <DoubleAnimation Duration=00:00:00.5 From=1 To=0

                      Storyboard.TargetName=imgQuestion08

                        Storyboard.TargetProperty=Opacity  />

      <DoubleAnimation Duration=00:00:00.5 From=0 To=1

                      Storyboard.TargetName=imgAnswer08

                        Storyboard.TargetProperty=Opacity  />

    </Storyboard>

    <Storyboard x:Name=img08HideAnimation >

      <DoubleAnimation Duration=00:00:00.5 From=1 To=0

                      Storyboard.TargetName=imgAnswer08

                        Storyboard.TargetProperty=Opacity  />

      <DoubleAnimation Duration=00:00:00.5 From=0 To=1

                      Storyboard.TargetName=imgQuestion08

                        Storyboard.TargetProperty=Opacity  />

    </Storyboard>

    <Storyboard x:Name=img09RevealAnimation >

      <DoubleAnimation Duration=00:00:00.5 From=1 To=0

                      Storyboard.TargetName=imgQuestion09

                        Storyboard.TargetProperty=Opacity  />

      <DoubleAnimation Duration=00:00:00.5 From=0 To=1

                      Storyboard.TargetName=imgAnswer09

                        Storyboard.TargetProperty=Opacity  />

    </Storyboard>

    <Storyboard x:Name=img09HideAnimation >

      <DoubleAnimation Duration=00:00:00.5 From=1 To=0

                      Storyboard.TargetName=imgAnswer09

                        Storyboard.TargetProperty=Opacity  />

      <DoubleAnimation Duration=00:00:00.5 From=0 To=1

                      Storyboard.TargetName=imgQuestion09

                        Storyboard.TargetProperty=Opacity  />

    </Storyboard>

 

  </Canvas.Resources>

 

  <Rectangle Width=800 Height=500 Canvas.Left=0 Canvas.Top=0>

    <Rectangle.Fill>

      <LinearGradientBrush>

        <GradientStop Color=#00000000 Offset=0.0 />

        <GradientStop Color=#55555555 Offset=0.25 />

        <GradientStop Color=#00000000 Offset=0.75 />

        <GradientStop Color=#88888888 Offset=1.0 />

      </LinearGradientBrush>

    </Rectangle.Fill>

  </Rectangle>

 

  <!– Title bar –>

  <Rectangle x:Name=TitleBar Canvas.Left=25 Canvas.Top=10

            Width=750 Height=75 RadiusX=16 RadiusY=16

            Stroke=#E0F0F0 StrokeThickness=8>

    <Rectangle.Fill>

      <LinearGradientBrush StartPoint=0.5,0 EndPoint=0.5,1 >

        <GradientStop Color=#FFC4E1F0 Offset=0/>

        <GradientStop Color=#FF64E1F0 Offset=1/>

      </LinearGradientBrush>

    </Rectangle.Fill>

  </Rectangle>

 

  <TextBlock Canvas.Left=50 Canvas.Top=33

            FontFamily=Arial FontSize=28 FontWeight=Bold>

    Arcane Code’s Match the Dot Net Rocks Hosts Game

  </TextBlock>

 

  <!– Instructions –>

  <Canvas Canvas.Left=50 Canvas.Top=390>

    <Rectangle x:Name=Instructions Canvas.Top=0 Canvas.Left=0

            Width=625 Height=105 RadiusX=16 RadiusY=16

            Stroke=#E0F0F0 StrokeThickness=4>

      <Rectangle.Fill>

        <LinearGradientBrush StartPoint=0.5,0 EndPoint=0.5,1 >

          <GradientStop Color=#FFC4E1F0 Offset=0/>

          <GradientStop Color=#FF64E1F0 Offset=1/>

        </LinearGradientBrush>

      </Rectangle.Fill>

    </Rectangle>

 

    <TextBlock Canvas.Left=10 Canvas.Top=15

              FontFamily=Arial FontSize=16 FontWeight=Bold>

      Instructions

    </TextBlock>

 

    <TextBlock Canvas.Left=15 Canvas.Top=38 Width=550

              FontFamily=Arial FontSize=12 TextWrapping=Wrap >

      Click on the question marks to reveal the hosts of Dot Net Rocks. Carl Franklin,

      Richard Campbell, Rory Blyth, Mark Dunn and Mark Miller challenge you to find them.

      Watch the message bar in the middle for quotes from the DNR hosts.

      (Yes, I know, technically Mark Miller isn’t a host, but every time he’s on he thinks he

      is, so I decided to let him live out his fantasies in this game.)

    </TextBlock>

  </Canvas>

 

  <!– The images –>

  <Canvas x:Name=img00 Canvas.Left=050 Canvas.Top=100 Height=100

      MouseLeftButtonDown=imgMouseLeftButtonDown Tag=00    >

    <Image x:Name=imgQuestion00 Height=100 Width=100 Source=Dunno.png />

    <Image x:Name=imgAnswer00 Height=100 Source=Carl01.png Opacity=0 />

  </Canvas>

 

  <Canvas x:Name=img01 Canvas.Left=200 Canvas.Top=100 Height=100

      MouseLeftButtonDown=imgMouseLeftButtonDown Tag=00    >

    <Image x:Name=imgQuestion01 Height=100 Width=100 Source=Dunno.png />

    <Image x:Name=imgAnswer01 Height=100 Source=Carl01.png Opacity=0 />

  </Canvas>

 

  <Canvas x:Name=img02 Canvas.Left=350 Canvas.Top=100 Height=100

      MouseLeftButtonDown=imgMouseLeftButtonDown Tag=00    >

    <Image x:Name=imgQuestion02 Height=100 Width=100 Source=Dunno.png />

    <Image x:Name=imgAnswer02 Height=100 Source=Carl01.png Opacity=0 />

  </Canvas>

 

  <Canvas x:Name=img03 Canvas.Left=500 Canvas.Top=100 Height=100

      MouseLeftButtonDown=imgMouseLeftButtonDown Tag=00    >

    <Image x:Name=imgQuestion03 Height=100 Width=100 Source=Dunno.png />

    <Image x:Name=imgAnswer03 Height=100 Source=Carl01.png Opacity=0 />

  </Canvas>

 

  <Canvas x:Name=img04 Canvas.Left=650 Canvas.Top=100 Height=100

      MouseLeftButtonDown=imgMouseLeftButtonDown Tag=00    >

    <Image x:Name=imgQuestion04 Height=100 Width=100 Source=Dunno.png />

    <Image x:Name=imgAnswer04 Height=100 Source=Carl01.png Opacity=0 />

  </Canvas>

 

  <Canvas x:Name=img05 Canvas.Left=050 Canvas.Top=250 Height=100

      MouseLeftButtonDown=imgMouseLeftButtonDown Tag=00    >

    <Image x:Name=imgQuestion05 Height=100 Width=100 Source=Dunno.png />

    <Image x:Name=imgAnswer05 Height=100 Source=Carl01.png Opacity=0 />

  </Canvas>

 

  <Canvas x:Name=img06 Canvas.Left=200 Canvas.Top=250 Height=100

      MouseLeftButtonDown=imgMouseLeftButtonDown Tag=00    >

    <Image x:Name=imgQuestion06 Height=100 Width=100 Source=Dunno.png />

    <Image x:Name=imgAnswer06 Height=100 Source=Carl01.png Opacity=0 />

  </Canvas>

 

  <Canvas x:Name=img07 Canvas.Left=350 Canvas.Top=250 Height=100

    MouseLeftButtonDown=imgMouseLeftButtonDown Tag=00    >

    <Image x:Name=imgQuestion07 Height=100 Width=100 Source=Dunno.png />

    <Image x:Name=imgAnswer07 Height=100 Source=Carl01.png Opacity=0 />

  </Canvas>

 

  <Canvas x:Name=img08 Canvas.Left=500 Canvas.Top=250 Height=100

      MouseLeftButtonDown=imgMouseLeftButtonDown Tag=00    >

    <Image x:Name=imgQuestion08 Height=100 Width=100 Source=Dunno.png />

    <Image x:Name=imgAnswer08 Height=100 Source=Carl01.png Opacity=0 />

  </Canvas>

 

  <Canvas x:Name=img09 Canvas.Left=650 Canvas.Top=250 Height=100

      MouseLeftButtonDown=imgMouseLeftButtonDown Tag=00    >

    <Image x:Name=imgQuestion09 Height=100 Width=100 Source=Dunno.png />

    <Image x:Name=imgAnswer09 Height=100 Source=Carl01.png Opacity=0 />

  </Canvas>

 

  <!– Message Bar across Middle of Screen –>

  <Rectangle Width=700 Height=26 Stroke=#FF000000 Canvas.Left=50 Canvas.Top=212>

    <Rectangle.Fill>

      <LinearGradientBrush EndPoint=1,0.5 StartPoint=0,0.5>

        <GradientStop Color=#FFDDDDDD Offset=1/>

        <GradientStop Color=#FF464646 Offset=0.393/>

        <GradientStop Color=#FE8B8B8B Offset=0.723/>

      </LinearGradientBrush>

    </Rectangle.Fill>

  </Rectangle>

 

  <TextBlock x:Name=StatusArea Canvas.Left=55 Canvas.Top=215

            FontSize=14 FontWeight=Medium Foreground=White >

    Match the Hosts!

  </TextBlock>

 

  <!– Reset Game Button–>

  <Canvas Canvas.Top=455 Canvas.Left=690 MouseLeftButtonDown=ResetGame>

 

    <Rectangle Stroke=Black StrokeThickness=2

        Width=100 Height=40

        RadiusX=10 RadiusY=10>

      <Rectangle.Fill>

        <LinearGradientBrush>

          <GradientStop Color=Gray Offset=0/>

          <GradientStop Color=Snow Offset=0.6/>

          <GradientStop Color=Gray Offset=1/>

        </LinearGradientBrush>

      </Rectangle.Fill>

    </Rectangle>

 

    <TextBlock Canvas.Top=10 Canvas.Left=10

              FontSize=14 FontWeight=Bold

              Text=Reset Game />

 

  </Canvas>

 

 

</Canvas>

The Silverlight Match the Dot Net Rocks Hosts Game – Part 1 – Intro

Alabama Code Camp 5 did something interesting, they sponsored a programming contest. The rules were pretty simple, you had to code a game using Silverlight 1.0. First prize would be awarded a Zune.

If I tell you I still want a Zune, you’ll be able to figure out how I did. Actually, I did come in second place, but that’s OK. I had a fun time coding the game, and it was neat to get in there roll up my sleeves and learn a new technology. Not to mention listening to all those old DNR episodes to get the quotes. (I still want a Zune though. ;-)

Here’s what I came up with, the Arcane Code Match the Dot Net Rocks Hosts Game. Play is quite simple, you click on the question marks. The first click reveals a DNR host, if you match with the second click the hosts photo stays up, and you are rewarded with a witty quote across the middle of the screen. If you fail to match, you are cruelly taunted then the pictures fade back to question marks. Match all the hosts to win. Below is an example before play begins. (Click for a bigger image).

Questions

And here is a game with all the hosts revealed. Carl Franklin, Mark Dunn, Rory Blyth, Richard Campbell and Mark Miller revealed in all their glory.

dnrmatch02

Now, before I get a zillion comments and e-mails, yes I am perfectly aware that Mark Miller is not a DNR Host. However, every time he gets on Mark Miller thinks he’s a host! And frankly, if it will help him to achieve his goal of 125 refactorings inside RefactorPro, then hey I’m certainly willing to help him live out his delusions inside the game.

Each time you play, by the way, the pictures get scrambled in a different order. In a few weeks I hope to figure out a place to host the game so you can actually play for yourself and not just have to look at static screen shots. Right now though I use WordPress for my blog and it (unfortunately) doesn’t do Silverlight.

Over the next two days I’ll show you the code behind, tomorrow I’ll post the XAML and Friday the Javascript. On both days I’ll talk a little bit about the code and what I did. If anyone would like a copy of the entire project, just shoot me an e-mail: arcanecode at gmail.com and I will send you a zip file with the whole project.

Apple – The New "Evil Empire"

First, in the interest of disclosure let me state right up front that I’m a “Microsoft fanboy”. Sue me. As a professional developer for the last 20 plus years, I think they make some really great stuff, and do a lot to get the word out to the developer community.

I say this because I really get tired of the gushing lately over how wonderful Apple is and how evil Microsoft is. Hmm, let’s contrast a moment, and I think the recent product announcements of the new Zunes makes a good place to do so.

For those who haven’t heard, Microsoft has just announced the release of some new Zunes, the Zune 80 (80 gig HD) and the Zune Flash series, which will have flash drives and come in 4 and 8 gig sizes. The original Zune will now be known as the Zune 30. There are a lot of cool new features in the interface, such as Podcasting, and the removal of the 3 day limit on listening to wi-fi shared songs, not to mention it just looks easier to use.

What’s really nice is all the early adopters of the original Zune 30’s won’t be left in the cold. There will be free upgrades so the older Zune 30’s will have all the same new software features as the newer Zunes.

Let’s contrast that with Apple and its iPhone and iTouch units. The iTouch, supposedly just a stripped down iPhone, yet there are many features such as e-mail that could be there, but are lacking for no apparent technical reason that anyone can explain.

Then there’s the iPhone itself, locked down worse than Fort Knox. For those who believe that when they plunk down 600 plus dollars for something it should be theirs to do with as they want, the very first upgrade rendered the iPhone into a brick. And once it’s bricked, expect no help from Apple.

Now, I wouldn’t expect them to try and restore everything, but the least they could do is a factory reset to brand new condition. I’d say that was very reasonable. But nope, you’re just stuck with a brick.

From everything I have been able to gather, there’s no real technical reason for the upgrade to force the phone to brick status. I could understand perhaps forcing a reset to “new” status, or that some apps would not work, but the complete paper weight scenario seems like nothing more than punishment on Apple’s part.

Before someone points out there are online sites with “debricking” software, my point is not about the phone, but about the attitude from the company that puts it out. Further, I also realize that ‘hacking’ the iPhone falls outside the terms of service. Cancel my account, make me reset to factory new condition, maybe charge me a twenty five or fifty dollar service fee to do the reset, OK those all sound reasonable. But bricking my expensive phone? Seems a little harsh don’t you think?

So let’s see here, on one hand we have a company that is rewarding it’s early adopter customers by making sure their devices will have all the latest greatest features at no extra charge.

On the other hand we have a company that requires you to use your phone with only they software they approve of. Fall outside that use, and your punishment: to have the sexiest, sleekest brick around.

Hmm, now who is sounding evil?

The Speaker Dinner

As you have probably guessed by now, I’ve grown to really enjoy speaking at and attending code camps and user group meetings. There are a lot of benefits to being a speaker, but to me one of the biggest is the Speaker Dinner the night before the code camp. In case you don’t know it, there’s usually a dinner the night before most code camps. Admission to the dinner is speaking at code camp the next day.

I got home just a little while ago from the dinner prior to tomorrows (hmm, well today’s since it’s after midnight now) Alabama Code Camp. I got to hang out with some really ultra smart folks tonight, and talk about a wide variety of topics. I know there were at least 4 MVPs there, possibly more since I didn’t know everyone.

Dinner was a mini-education in itself, as I picked up on several new topics. I also got to laugh and hear some great stories. If you have ever thought about speaking at a code camp I would highly encourage you. The rewards are well worth the admission cost!

Follow

Get every new post delivered to your Inbox.

Join 100 other followers