Tuesday, 30 August 2011

Thoughts on UI – disable or hide..?

To guide the user on what he/she is expected to do gives a very high user experience. For instance, disable the save button until required fields are filled in and are validated. Maybe even make the button the user “should” click greenish to emphasize that it is the expected button to use. Sure, some things may take a while to validate and then it’s not suffice to let the user wait for the save button to be enabled, but that’s another story. User “errors” should be caught as early as possible! Because that’s what we  are used to in the real world – we are utterly bombarded with feedback all the time! E.g. try to open a door! You will instantly feel if it is locked or not. You don’t click the “open” button and cross your fingers that you did everything correct so the door will open…

And it works for more things then just a save button. To disable things the user is not supposed to touch until expected aids the user in a very good way.

But is it even better to hide things?

It’s a tough question. Here’s my thoughts.

It’s good to hide things if it confuses the user and isn’t relevant. If the user is presented with to much information at one time he/she doesn’t know what to do. That’s a known psychological phenomenon called choice overload phenomenon.

But at the same time, it’s good to know what to expect. To hide a save button would be terrible. If you see a form with no save you would probably leave it untouched. Then it’s better to disable instead of hiding.

But when do you know when things are confusing and should be hidden or when they should be disabled to let the user know what he/she is expected to do?

Simple. Talk to the users! Ask them! Show them the alternatives! “What do you think is better? This?”

mockup1

“Or this?”

mockup2

Thursday, 11 August 2011

jqGrid MVC model

