Interrupting your user

8 10 2010

When designing user interfaces a lot of thought needs to go into when you are going to interrupt a user.  I am going to walk you through an example of what not to do.  My example deals with messing around with CSVs in excel.  I recently have been doing a lot of data loading via CSV and found myself getting consistently interupted by Excel when trying to save.  Pretty simple concept, once I have changed a document I should be able to click save ( yup thats it, nothing else.. pretty simple request huh?). 

 

Well take a walk with me…

Step 1.  Right click your desktop->new->Text Document

Step 2.  Change the name to Test.csv

Step 3.  Double Click Test.csv and it will open in Excel.

Now you will have an empty spreadsheet.  I am going to enter(| indicate next cell) : This | Is | A | Bad | UI | Interaction

 

Excel_CSV_PreSave

Step 4.  Click Save.

Kaboom.  Greeted with a Sound AND Messagebox:

Excel_CSV_ClickSave

So I dont understand?  I just entered text why is it complaining about tabs?  Oh well I want to Save my csv format… so i click yes.

Step 5. Click Yes

Step 6.  Close

I then go to close the workbook and am greeted with:

Excel_CSV_NoChanges_Close

Hmm.. I didn’t change anything so NO!

Step 7. Click No.

So lets open it back up and see what’s going on.

Step 8. Double click Test.csv

Excel_CSV_NoChanges_Close_ClickedNo

Really?  I guess that first warning was justified. Apparently, when I clicked yes to keep the format it meant, collapse into one cell and insert ? character…hmm.

Note: If you are on Windows 7 you might just see ThisIsABadUIInteraction either way it collapsed the text into one cell

Lets get out of here

Step 9.  Close

Excel_CSV_NoChanges_Close

GAH!  Again!

Step 10.  Click No

So now I am just going to take over, I am going to open up the raw text and fix the CSV myself. 

Step 11.  Edit text and place “,” between each word and remove the TABs.  Save and Close.

Excel_CSV_Fixing_WithVim

Step 12. Open this thing back up(in Excel).

Ah.. much better.

Step 13.  Add a new row

EXcel_Csv_Take2

Step 14.  Save (yes)

Here comes that warning again… click yes.

Excel_CSV_Changes_Y_SaveAs_Exist_Y_NonCompatx2 (1)

Step 15.  Close

Nooooo not again.  I don’t think I changed anything….

Excel_CSV_NoChanges_Close

Step 16.  Lets click yes for the fun of it…

Woah…. Save As?  Really?

Excel_CSV_Changes_Y_SaveAs

Well I don’t want to change the name… so I click

Step 17. Click Save

Noooo tooo many decisions…

 Excel_CSV_Changes_Y_SaveAs_Exists

Really?

Step 18.  Click Yes.

We have to be done right…?

Excel_CSV_Changes_Y_SaveAs_Exist_Y_NonCompatx2 (1)

Brain explodes… get a friend to click yes…

Step 19.  Click Yes.

 

FINALLY it closes.

 

So just take that for what its worth. There are really only 3 issues that reoccur that makes the process painful.   One if I open up a document and click save:  Save it, pretty simple.  Excel offers a “Save As” option, if i want to specify a different name or change the type I will select “Save As”.  Second, when its time to close prompt me to save changes (if I have actually changed anything) and then accept my answer and close.  In our situation it took 5 clicks to close… ?

 

Another thing I notices was the dialog that says “filename may contain features that are not compatible with ..(CSV or TAB).  What does that mean?  I didn’t do anything more than enter csv stuff.  I didn’t use formulas, charts, etc so what is it complaining about?  The first time I opened a CSV, it was completely empty.   But for some reason it decided it was a TAB delimited format?  Who knows.  Its almost like they are trying to scare you into the “lastest Excel format.”  Oh well, if you take anything away from this entry:

 

Americans are indecisive by nature, so don’t overwhelm  with unnecessary, and very word prompts.  Code to the things that the majority of people will want/need to do, and only interrupt people when it is completely necessary.





.NET 4.0 Dynamic

30 09 2010

There is a built in type now in .NET 4.0 called “dynamic.”  Just as its name implied its a lose type, it has an infinite amount of properties and methods…(reminds me of python)  This should not be confused with “var” which is a strongly typed lazy-man’s variable. 

 

Dynamic is whatever you want it to be quick example… 

 

The below code illustrates the use of dynamics.   I have declared a simple class that has two properties (Name and Description) and one method PrintUsingReflection…

 

using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Reflection;

namespace BlogTests
{
    public class Test
    {
        public string Name { get; set; }
        public string Description { get; set; }

        public void PrintUsingReflection(object test)
        {
            string name = test.GetType().GetProperty("Name")
			    .GetValue(test, null) as string;
            string Description = test.GetType()
			   .GetProperty("Description")
			   .GetValue(test, null) as string;

            Console.WriteLine("Hello there " 
			    + name + " description"
				+ Description);
        }
    }

