Scott Hanselman

The Nerd Parent's Guide: When and how to introduce your kids to Star Wars

May 7, '12 Comments [50] Posted in Daddy
Sponsored By

Tiny Baby Boba FettLet me start by saying I'm not a big Star Wars guy. I enjoyed the films when I was growing up (I think I saw the original  (Ep. 4 ANH) in 1980 along with Empire Strikes Back in 1980) but I haven't thought much about them since. That said, I appreciate the films and I like movies in general. Watching movies with my kids (usually Pixar movies) have brought us a lot of shared joy as a family. However, Star Wars are fun and classics and we wanted to share them with our kids in a way that worked for everyone given their age and our parenting style.

TL;DR Version

  • Show Star Wars to your kids when you think they can discuss and analyze the themes appropriate. That might be 5, it might be 10. They're your kids.
    • Don't forget you can skip parts.
  • Show them the movie in 20 minute segments and make it a serial adventure rather than a movie.
  • Show the films in "Machete Order" which is episodes 4,5,2,3,6. This maintains surprises while ending on a high note.
    • Yes, episode 1 is hacked out and not shown. You can show it at the end of it all along with the Clone Wars cartoons and bill them as supplemental material.
  • Consider Harmy's Despecialized Version fan edit or the Star Wars: Revisited fan edit.
  • Make the films an event with crafts and discussion of mythology rather than just dumping in on their little brains.

You Stayed to Read it All Version

We are extremely conscious of our two boys' "screen time." They don't use the computer or play video games and they can watch TV on "show days." Show Days for us are Tues, Friday and Saturday when they can watch 20-30 minutes of TV. That adds up to about 90 minutes of screen time a week. We'd rather they dance around, dig in the dirt and play, which they do very well.

The boys watch Wild Kratts, Super Hero Squad and a few other kids' shows. They are 4 and a half and 6 and a half. Lately the topic - actually more the mythology - of Star Wars has been coming up.

"Daddy, who is Dark Vader? Is he a bad guy? I heard he has a Light Saver."

As both boys are in school it was inevitable that this topic would come up amongst the other kids. We're not ones to give in to peer pressure but in this case the boys seemed extremely interested in the characters, and as an extension the ideas around space, distance, planets, travel and the meta-topic of good vs. evil.

The wife and I were aiming to wait until they were 6 and 8 to watch Star Wars but since the interest was so high we decided to give it a try in a very structured way. Everyone's kids are different. One 10 year old might not like the aliens in the cantina while other person's 5 year old would be fascinated by how they constructed the masks and not believe they were real aliens. Our kids tend to be "makers," and as such are always constructing and deconstructing. They also understand the idea of actors and general movie making having made small films on an iPod touch so here's how we decided as parents to introduce our two boys to Star Wars.

Disclaimer: Everyone's kids are different. I'm not advocating, preaching or suggesting that you should do any of this. If some of it works for you, great, if not or if you disagree, also great. This system worked for us.

Structure and Time

First, I watched the first 1977 Star Wars to refresh my memory about the themes and general tone. We didn't want to put two hyper little people in front of effectively what is an intense two hour chase scene that they wouldn't understand. We also wanted to see if there were some scenes that we wanted to avoid all together.

My favorite site for getting into extreme detail on a film's content is Kids In Mind. I use this site before the boys and I watch anything. In this case Kids In Mind has this to say about Star Wars.

Smoking skeletons are seen; many people are threatened with laser guns and sometimes are actually shot (resulting in some deaths but no blood). An arm is cut off and some blood is seen. A few scuffles and attacks, and a couple of explosions. A man is strangled and a man is lifted by his throat and then his limp body is tossed across a room.

This is an excellent and short summary of the three scenes that concerned me. The arm cut off in the cantina, the skeleton at Luke's house and Vader force-choking people, as well as general lasers and chaos.

We decided that we'd show them the first movie (ep. 4 ANH) in 20 minute sittings. We literally sat down, watched 20 minutes at a time then stopped it. Star Wars IV: A New Hope is 125 minutes long, so about 5 or 6 sittings. It took us two weeks to watch the movie, which was just about right.

We also skipped over the severed arm in the Cantina as it's the only blood shown in the first movie and kinds of stands out in a gross way. We also implied that Luke's Aunt and Uncle ran away and the smoking husk in front of the house was a droid that was caught in the fire. These two small omissions cut out 90% of the violence that we were uncomfortable with. The rest we deal with by showing the movie in chunks as well as using each segment as an opportunity to talk about interesting topics as a family.

Discussion

