March 2005 - Posts
Every now and then you will see blog entries which read along the lines of:
"I think that product Y is the suckiest thing on the planet
because every time I press the foo button everything
chokes and I have to restart my PC"
Reading that causes an adjustment to my in-built impartiality-o-scope.
Don't get me wrong, some things that fail to meet expectations really do suck, for example:
Imagine that, when the next automatic computer update
comes down the line it breaks something really stupid -
ie: it forces you to restart Windows every time you press
the Start button . That just flat out sucks! They should've
tested that, somebody needs to get chewed-out for that.
On the other hand, what if product Y or the foo button are relatively new. Does it NOT suck that pressing the new foo button causes you to restart your PC... hrmm, no, it still sucks but... rather than going down on the whole of product Y because the foo button is fundamentally broken, why not use this space of time as an opportunity to tell me something of the sense of wonderment that you felt as you were about to press that shiny new foo button for the first time. Tell me what plans you had for it. Tell me how you were about to use it. Was it creative? Was it new?
I'm much more likely to get engaged with your blog writing if you can help me to live the software dream.
"We live, as we dream alone"
- Joseph Conrad (English novelist, 1857-1924)
"When I hear somebody sigh, "Life is hard,"
I am always tempted to ask, "Compared to what?""
- Sydney J. Harris
"Between the conception and the creation
between the emotion and the response
Falls the shadow"
- Joseph Conrad (English novelist, 1857-1924)
"It occurred to me that my speech or my silence, indeed any action of mine, would be a mere futility"
- Joseph Conrad (English novelist, 1857-1924)
"I dream for a living"
- Steven Spielberg
Quotes found on http://en.thinkexist.com/
Consider exposing raw Generic collections from your data logic layers, such
as:
public class PersonManager {
...
public List<Person> ListPeople(...) { ... } ;
}
When I started messing around with building applications in 2.0 I quickly
wrapped Generic collections like so:
public class PersonCollection : List<Person> {
...
}
This was normally done so that I could hang a Sort method off of them:
public class PersonCollection : List<Person>, IBidirectionalSort {
...
public void Sort( string sortExpression, bool isAscending ) { ... }
}
The downside of this approach is that you end up writing fiddly code around
calls to generic helper methods.
As an example, let's say that I write a nice generic helper method to page my
collections:
public static void Page(ref List<T> data, int maximumRows, int startRowIndex)
where T : IDataObject, new() {
if (data.Count > 0) {
if (maximumRows > 0 && startRowIndex >= 0) {
List<T> tmpColl = null;
int remainingRowCount = data.Count - startRowIndex;
int count = (remainingRowCount >= maximumRows) ? maximumRows : remainingRowCount;
if (count > 0) {
tmpColl = new List<T>();
tmpColl.AddRange(data.GetRange(startRowIndex, count));
}
data = tmpColl;
}
}
}
If I’ve wrapped my collection – as per the PersonCollection example – then
using the generic Page method will require temporary object creation when I’m
calling it, something like:
public PersonCollection ListPeople(...) {
...
PersonCollection people = FillList( reader ) ;
List<Person> tmp = Page( people, 20, 0 ) ;
PersonCollection peopleToReturn = new PersonCollection() ;
peopleToReturn.AddRange( tmp ) ;
return peopleToReturn ;
}
So, you can see that by the time we have many generic methods, working with
temporary objects becomes cumbersome. Exposing List<Person> from
this method would lead you to build your surrounding methods – such as FillList
and Page – to work with your code better but will also lead to leaner code:
public List<Person> ListPeople(...) {
...
List<Person> people = FillList<Person>( reader ) ;
return Page<Person>( people, 20, 0 ) ;
}
Danny Chen just blogged about the SiteMap and showed some interesting ways to make use of custom attributes:
http://weblogs.asp.net/dannychen/archive/2005/03/28/396099.aspx
There's another one that I'd like to add to this list. Commonly people are using UL elements to create navigational links because they require less Html to be emitted in the page and are easily styled into nice looking links. This is the approach that modern applications such as CommunityServer and ProjectDistributor use for their lists of links. So how would you do that with a SiteMap? The answer is actually pretty simple because you can bind a SiteMapDataSource directly to a Repeater.
<ul>
<asp:Repeater DataSourceId="myDataSource" ...>
<ItemTemplate>
<li>
<a href='<%# Eval("Url") %>'><%# Eval("Title") %></a>
</li>
</ItemTemplate>
</asp:Repeater>
</ul>
<asp:SiteMapDataSource id="myDataSource" runat="server" ShowStartingNode="false" />
That will give you a nice list of clickable links.
On my site I actually only required a subset of the items in the SiteMap to be rendered on a specific menu. For example, I had a left navigation menu which only displays a subset of the total items. In this case I can use the technique that Danny showed off by adding a custom attribute to my SiteMapNode's like so:
<?xml version="1.0" encoding="utf-8" ?>
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >
<siteMapNode url="Home.aspx" title="Home" DisplayOnLeft="true">
<siteMapNode url="Work.aspx" title="Work" />
<siteMapNode url="School.aspx" title="School" DisplayOnLeft="true" />
</siteMapNode>
</siteMap>
So, you can see that 2 of the nodes contain a DisplayOnLeft attribute which is set to true. Now, to conditionally display those items in my sidebar navigation list I can hook the Repeater's ItemDataBound event and write logic like so...
SiteMapNode node = e.Item.DataItem as SiteMapNode ;
string display = node["DisplayOnLeft"];
if( string.IsEmptyOrNull( display ) || display != "true" ) {
e.Item.Visible = false ;
}
Web applications that are context aware will be able to make greater use of
autonomous agents to directly manipulate graphical objects and affect the users
display. The MIT paper titled "Autonomous Interface Agents" says of
autonomous agents:
An autonomous agent is an agent program that operates in parallel with
the user. Autonomy says that the agent is, conceptually at least, always
running. The agent may discover a condition that might interest the user and
independently decide to notify him or her. The agent may remain active based on
previous input long after the user has issued other commands or has even turned
the computer off.
How many times have you built an application and then, when speaking to a
user about how they use it been confounded to learn that they don't know about
some of the best little features that you included in it?
So through better use of agents, we can envisage a system where users
are offered cues to increase their productivity with a tool. Some simple
illustrations of this might include systems which:
- know which features a user hasn't used and offers some ad-hoc advice about
that feature to a user.
- can offer help whilst a user is using a particular feature
- remember past screens that a user has used and offer them as "favourite"
short-cuts
- offer quick-fix solutions for filling-in forms with valid data
Autonomous Interface Agents
http://web.media.mit.edu/~lieber/Lieberary/Letizia/AIA/AIA.html
If
you want to install the ASP.NET V2 tables and procedures for things such as
Membership, Personalization, etc you need to run the aspnet_regsql.exe tool
against your database. The tool can
be found in the %windir%\Microsoft.NET\Framework\{FRAMEWORKVERSION} folder.
Once
you've run that tool, then it's just a matter of replacing the ConnectionString
entry in your web.config file to point to your database:
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<connectionStrings>
<remove name="LocalSqlServer" />
<add connectionString="CONN STRING" name="LocalSqlServer" providerName="System.Sql.DbClient" />
</connectionStrings>
</configuration>
After
that, the built-in API's will read and write from your database for
that application.
In the excellent whitepaper titled "Out of context: Computer systems that
adapt to, and learn from, context", there's a section nearing the end
titled: "The view of context from other fields" - containing the following
subsections:
Mathematical and formal approaches to AI.
Context in the human-computer interface field
Context in
sociology and behavioral studies
Taken from the section on the mathematical approach the document uses the
following example to highlight the problems about making contextual
assumptions:
Several areas of mathematics, and formal approaches to artificial
intelligence (AI), have tried to address context in reasoning. When formal
axiomatizations of commonsense knowledge were first used as tools for reasoning
in AI systems, it quickly became clear that they could not be used blindly.
Simple inferences: “If Tweety is a bird, then conclude that Tweety can fly”
seemed plausible until the possibility that Tweety might be a penguin or an
ostrich, a stuffed bird, an injured bird, a dead bird, etc., was
considered.
While having a better understanding of context seems to be the next major
landmark in building better software, we've already seen that making poor
assumptions can lead to even greater user dissatisfaction.
"Out of context: Computer systems that adapt to, and learn from,
context":
http://www.research.ibm.com/journal/sj/393/part1/lieberman.html
Today I had a conversation with a friend who is a musician. We were
discussing some of the similarities between music and software and even
extending many of them to any creative pursuit where the output is consumed by
others. One of the things that we noted was that, as with software,
end-users of music do not always share the feelings and
experiences envisaged by the architects of the product. I'm not sure
how a musician can give corrective advice to an end-user
about such a discrepancy at "runtime" but, in software we are
fortunate that we can use context and UI elements to teach a user about the
intended usage of a system.
Providing the user with a shared understanding of a system's
capabilities is an area that will increasingly be solved by software agents that
can use contextual information to know when and how to offer explicit and
implicit cues. Examples of this in current Microsoft software
include:
- AutoCorrect functionality in Office
- AutoComplete and Refactoring in VS2005
- Clippy and friends in Office
My days of using handcrafted Access database applications to automate
inventory reconcilliation seems to be nothing but a distant blur. Too soon
it seems that I was whisked away from my accounting world of Office applications
and surrounded by millions of rows worth of raw data. There's
something about real, raw data that seems to make my nerve edges jingle in a
merry way.
After the accounting world, I moved into the world of "fetch and
format". This often feels like the world of high demand,
low-cost, digital paintings. There's an art to it that I haven't yet
mastered so onward I plod. The frustration here is that I often feel that
I'm not empowered to take control of information and really build something that
creates valuable knowledge for a creative, energetic culture. Is it my
mind that is limiting me? My skills?
Things started to change recently with some of the new applications and
frameworks that the Office team have created; OneNote and Information Bridge
Framework (IBF) certainly appeal to me, as does InfoPath to a slightly
lesser extent.
In IBF I can clearly see the ability to put useful information at the
finger-tips of empowered knowledge workers and insustrial consumers
of data. Having worked in an environment where access to information
is vital while preparing financial models in a time-restricted environment I can
see the benefits of opening up systems and this seems like a framework that is
ready and able to make that a reality.
Once again it is Office products that have my blood surging and my head
spinning with plans for putting data through a technological treadmill and
producing artefacts of value at the other end.
After reading Mark's entry about Joe versus the Volcano I decided that I'd go out and hire it for a look. Finding a video store which had it in stock wasn't that easy, even asking for it raised eyebrows - this is not surprising given that the film was released at about the same time that some of the staff who were serving me were born.
The film is an enigmatic look at man who, after being told that he has a fatal disease called 'Brain Cloud', is able to release himself from a dull existence and free his mind to the real potential of the world.
Echoing Mark's sentiments, I loved the quote about the world being asleep:
My father says that almost the whole world is asleep. Everybody you know. Everybody you see. Everybody you talk to. He says that only a few people are awake and they live in a state of constant total amazement
My other favourite scene in the movie arrives as Joe is in the process of awakening; he turns to his hired chauffeur and asks him for advice about what kind of clothes he should purchase, to which the chauffeur answers:
Clothes make the man. I believe that. You say to me you want to go shopping, you want to buy clothes, but you don't know what kind. You leave that hanging in the air, like I'm going to fill in the blank, that to me is like asking me who you are, and I don't know who you are, I don't want to know. It's taken me my whole life to find out who I am, and I'm tired now, you hear what I'm saying?
"In the moment when I truly understand my enemy, understand him well enough
to defeat him, then in that very moment I also love him."
- Orson Scott
Card
"What does not kill me makes me stronger"
- Johann Wolfgang von Goethe
"The enemy of my enemy is my friend."
- Unknown
"They may forget what you said, but they will never forget how you made them
feel."
- Carl W. Buechner
"You su-diddly-uck, Flanders!"
- Homer Simpson
More Posts
Next page »