    [TestClass()]
    public class DynamicExample
    {
        [TestMethod]
        public void ThisIsATest()
        {
            object test = new Test()
            {
                Name = "Nick",
                Description = "Nicks Dynamic Variable!"
            };

            PrintUsingDynamics(test);
        }

        private void PrintUsingDynamics(object test)
        {
            dynamic dynamicTest = test;
            Console.WriteLine("Hello there " + 
			   dynamicTest.Name + " description" +
			   dynamicTest.Description);
            dynamicTest.PrintUsingReflection(test);
        }
    }
}

 

Lets start with the test method called ThisIsATest()..

I am going to start by initializing a class called Test and then cast it to an object…

object test = new Test()
{
    Name = "Nick",
    Description = "Nicks Dynamic Variable!"
};
PrintUsingDynamics(test);

I then pass the object to PrintUsingDynamics() within the method you will see:

dynamic dynamicTest = test;
Console.WriteLine("Hello there " + dynamicTest.Name + " description" + dynamicTest.Description);

I take the provided object(which is really a “Test” class) and assign it to the “dynamicTest” variable.  At this point it is now a lose type…

In case you don’t believe me.. lets see what intellisense has to say about “dynamicTest.”

 

Dynamic_AutoComplete

 

Inside my Console.WriteLine i then attempt to access the Name and Description properties… then the black magic of dynamics takes over.  Volia! 

“Hello there Nick Nicks Dynamic Variable”.. displays. 

 

Back in the day we would have had to have done some really code heavy reflection:

private void PrintUsingReflection(object test)
{
    string name =  
	   test.GetType()
	     .GetProperty("Name")
		 .GetValue(test, null) as string;
    string Description =    
	   test.GetType()
	     .GetProperty("Description")
		 .GetValue(test, null) as string;

   Console.WriteLine("Hello there " + name  + 
                    " description" + Description);
}

Now if you look at the line after we print out "Hello there..” you will see dynamicTest.PrintUsingReflection(test);

dynamic dynamicTest = test;
Console.WriteLine("Hello there " + dynamicTest.Name + " description" + dynamicTest.Description);
dynamicTest.PrintUsingReflection(test);

Which is an example of accessing a “method” on a dynamic type.  At runtime the compiler crosses its fingers, and then calls the method PrintUsingReflection and hopes that the method exists… and volia!  Once again

“Hello there Nick Nicks Dynamic Variable”.. displays. 

 

This has been around for a while… but its incredibly useful!

 

Which one is easier (correct answer is dynamics)….?  The basic premise is whatever you type has to exist.. the compiler basically skips compile time checking.. and at runtime will resolve any property/method calls so be careful!

 

This comes in handy when doing things with “DataItems” that are anonymous types… avoids you having to create some type of “internal” type just so you can get to the data you need..





A pessimist is never wrong.

27 08 2010

I got into trouble a while back because I wrote a method that does exactly what I said it would do.  Strange huh?  I missed one important step, I trusted that the end user would respect the input requirements of my routine. 

Simply stated, if you provide me this input(preferably Hairy Potter or Genie) , my method will solve world hunger.

Unfortunately my end user provided me with a red headed step child, and as a result I returned a pyramid scam. 

 

Before I dive into code, I want to justify the title of the post.  One important lesson learned in software development is that you can’t trust anyone, and that no field tests Murphy’s Law more, than large scale system development.  When I develop I ask myself how can someone break this?  And I then make every reasonable effort to prevent a user from doing something that they shouldn’t. If I write my code to assume that everyone is illiterate, or refuses to read the documentation in my code, then I will protect myself from evil programmers.

 

Simple example which also shoes a fancy .NET 4 way of checking method pre/post conditions.

 

ConvertToCaptish_withComments

 

So my documentation clearly states a NON NULL English word,  but deush-bag Larry decided that he wanted all words so he passed null.  When he ran his program he was very angry to find out that null translates to an “Object reference” exception, which was not included in my documentation.

 

What you can’t see here is that my code assumes that the user will follow my simple request, pass me a NON-Null value.  So in my code I don’t assert that the user passed a non null value, I just TRUST that the user will follow my orders. 

So my code:

        /// <summary>
        /// This method will convert any plain english word to the Capitish language.
        /// </summary>
        /// <param name="englishWord">A non-null english word</param>
        /// <returns>Capitish equivelent word/words</returns>
        public static string[] ConvertToCapitish(string englishWord)
        {
            //1 known trouble word
            string[] matches = null;
            List<Func<string, string[]>> translators = new List<Func<string, string[]>>()
            {
                (string x)=>x.GetHomonyms(),
                (string x)=>x.GetKnownPermutations(),
                (string x)=>x.WildGuess(),

            };
            int index = 0;
            do{
                matches = translators[index++](englishWord);

            }while(matches == null || matches.Length == 0);
          
            return matches;
        }