I really love jqGrid! If you haven’t used it before – try it! http://www.trirand.com/blog/
To be able to use it with ASP.NET MVC and JSON I created a generic model class to easier reuse it when passing it around in MVC actions in the controller.
To load jqGrid with data (JSON) I call an MVC action like this:
   1: $('#StoreGrid').jqGrid({
   2:         url: '<%=Url.Action("StoreList", "StoreController") %>',
   3:         datatype: 'json',
   4:         mtype: 'POST',
   5:         colNames: ['Store Name', 'Store Address'],
   6:         colModel: [
   7:             { name: 'Name', index: 'Name' },
   8:             { name: 'Address', index: 'Address'},
   9:         ]
  10: ...
Just an example, you get the point. Then in my action I do:
1: public JsonResult StoreList(string sidx, string sord, int page, int rows)
2: {
   3: ...
   4:     // Load data from repository
   5:     IQueryable<Store> stores = _repository.GetAllStores();
   6:     int totalRecords = stores.Count();
   7:     int totalPages = (int)Math.Ceiling(totalRecords/(double)rows);
   8:  
   9:     // Convert business model to view model
  10:     List<StoreListItem> storeListItems =
  11:         stores 
  12:         .Select(s => new StoreListItem(s.Id, s.Name, s.Address)
  13:         .ToList();
  14:  
  15:     // Create JSON data that jqGrid understands :-)
  16:     var jsonData = new JqGridJsonModelView<StoreListItem>
  17:        {
  18:          total = totalPages,
  19:          page = page,
  20:          records = totalRecords,
  21:          rows = storeListItems
  22:        };
  23:     return Json(jsonData, JsonRequestBehavior.AllowGet);
Ignore the repository stuff, paging and sorting is ignored here for simplicity – you shouldn’t – hence the totalPages calculation is redundant (but it wont be if you call e.g. GetStores() and pass paging and sorting parameters to it). Anyway, the key here is the JqGridJsonModelView<T> class. With it I can create any class (in this case StoreListItem) and it will create JSON data that jqGrid understands. Lovely! But what does it do? It’s really simple:
1: /// <summary>
2: /// See http://www.trirand.com/jqgridwiki/doku.php?id=wiki:retrieving_data#json_data
   3: /// </summary>
   4: public class JqGridJsonModelView<T> where T : IHaveJqGridCells
   5: {
   6:     /// <summary>
   7:     /// Total number of pages
   8:     /// </summary>
   9:     public int total { get; set; }
  10:  
  11:     /// <summary>
  12:     /// Current page
  13:     /// </summary>
  14:     public int page { get; set; }
  15:  
  16:     /// <summary>
  17:     /// Total number of records
  18:     /// </summary>
  19:     public int records { get; set; }
  20:  
  21:     public IList<T> rows { get; set; }
  22: }
The property names has a bad naming here, but it must corrolate with jqGrid (http://www.trirand.com/jqgridwiki/doku.php?id=wiki:retrieving_data#json_data). T here is the model, in this case StoreListItem. But what’s IHaveJqGridCells? Again, simple:
1: public interface IHaveJqGridCells
   2: {
   3:     string id { get; set; }
   4:  
   5:     string[] cell { get; }
   6: }
Bad naming, as said before, but it’s what jqGrid uses. So, all we have to do is let StoreListItem implement IHaveJqGridCells:
1: public class StoreListItem : IHaveJqGridCells
2: {
   3:     // Implement interface
   4:     public string id { get; set; }
   5:  
   6:     // Private properties to "disable" serialization (no need to be public anyway)
   7:     readonly string Name;
   8:     readonly string Address;
   9:  
  10:     public StoreListItem(int id, string name, string address)
  11:     {
  12:         this.id = id.ToString();
  13:         
  14:         Name = name;
  15:         Address = address;
  16:     }
  17:  
  18:     // Implement interface
  19:     public string[] cell
  20:     {
  21:         get
  22:         {
  23:             return new[] { Name, Address }
  24:         }
  25:     }
  26:  
Voila! We’re done! Now this class will be serialized into JSON (by MVC action). The serialized result will look like this:
1: {
   2:   "total" : "xxx", 
   3:   "page" : "yyy", 
   4:   "records" : "zzz",
   5:   "rows" : [
   6:     {"id" : "1", "cell" : ["Store 1", "Address 1"]},
   7:     {"id" : "2", "cell" : ["Store 2", "Address 2"]},
   8:       ...
   9:   ]
  10: }
Now it’s really easy to extend the model and/or reuse it.

Hope you like it!

Update (requested by danlimerick):
If you want to do sorting/paging. I usually use Dynamic Linq Query Library and make sure the naming in the jqGrid colModel corrolates to the naming in the repository:
1: IQueryable<Store> stores = _repository.GetStores(sidx, sord, (page - 1), rows);
   2:  
   3: ...
   4:  
   5: public IQueryable<Store> GetStores(string sortIndex, string sortOrder, int page, int rowsPerPage)
   6: {
   7:     return _context.Store
   8:                 .OrderBy(sortIndex + " " + sortOrder)
   9:                 .Skip(page * rowsPerPage)
  10:                 .Take(rowsPerPage);
  11: }
I wouldn’t call it elegant, but it’s a pretty convenient way.

Wednesday, 22 June 2011

ASP.NET Web Forms MVP - what MS forget

MS has created a Model-View-Presenter framework for web forms. ASP.NET Web Forms MVP
It looks very good and I will probably use it for new web form projects (god forbid) and inject it in legacy web form projects. I like the testability and SoC and features like databinding a la Silverlight and WPF.

But I think they miss one point, the point I like the most and the reason why I will stick to ASP.NET MVC. The marriage between MVC and AJAX. The RESTfulness of MVC. It allows me to build applications with high user experience. I don't like the PostBacks, ViewStates etc. And please don't mention UpdatePanels, I have banged my head against it enough. Especially when I compare it to the beauty of MVC + AJAX.

But for some projects it may be quicker to use web forms. Then I will use it. But it hasn't happend - yet...

Tuesday, 14 June 2011

TFS: Check in changes to another branch

Sometimes you start working on some changes just to later realize that you have to interrupt your current work for other things. Then you all say: "That's what shelving is for!" then I say "yes, but sometimes you feel it would be better to make a branch with those changes instead. That works. Just create a branch (from a label or whatever) then shelve your pending changes and then unshelve your changes to that branch with TFS Power Tools:

C:\TFS>tfpt unshelve [shelve name] /migrate /target:$/Some/Path/To/Name-branch-NewFeatures /source:$/Some/Path/To/Name

Monday, 13 June 2011

Easy to make an impression - use Jing from TechSmith!

I really recommend Jing from TechSmith. I'm not gonna talk too much about what it is, you can find that out by yourselves.
But it's really easy to make people impressed by it. Created a video clip where I showed some instructions on how to do a certain thing (instead of writing 10 pages of text-instructions). It was really - really - appreciated and impressive!

I also use it when I can't reproduce a bug and send it off to customers (instructions on how I try to reproduce the bug). Very handy!
Now, I just wish customers could send me bug reports as video clips... :-)

Web deploy from Visual Studio 2010

Saw a presentation (movie clip) where a person deployed to a remote IIS from VS2010 via Publish. It looked very sweet.
Web Deploy

The problem is, you have to do some (or a lot) configuration in IIS to allow this. All information is found in this blog post http://weblogs.asp.net/scottgu/archive/2010/09/13/automating-deployment-with-microsoft-web-deploy.aspx

Tuesday, 31 May 2011

Story points = time - but time is relative

I'm reading the book Agile Estimating and Planning by Mike Cohn. I "disagreed" with some things he wrote in the book so I wrote him a mail asking him mainly about the story points. My opinion was that you should never compare story points with work-load. He gave me a good answer and also a link to his blog (I'm not going into details of the answer) http://blog.mountaingoatsoftware.com/its-effort-not-complexity.

Now I understand I had got it all wrong. It's absolutely valid to compare it with work load. It's just that my time is different than your time. That's why you shouldn't use time when e.g. playing planning poker - because five points for me may be ten hours of work, but for someone else it's five hours or maybe twenty. But you should never tell the others what a "five" is for you. Just agree on that ten points are twice as much work and two points are half (not quite). Thus it's a good method to try to find a basic story that everyone can agree on is in the middle range and then compare all other stories to that one.

Then - if the team is pretty much the same and the project is similar - you could actually say that 100 story points = 400 hours of work (or something like that).