The benefits of splitting the movie up into two weeks of six segments are many. The most significant benefit given that the audience is children was time for discussion and "cool down." Little boys LOVE action. My boys were concocting crazy scenarios with good guys, bad guys and explosions long before they even knew what a television was. They are hard-wired for action,  but again, two hours of action is too much for our little people. Twenty minute segments gave the boys one effectively interesting action segment and a bunch of expository dialogue.

Each time something interesting happened or something intense happened we would pause the show and say something like "wow, why did Han do that?" This would often turn into a 10 minute discussion between us - and more often between the two boys - debating the merits of one action versus another. It also allowed the kids to connect to the story more than the action. If we got the sense that they weren't understanding the movie we would just stop it and try again in 6-12 months. No joke. There's no point showing a movie to a kid if they aren't going to understand the themes.

In a fast moving films it can be hard to keep track of what's happening and who is who. Stopping every 10 minutes or so for a discussion or a
feigned confused parent question like "Wait, why is that guy mad at that guy? Who are they?" gives the kids a chance to absorb the content as well as good practice in explaining complex plots to adults.

Watching only 20 minutes at a time also took what would have been an intense two hour explosion and turned it into a fun weekly episodic serial with the excitement of "what will happen next?" propelling the boys through the week. More discussions and analysis continued and continues even now. The boys haven talked about good and bad, when to fight and when not to.

Version

There's lots of versions of Star Wars as George Lucas continues to tinker with it. The Special Editions that came out in 1997 included lots of computer graphics tweaks and changes. He continued to make changes in 2004 when Star Wars was released on DVD and again when it was released on 2011 on Blu-Ray.

There have been a number of "fan edits" of Star Wars but one of the highest quality and certainly the most popular is "Harmy's Despecialized Edition." This edition takes nine different sources and merges them together into an excellent fan preservation. The Despecialized HD version is a 720 AVCHD dual-sided DVD9.