Gracefully crashes upon null strings.  Now, this is only a simple example that I created to show you why you should always ASSERT that people are following your orders.  Pre .net 4 this was painful, you either had to use an external library, or you had to use asserts.  Either way it was painful.  Well now.. .NET 4 gives you “Code Contracts” which makes this much easier.

 

Just an aside I use mock string  Extension methods to do the actual translation:

    public static class StringExtensions
    {
        public static Dictionary<string, string[]> MagicDictionary = new Dictionary<string, string[]>();

        public static string RANDOM_WORD { get; set; }

        public static string[] GetHomonyms(this string word)
        {
            return MagicDictionary[word];
        }
        public static string[] GetKnownPermutations(this string word)
        {
            return MagicDictionary[word];
        }
        public static string[] WildGuess(this string word)
        {
            return MagicDictionary[RANDOM_WORD];
        }
    }

A quick blurb about code contracts:

Code Contracts allow me to enforce my assumptions in an easy fashion.  They provide you with static and dynmaic checking and also allow you to easily append your assumptions in auto generated documentation.  So the first cool part is that when I add code contracts it will extend my signature to enforce my constraints.  So in the above example if I add a contract that states string must be non null, and Larry tries to pass a null it wont compile!  The second piece is at runtime, if Larry decides to set a string to null and pass it, to try to get around my “check” it will also kill is attempt to foil my method.  The last piece is there are tools you can run that allow you to extract both the “method documentation” and the assumptions (pre/post conditions) that you used.    Enough words, and lets let the code speak for a bit!

An example:

I lied earlier  ;) you need to download the “static” checker for Visual Studio 2010.  All the .NET classes are there, but if you want to IDE integration/checking then you need to download from here:

http://msdn.microsoft.com/en-us/devlabs/dd491992.aspx

So I added some contracts:

        /// <summary>
        /// This method will convert any plain english word to the Capitish language.
        /// </summary>
        /// <param name="englishWord">A non-null english word</param>
        /// <param name="iReadYourPreConditions">Pass true to prove you read this</param>
        /// <returns>The possible set of capitish words</returns>
        public static string[] ConvertToCapitish(string englishWord, Boolean iReadYourPreConditions)
        {
            // Assumption
            Contract.Requires(iReadYourPreConditions == true, "Please read the pre conditions..");
            //Requirement
            Contract.Requires<ArgumentNullException>(englishWord != null, "word cannot be null");

            //1 known trouble word
            string[] matches = null;
            List<Func<string, string[]>> translators = new List<Func<string, string[]>>()
            {
                (string x)=>x.GetHomonyms(),
                (string x)=>x.GetKnownPermutations(),
                (string x)=>x.WildGuess(),

            };
            int index = 0;
            do{
                matches = translators[index++](englishWord);
                (null as String).GetHomonyms();
                englishWord.GetHomonyms();

            }while(matches == null || matches.Length == 0);
          
            return matches;
        }

And as you will see i now have 2 contracts.  A is that you read my method description, and B is that you passed me a non null English word. 

So first I thought it was neat I compiled and got a “warning” that basically said hey you wrote bad code!  I didn’t even ask for any advice!

ConvertToCaptish_withCodeContract_warning

I purposely created this issue but, obviously I should do a null check on “english word.”  What the compiler sees is (assuming englishWord is null):

 (null as String).GetHomonyms();

Which obviously will result in a crash.

 

But back to my contracts.  So assume Larry doesn’t read and just tries to use my method:

ConvertToCaptish_withCodeContract_1

Fail!  So he reads my pre conditions, and then tries:

ConvertToCaptish_withCodeContract_1_a

Then he gets bold and attempts to really trick my code:

ConvertToCaptish_withCodeContract_2

 

And fails yet again.    Well lets just say he found a way to get a null in there.. what happens then?  Well at runtime I would slap him on the wrist with a ArgumentNullException:

ConvertToCaptish_withCodeContract_runtime

 

Conclusion:

Well there is your intro guide to CodeContracts.  I didn’t really show you any truly cool stuff.  But hopefully it hints on the potential of code contracts. Unfortunately there aren’t that many good resources out there yet on CodeContracts so you have to get your hands wet… the true moral of this blog is to be defensive in your programming!  Code with a purpose, and make sure you hold people responsible for adhering to your contract.

 

PS if you are wondering what Capitish is?  Well people that know me know I sometimes have issues with mixing up common words, or just spelling things as they sound… details.. but over the years people that know me, know how to translate what I say, and what I really mean.

Take care.








Follow

Get every new post delivered to your Inbox.