Nerd on: You likely know that a DVD can hold about 5 gigs and shows films at 480p (480 horizontal lines of resolution) and a Blu-Ray can hold 25 gigs (or 50 gigs when dual later) and show films at 1080p. There was another interstitial format that is used on dual layer DVDs that can hold almost 9 gigs. The AVCHD (Advanced Video Coding High Definition) format is similar to Blu-Rays (it's a derivative, effectively) and you'll find it in many Camcorders. It originally supported 720p.

You can get the Star Wars Despecialized Edition HD in an AVCHD format for a Dual Layer DVD9. This is a lovely 720p fan edit that is playable on many Blu-Ray Players.  I own the original DVD boxed set as well. You are required to own a legitimate copy in order to watch a fan edit and you must never pay for a fan edit. There's box art and labels you can download to put together your own copies for the home. There's a number of nice YouTube comparison clips showing your choices so you can decide if you care or not about showing a fan-edit. We watched the Harmy Edit and at least *I* appreciated the effort and attention to details.

Movie Ordering

We all watched the movies in the order they came out which is 4,5,6 then 1,2,3. When you're showing the Star Wars movies to a new person you have the advantage to show them the movies in whatever order you like. The most popular and well respected alternate order is Machete Order from Rod Hilton. He recommends showing the movies in this order:

  • 4 - Star Wars: A New Hope
  • 5 - Empire Strikes Back
  • 2 - Attack of The Clones
  • 3 - Revenge of the Sith
  • 6 - Return of the Jedi

You'll notice there is no film #1. That's for a number of reasons, some passionate but others logical and well-reasoned.

Passion:

"Episode I is a failure on every possible level. The acting, writing, directing, and special effects are all atrocious, and the movie is just plain boring. Luckily, George Lucas has done everyone a favor by making the content of Episode I completely irrelevant to the rest of the series. Seriously, think about it for a minute. Name as many things as you can that happen in Episode I and actually help flesh out the story in any subsequent episode."

Logic:

Every character established in Episode I is either killed or removed before it ends (Darth Maul, Qui-Gon, Chancellor Valorum), unimportant (Nute Gunray, Watto), or established better in a later episode (Mace Windu, Darth Sidious). Does it ever matter that Palpatine had an apprentice before Count Dooku? Nope, Darth Maul is killed by the end of Episode I and never referenced again. You may as well just start with the assumption that Dooku was the only apprentice. Does it ever matter that Obi-Wan was being trained by Qui-Gon? Nope, Obi-Wan is well into training Anakin at the start of Episode II, Qui-Gon is completely irrelevant.

Search your feelings, you know it to be true! Episode I doesn’t matter at all.

He goes on to explain not only that Episode #1 doesn't matter but also that the really exciting surprises like who Luke's father is and others are maintained with this order.

Given that our kids are younger, we will stop the series for a while after Attack of the Clones. Episode 3, Revenge of the Sith is super dark and the most physically violent of the films. I want to wait until I feel the boys are ready to internalize that much intensity. I suspect that will be around 10 years or, perhaps older. After that we'll pick up where we left off. You should decide on your own but it's useful to be aware or reminded how dark episode 3 really is.

Conclusion

We've introduced our little people to the Star Wars mythology in a slow and structured way. There's been more talking and playing in the backyard so far than actual movie watching. There's been discussions of movie making and how to make an alien mask. There's been cardboard light sabers and grappling hooks made of straws. There's been hours of discussion about character motivations, why good guys do good stuff, why bad guys do bad stuff and why Han Solo likes money and whether or not we should "mess with Yoda."

Overall, my wife and I are happy with the results. I hope this post helps you and your kids jump into Star Wars. I'm off now to deal with the burning question in our household this week: "Who took Chewbacca's pants?"

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web

Introducing Workspace Reloader - A Visual Studio AddIn to save your open files across project reloads

May 2, '12 Comments [27] Posted in Open Source | VS2010
Sponsored By

Works on my machineA while back my buddy Sam Saffron (from Stack Overflow and Mini Profiler) complained to me on Skype that he was finding it very irritating that every time he updated his project outside of Visual Studio he would be prompted to "Reload Project" and would lose all his open files because Visual Studio would close them.

This apparently is becoming kind of an issue at Stack Overflow. Since they use distributed source control and often have a dozen or more folks all coding inside the same project they are integrating all the time. They'll be deep into something, update their project to test it and all their open windows close.

It's a weird Visual Studio behavior that I've never understood. Visual Studio saves all your open files and window positions when you close the IDE and restores them when you open your solution. But when you open a project then right click and "Unload Project" you'll lose all your windows. I've reported it as a bug and it's also been voted up at User Voice, visited as a Question at StackOverflow, and a few folks have tweeted about it (The SO guys with their thumbs on the scale, no doubt) and been bugging some folks but then I got the idea to just fix it myself. It'd be a good chance to write my first Visual Studio Add-In, see if this is even possible, and fix an irritant at the same time.

DOWNLOAD: Workspace Reloader Visual Studio Add-in - "This package will reload the code files you had open when your project file was modified and unloaded then reloaded"

Warranty: To be clear this is the smallest of extensions. It only listens to two events and it's only 12k so you have no reason that I know of to be afraid of it. Plus, it works on my machine so you've got that going for you.

Creating a Visual Studio Extension

Developing Visual Studio Extensions requires some patience. It's gotten a lot better with Visual Studio 2010 but back in the 2003-2005 days it was really hairy. There's a number of different kinds of things you can extend. You can add menus, add tool bars, commands, new templates, new kinds of designers and visualizers, as well as use just the shell to create your own IDE.

I wanted to create an add-in with Zero UI. I had no need for buttons or menus, I just wanted to listen to events and act on them. I downloaded the Visual Studio 2010 SDK after reading this blog on extending Visual Studio 2010. Make sure you get the right version. I have Visual Studio 2010 SP1 so I needed the updated Visual Studio 2010 SP1 SDK.

File | New Project | Visual Studio Package

I made a new Visual Studio Package. This builds into a VSIX (which is just a ZIP file - isn't everything?). A VSIX has a manifest (which his just XML - isn't everything?) that you can edit in a GUI or as a text file.

I want my VSIX package to work on Visual Studio 11 Beta as well as Visual Studio 2010  so I added to the SupportedProducts node like this. VSIXs other than templates aren't supported in Express (I keep pushing, though):

<SupportedProducts>
<VisualStudio Version="10.0">
<Edition>Ultimate</Edition>
<Edition>Premium</Edition>
<Edition>Pro</Edition>
</VisualStudio>
<VisualStudio Version="11.0">
<Edition>Ultimate</Edition>
<Edition>Premium</Edition>
<Edition>Pro</Edition>
</VisualStudio>
</SupportedProducts>

I also setup the name, version and description in this file. 

I need to decide when my package is going to get loaded. You can add one or more ProvideAutoLoad attributes to a Package class from the VSConstants class. A number of blogs posts say you need to hard code a GUID like this, but they are mistaken. There are constants available.

[ProvideAutoLoad("{ADFC4E64-0397-11D1-9F4E-00A0C911004F}")]

I can have my package automatically load in situations like these:

  • NoSolution   
  • SolutionExists
  • SolutionHasMultipleProjects   
  • SolutionHasSingleProject
  • SolutionBuilding
  • SolutionExistsAndNotBuildingAndNotDebugging
  • SolutionOrProjectUpgrading
  • FullScreenMode

For my package, I need it loaded whenever a "Solution Exists," so I'll use this Constant (in lieu of a hard coded GUID):

[ProvideAutoLoad(VSConstants.UICONTEXT.SolutionExists_string)]

Next, I wanted to listen to events from the Solution like the unloading and loading of Projects. I started with the IVsSolutionsEvents interface that includes OnBefore, OnAfter and OnQuery for basically everything. Elisha has a simple listener wrapper as an answer on StackOverflow that I modified.

The SolutionEventsListener uses the very useful Package.GetGlobalService to get hold of the solution.

IVsSolution solution = Package.GetGlobalService(typeof(SVsSolution)) as IVsSolution;
if (solution != null)
{
solution.AdviseSolutionEvents(this, out solutionEventsCookie);
}

We then sign up to hear about things that might happen to the Solution using the IVsSolutionEvents interfaces and making them look like friendly events.

public event Action OnAfterOpenProject;
public event Action OnQueryUnloadProject;

int IVsSolutionEvents.OnAfterOpenProject(IVsHierarchy pHierarchy, int fAdded)
{
OnAfterOpenProject();
return VSConstants.S_OK;
}

int IVsSolutionEvents.OnQueryUnloadProject(IVsHierarchy pRealHierarchy, ref int pfCancel)
{
OnQueryUnloadProject();
return VSConstants.S_OK;
}

I want to hear about things just before Unload happens and then act on them After projects Open. I'll save the Document Windows. There's an interface that manages Documents and Windows for the Shell called, confusingly enough IVsUIShellDocumentWindowMgr

I save the windows just before the unload and reopen them just after the project opens. Unfortunately these are COM interfaces so I had to pass in not an IStream but an OLE.IStream so while the ReopenDocumentWindows is easy below...

listener.OnQueryUnloadProject += () =>
{
comStream = SaveDocumentWindowPositions(winmgr);
};
listener.OnAfterOpenProject += () => {
int hr = winmgr.ReopenDocumentWindows(comStream);
comStream = null;
};

The SaveDocumentWindowPositions is more complex, but basically "make a memory stream, save the documents, and seek back to the beginning of the stream."

private IStream SaveDocumentWindowPositions(IVsUIShellDocumentWindowMgr windowsMgr)
{
if (windowsMgr == null)
{
return null;
}
IStream stream;
NativeMethods.CreateStreamOnHGlobal(IntPtr.Zero, true, out stream);
if (stream == null)
{
return null;
}
int hr = windowsMgr.SaveDocumentWindowPositions(0, stream);
if (hr != VSConstants.S_OK)
{
return null;
}

// Move to the beginning of the stream with all this COM fake number crap
LARGE_INTEGER l = new LARGE_INTEGER();
ULARGE_INTEGER[] ul = new ULARGE_INTEGER[1];
ul[0] = new ULARGE_INTEGER();
l.QuadPart = 0;
//Seek to the beginning of the stream
stream.Seek(l, 0, ul);
return stream;
}

If this does it's job you'll never know it's there. You can test it by installing Workspace Reloader, opening a project and opening a few code files. Now, edit the CSProj as a text file (maybe add a space somewhere) and save it. Visual Studio should prompt you to Reload the Project. Workspace Reloader should keep your files and windows open.

I hope this helps a few people. The source is here.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web

Back to Basics: Moving beyond for, if and switch

April 26, '12 Comments [72] Posted in Back to Basics
Sponsored By

I visit a lot of customers and look at a lot of code. I also worked with a number of large production code bases in my previous jobs and I see a lot of ifs, fors and switches. I see loops inside of loops with ifs inside them, all doing various transformations of data from one form to another. I see strings getting parsed to pull bits of data out in ways that are easy to say in English but take 100 lines to say in code.

Should they? When we are just getting started programming we learn about if first, then for, then the much abused switch statement.

I saw this little snippet on Miguel's blog a few weeks ago:

var biggerThan10 = new List;
for (int i = 0; i < array.Length; i++){
if (array [i] > 10)
biggerThan10.Add (array[i]);
}

It's straightforward. Take an array of ints and make a new list with those that are larger than 10. We've all see code like this a million times. Here's the same thing in a few other languages.

C#

var a = from x in array where x > 10 select x; 
var b = array.Where(x => x > 10);

Ruby

a = array.select{|x| x >10}

JavaScript

a = array.filter(function(x){return x > 10});

I'd much rather write these one line operations than the loop and if above. I still see this out in the world, so perhaps people haven't seen enough examples. I asked friends on Twitter to submit their examples. Thank you Twitter friends!

Here's a few nice examples. Iron Shay has some nice LINQ examples on his blog. Please do share yours in the comments. Be sure to use <pre> tags.

NOTE: This is NOT about "shoving stuff into one line" but rather looking at solutions that are equally as readable but also simpler, terser, and less error prone than loops of loops.


def calculate_primes(n):
no_primes = []
primes = []

for i in range(2, 8):
for j in range(i*2, n, i):
no_primes.append(j)

for x in range(2, n):
if x not in no_primes:
primes.append(x)

return primes


calculate_primes(500)


# Can be like this instead!

(lambda n: [x for x in range(2, n) if x not in [j for i in range(2, 8) for j in range(i*2, n, i)]])(500)

From Aaron Bassett


foreach (var i in categories) {
foreach (var x in GetAllChildCategories(i.Id)) {
yield return x;
}
}

//Can be...

return categories.SelectMany(i => this.GetAllChildCategoriesIds(i.Id));

From James Hull


var inputNumbersInString = Console.ReadLine();
var inputNumbersStringArray = inputNumbersInString.Split(' ');
var inputNumbers = new List<int>();

for (int i = 0; i < inputNumbersStringArray.Length; ++i) {
inputNumbers.Add(int.Parse(inputNumbersStringArray[i]));
}

int maxNumber = inputNumbers[0];

for (int i = 1; i < inputNumbers.Count; ++i)
if (inputNumbers[i] > maxNumber)
maxNumber = inputNumbers[i];

Console.WriteLine(maxNumber);

//Or rather...

Console.WriteLine(Console.ReadLine().Split(' ').Select(t => int.Parse(t)).ToList().Max());

From Amit Saraswat


// create a poker deck as a list of two characters strings: 
// rank, suite

char[] figures = "23456789TJQKA".ToCharArray();
char[] suites = "SHDC".ToCharArray();
List<string> deck = new List<string>();

foreach (var figure in figures) {
foreach (var suite in suites) {
deck.Add(string.Format("{0}{1}", figure, suite));
}
}

//Or, neatly
var cards = from r in "23456789TJQKA" from s in "SHDC" select "" + r + s;

From Jack Nova


bool include = false;
if (op == Operator.And) {
bool current = true;
foreach (var item in Items) {
current = current & item.Process();
}
include = current;
}
else {
bool current = false;
foreach (var item in Items) {
current = current | item.Process();
}
include = current;
}
return include;

//Or this lovely Aggregate

return op == Operator.And ?
Items.Aggregate(true, (current, item) => current & item.Process()) :
Items.Aggregate(false, (current, item) => current | item.Process());

From Kevin Meiresonne


sbyte[] sByteArray = new sbyte[100];
byte[] uByteArray = new byte[sByteArray.Length];

for (int i = 0; i < sByteArray.Length; i++) {
uByteArray[i] = (byte)sByteArray[i];
}

//Or, instead of the loop above
byte[] uByteArray1 = Array.ConvertAll(sByteArray, x => (byte)x);

From Fahad Mustafa


Scott: I have to say here that I prefer the first option. ;)

// This is the "classic" solution to the FizzBuzz problem.
for (int i = 1; i <= 100; i++) {
if (i % 3 == 0 && i % 5 == 0) {
Console.WriteLine("FizzBuzz");
}
else if (i % 3 == 0) {
Console.WriteLine("Fizz");
}
else if (i % 5 == 0) {
Console.WriteLine("Buzz");
}
else {
Console.WriteLine(i.ToString());
}
}

// One line
Enumerable.Range(1, 100).ToList().ForEach(n => Console.WriteLine((n % 3 == 0) ? (n % 5 == 0) ? "FizzBuzz" : "Fizz" : (n % 5 == 0) ? "Buzz" : n.ToString()));

From Craig Phillips


A good one...I'm surprised more people don't use this.

var temp = String.Empty;
foreach (var entry in myStringList) {
    if (String.IsNullOrEmpty(temp)) {
        temp = entry;
    }
    else {
        entry += ", " + entry;
    }
}

//becomes

var temp = String.Join(", ", myStringList)

From Holger Adam


A class with properties in one line of F#. That'd be a dozen or more lines of C#.

type Person = { Name:string; Age:int }

From Phillip Trelford


/// Input is a string with numbers : 10+20+30+40
/// Output is integer with required sum (100)
string input = "10+20+30+40";
var result = Regex.Split(input, @"\D+").Select(t => int.Parse(t)).Sum();
Console.WriteLine("Result is {0}" ,result);

From Srinivas Iyengar


There are a million things available to the programmer beyond the first three keywords we learn. What are your favorite patterns (doesn't matter what language) that have helped you break away from the basics and move to the next level?


Sponsor: Big thanks to the folks at DevExpress for sponsoring this week's feed. Check out a free trial of CodeRush, one of my favorite products! Introducing CodeRush by DevExpress. The Visual Studio add-in that helps you create more reliable applications. Tools to build & maintain your code without getting in the way of your IDE.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web

A world of pretty cloud drive icons - SkyDrive, Google Drive, Dropbox, BitBucket and GitHub

April 24, '12 Comments [16] Posted in Musings
Sponsored By

Today Google Drive and Windows SkyDrive came out with clients for Windows and Mac. There's also SkyDrive apps for Windows Phone, iPhone, or iPad and OneNote apps that sync to SkyDrive for Windows Phone, Android,iPhone, or iPad.

I'm a paying DropBox customer myself with over 60 gigs in there. I also use BitBucket and GitHub for source code.

I also like tidy and pretty icons, for folders, programs and external drives. I made custom icons for the Visual Studio Command prompt as well as Visual Studio in PowerShell.

I put all these new cloud folders as well as my GitHub and BitBucket folders (these aren't custom apps, just folders where I keep my source repositories) in my favorites in Windows Explorer.

I made custom .ICO icons for GitHub and BitBucket from high-res PNGs. Just right click on a Folder, click Properties, then Shortcut Change Icon to select your custom icon.

Pretty Icons for all my Cloud Services

Select your icon from your folder properties as shown in this screenshot:

Checking a folder's icon

That DropBox icon isn't fitting in with my whole "Cloud Folder" aesthetic. I was going to make a custom DropBox icon by combining their icon along with the standard Windows Explorer folder icon but I double-checked the Dropbox.exe first. You never know if the designer may have left some optional or alternative views of an icon in the EXE. Plus we know that the DropBox folks pay attention to details.

I can see in %APPDATA%\Dropbox\bin\Dropbox.exe that there are other views of the main DropBox icon.

Ah, there are hidden DropBox icons!

Ah, that's much nicer!

Changing the DropBox icon to a folder

I put my custom icons up on SkyDrive.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web

Create a great mobile experience for your website today. Please.

April 23, '12 Comments [32] Posted in ASP.NET | HTML5 | Mobile
Sponsored By

People are fascinating with making mobile web sites. It's amazing that we're not impressed with the fact we carry tiny supercomputers in our pockets but we're amazed when a website looks decent on our phones.

There's a few directions you can go when going mobile for your site, and the key is finding balance. You can:

  • Do nothing. Your site will probably work on a mobile device but each day it will look worse and worse to a discerning public with increasing expectations.
  • Use Adaptive/Responsive Design. This is my favorite option. If your site is read-mostly (rather than a data-entry application) you can get a great experience on all devices by adaptively rendering your site based on screen-size. If you're focused on performance you can add a server-side component and resize image for mobile as well. Visit http://mediaqueri.es for inspiration.
  • Use a mobile framework. There's lots great frameworks like Sencha, Kendo, jQuery Mobile and others. These frameworks can get you near-native looking applications using HTML5 techniques.
  • Write a native app. For the time-being while it's cool to try to get native experiences using non-native tools, it's hard. The best native experience on a mobile device will remain a native-built application. This requires the most work with arguably the best experience. However, you CAN get 90-95% of the experience with less than 90% of the effort if you use some of these other techniques. Plus, you'll anger fewer users by not forcing them to download a crappy mobile app by just making a lovely mobile website.

image

If you take a moment and visit my site (this site) on your phone, or just resize the browser to a smaller size, you'll see that this blog is using a "responsive design" by designer Jeremy Kratz. The blog will change it's look based on if it's on a large monitor, an iPad or medium tablet, or a narrow phone. Watch the navigation bar turn into a drop down as the browser gets really narrow, for example.

My site's responsive design, as featured on the MediaQueri.es site

This was a relatively small - although thoughtful - change that instantly made my blog more accessible to the 8% of people who visit my site from a mobile device.

For larger data-focused sites, or sites that are "almost applications" you will want to consider a custom mobile version of your site. This is often done with the help of a mobile framework as mentioned above. I'll use jQuery Mobile as an example here. Let's say I have a conference browser application that looks like this on the desktop. I can navigate by date, speaker, tag, as well as view session details.

My site looks lousy on an iPhone

If I look at this same page on a mobile browser or something like the Electric Plum Mobile Simulator, it looks like crap.

Electric Mobile Simulator

I could use a mobile custom stylesheet just for phones, or I could use a CSS3 media query to make my existing stylesheet more mobile friendly...for example:

@media only screen and (max-width: 1024px) and (max-height: 768px)
{
/* do something, hide something, move something */
}

Or I could use a mobile framework along with a display mode in ASP.NET MVC to render a different view while still using the same controller logic. For example, I could have a _Layout.cshtml (that's like a "master page") and then a _Layout.Mobile.cshtml for mobile devices.

A Views folder with optional *.mobile.cshtml files for each mobile view

Mobile is just a included "display mode." You can create your own however you like. Here's one for Windows Phone. You could theoretically have ones like "tablet" or "nokia." I think you should have as few as you can get away with. Try to avoid complexity. This is just an example.

DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode("WP7") {
ContextCondition = ctx => ctx.GetOverriddenUserAgent().Contains("Windows Phone OS")
});

That "WP7" string is what you put in place of * in filename.*.cshtml. So that's _Layout.WP7.cshtml, or Index.WP7.cshtml, etc. For my example I'll just make a _Layout.Mobile.cshtml that will automatically be used when most mobile browsers like Mobile Safari, Blackberry or Windows Phone hit my new site.

Here is a new _Layout.Mobile.cshtml as a starting point for my conference browser mobile site. Remember that you can just File | New Project in Visual Studio with ASP.NET MVC 4 and select Mobile Site to get started on your own.

<!DOCTYPE html> 
<html>
<head>
<meta charset="utf-8" />
<title>@ViewBag.Title</title>
<meta name="viewport" content="width=device-width" />
<link rel="stylesheet" href="@Url.Content("~/Content/jquery.mobile-1.0.min.css")" />
<link rel="stylesheet" href="@Url.Content("~/Content/Site.Mobile.css")" />
<script type="text/javascript" src="@Url.Content("~/Scripts/jquery-1.6.4.min.js")"></script>
<script type="text/javascript">
$(document).bind("mobileinit", function() {
// jQuery Mobile's Ajax navigation does not work in all cases (e.g.,
// when navigating from a mobile to a non-mobile page), especially when going back, hence disabling it.
$.mobile.ajaxEnabled = false;
});
</script>
<script type="text/javascript" src="@Url.Content("~/Scripts/jquery.mobile-1.0.min.js")"></script>
</head>
<body>

<div data-role="page" data-theme="a">
@Html.Partial("_ViewSwitcher")

<div data-role="header">
<h1>@ViewBag.Title</h1>
</div>

<div data-role="content">
@RenderSection("featured", false)
@RenderBody()
</div>

</div>
</body>
</html>

Now that I have a custom _Layout.mobile.cshtml for mobile, THIS file will be used when I hit the site on a mobile device rather than the main _Layout.cshtml.

OK, here my application is using the mobile layout, but the existing session HTML which looks, again, like crap. I'm using a mobile layout with a desktop view.

Electric Mobile Simulator Sessions

The desktop view for a session uses a table (and that's OK you tableless-CSS people because it's a table of information):

<table>
<thead>
<tr><th>Title</th><th>Speaker(s)</th><th>Date</th><th>Room</th><th>Tags</th></tr>
</thead>
<tbody>
@foreach(var session in Model) {
<tr>
<td>@Html.ActionLink(session.Title, "Session", new { session.Code })</td>
<td>@Html.Partial("_SpeakersLinks", session)</td>
<td>@session.DateText</td>
<td>@session.Room</td>
<td>@Html.Partial("_TagsLinks", session)</td>
</tr>
}
</tbody>
</table>

But I need a cleaner mobile layout that respects a smaller screen size. I'll copy my SessionsTable.cshtml and make a SessionsTable.Mobile.cshtml with contents like this:

@using ConferenceSessionsBrowserMvc4.Models
@model IEnumerable<Session>

<h2>@ViewBag.Title</h2>

<ul data-role="listview">
@foreach(var session in Model) {
<li>
<a href="@Url.Action("Session", new { session.Code })">
<h3>@session.Title</h3>
<p><strong>@string.Join(", ", session.Speakers)</strong></p>
<p>@session.DateText</p>
</a>
</li>
}
</ul>

There are a few things to note in this HTML. First, I like that it's not littered with CSS that describes the look and feel of the site, but rather it uses the data- attributes from HTML5 to express the "role" of an element. The UL uses data-role="listview" that tells me it's a listview but doesn't dictate what it looks like.

Within the UL I've got some LIs that use standard semantic tags like A, H3, and P along with STRONG and along with the default theme it looks nice on mobile.

A nice mobile view using jQuery Mobile

ASIDE: See the the "Displaying mobile view" link at the top of the image there? With ASP.NET MVC 4 you can make a View Switcher easily with a partial View like this:

@if (Request.Browser.IsMobileDevice && Request.HttpMethod == "GET")
{
<div class="view-switcher ui-bar-a">
@if (ViewContext.HttpContext.GetOverriddenBrowser().IsMobileDevice)
{
@: Displaying mobile view
@Html.ActionLink("Desktop view", "SwitchView", "ViewSwitcher", new { mobile = false, returnUrl = Request.Url.PathAndQuery }, new { rel = "external" })
}
else
{
@: Displaying desktop view
@Html.ActionLink("Mobile view", "SwitchView", "ViewSwitcher", new { mobile = true, returnUrl = Request.Url.PathAndQuery }, new { rel = "external" })
}
</div>
}

And a ViewSwitcherController to change the "overridden" browser when you click the link. This is all in the jQuery.Mobile.MVC NuGet package that we will update for the final release.

public class ViewSwitcherController : Controller
{
public RedirectResult SwitchView(bool mobile, string returnUrl) {
if (Request.Browser.IsMobileDevice == mobile)
HttpContext.ClearOverriddenBrowser();
else
HttpContext.SetOverriddenBrowser(mobile ? BrowserOverride.Mobile : BrowserOverride.Desktop);

return Redirect(returnUrl);
}
}
OK, back to the Dates view. I can apply the same data- jQuery Mobile techniques to other screens, like the list of dates. I've got a data-role="listview" and a data-role="list-divider" as the dates change.
@model IEnumerable<DateTime>

@{
ViewBag.Title = "All dates";
DateTime lastDay = default(DateTime);
}
<ul data-role="listview">
@foreach(var date in Model) {
if (date.Date != lastDay) {
lastDay = date.Date;
<li data-role="list-divider">@date.Date.ToString("ddd, MMM dd")</li>
}
<li>@Html.ActionLink(date.ToString("h:mm tt"), "SessionsByDate", new { date })</li>
}
</ul>

And get a nice result like this:

jQuery mobile applied to a ListView of dates

You can even get cool data filtering "as you type" features for jQuery Mobile list views by using data-filter="true" on a listview.

data-filter=true in jQuery Mobile

Because these templates are all mobile specific they don't affect the way the site looks on the desktop. Also because these are simply new views for existing URLs and Controllers, I don't need write any new business logic.

It is worth reminding you that it won't always be the case that an application will have its controllers and URLs map neatly such that one desktop view = one mobile view. Sometimes you may need to split up a complex single page desktop interaction into multiple mobile views. This conference application ended up with six views for desktop and six for mobile (Index, Dates, tags, Session(Detail), SessionsTable, and Speakers.) It's conceivable if the application included data entry that I would need to break up some views as well as create some custom methods just for mobile, although with some planning around User Experience you can usually keep this to a minimum.

If the default browser sniffing that decides what's mobile and what's not isn't enough for your project, consider using a 3rd party database of mobile devices like the one provided by 51degrees.mobi. Their 51degrees.mobi mobile framework will help your site adapt to support all mobile devices as they include a database of devices as well as their capabilities. They can even compress images and improve low-bandwidth performance.

They have a NuGet package I can install like this:

51degrees.mobile mobile framework for ASP.NET

51Degrees and libraries like it will add new capabilities to the Request.Browser object. These are just a few examples, there's dozens.

Screen Width: <% =Request.Browser.ScreenPixelsWidth %></li>
Screen Height: <% =Request.Browser.ScreenPixelsHeight %></li>
LayoutEngine: <% =Request.Browser["LayoutEngine"] %></li>
AnimationTiming: <% =Request.Browser["AnimationTiming"] %></li>
BlobBuilder: <% =Request.Browser["BlobBuilder"] %></li>
CssBackground: <% =Request.Browser["CssBackground"] %></li>
CssBorderImage: <% =Request.Browser["CssBorderImage"] %></li>
CssCanvas: <% =Request.Browser["CssCanvas"] %></li>
CssColor: <% =Request.Browser["CssColor"] %></li>
CssColumn: <% =Request.Browser["CssColumn"] %></li>
CssFlexbox: <% =Request.Browser["CssFlexbox"] %></li>
CssFont: <% =Request.Browser["CssFont"] %></li>
CssMediaQueries: <% =Request.Browser["CssMediaQueries"] %></li>

You can use this information on the server side to augment these other techniques. For example, if the requesting device supports CssMediaQueries, great, you should use them, but it not, perhaps you need to fall back to another technique. If you know the screen-size on the server and it's below a certain size you can resize the image before you send it.

Thanks to Jon Galloway, Damian Edwards and Erik Porter for their brainstorming and Steve Sanderson for the sample application.


Sponsor: I want to thank my friends at DevExpress for sponsoring this week's feed. Do take a moment and check out a free trial of CodeRush, one of my favorite products!  Introducing CodeRush by DevExpress. The Visual Studio add-in that helps you create more reliable applications. Tools to build & maintain your code without getting in the way of your IDE.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.