# Wednesday, March 21, 2007

Just thought I'd make a post on what can be done to jazz up the average message bar or UI element.  In this example I'll be doing it by way of a Photoshop tutorial I hope everyone can follow.  As always, leave me a note in the commenst section below if this leaves any questions unansered.  It's a bit lengthy but I hope that is because I'm including enough detail.

Step 1: Start with a new image that will be the size of the UI piece you need.  In this case 500 pixels wide by 25 pixels high:

Step 2: Select the gradient tool (Keyboard Shortcut: G) and customize as follows (things to click on are highlighted in Red on the sample screen below):

  • Choose the Reflected Gradient mode from the toolbar
  • Drop down the Gradient picker and choose the Foreground to Background gradient

Sample screen:

Step 3:  Make a background

Select a background and foreground colour in the toolbox that match the look you are going for.  Today I am going for light gray and dark gray because as everyone known things that are made out of "technology" are gray!  :-)

Notice the lighter colour is above the darker colour - you can swap the foreground and background colour with the X key on the keyboard

Now click and drag your mouse from top of your image to the bottom.  You will want to hold the Shift key down - this makes sure your gradient is painted straight with regard to the edge of the image.  You may want to have a couple of tries at it to find something you like - remembering that you can start your drag from the canvas outside your image.

Mine now looks like this:

Step 4:  Adding a highlight

An image in Photoshop is made up of layers.  A discussion on layers is worth a whole book, but just do as I say you will be OK :-)  I'm saying this now because it is from here on dowm that layers come into play for our example.

Duplicate the layer that we made our gradient on by pressing Ctrl-J on the keyboard.  Watch the Layers window inside Photoshop when you do this and you will see a new layer being created.  Change the blend mode of this layer to be Differnece.  Don't worry if your gradient turns black - we'll sort that out in a second.

The Layers window will look like this:

Now:

  1. Invert the new layer by pressing Ctrl-I.  This should again change the look of your image but don't worry - few more steps to go yet.
  2. Press D on your keyboard to reset your toolbox colours to Black and White.
  3. Click the Add Layer Mask button on the Layers window to add a Layer Mask to our new top layer
  4. Press Ctrl-Backspace on your keyboard to fill the layer mask with the background colour - Black.  This has the effect of making our new later not show at all.

So what is the point of making a new layer and then hiding it?  Well, we don't want to hide all of it - now we will make a small piece show through the Layer Mask that is hiding the Inverted copy of our gradient.  I am choosing to show a circle but you can play with different shapes.  To do this:

  1. Select the Elliptical Marquee tool from the tool box.  This one is hiding behind the rectangular marquee tool.  Press Shift-M to switch between the available types of marquee until you find the one with the circle icon in the toolbox.
  2. Hold down the shift key to force the Elliptical Marquee to make only circles, not an oval or ellipse, then click and drag over the left side of yoru top layer to make a circle.
  3. Press the X key again to swap the Foreground and Background colours agian - now White should be the background colour.
  4. Press Ctrl-Backspace again on your keyboard to fill our little circle with White.  You should now be seeing the inverted gradient only in the circle.  That is what the layer mask is doing for us - Where pixels are White on the layer mask, the layer it is hiding will shine through.  While pixels are Black on the Layer Mask the associated layer will not be seen.
  5. Right-click our top layer on the Layers window and choose Blending Options.  Choose Bevel and Emboss and then tweak the sliders to taste.  Here's what values are working for me today:

By now our graphic will look something like this:

Step 5:  Adding Modes

Here I'd like to use the same graphic for Error, Information and OK messages.  I'll use System.Drawing to actually paint the message text onto the image on a form, but for now I just want three states for the message that I can choose from.

Press the T key on the keyboard to choose the Text tool on the toolbox.  I'm now going to set the foreground colour to a nice blue, click above our new circle and press type a question mark: ?

You will need to change the font face and font size on the toolbar to suit.  Once you have what you like click the Tick button on the toolbar to save this question mark as a new text layer.  NB:  Using the text tool you can always come back to this layer and change it.  I have also added a Drop Shadow to my text layer using the Blending Options menu item again, but this is just down to personal taste.  Try it and see what you think.

Now my screen looks like this:

Notice that all the layers have an Eye icon in the first column.  This is so you can show or hide whole layers at a time.  This will become important shortly!

Now hide the Question Mark text layer and add two more:  one for the famous Red X and another for a Green Tick.  Hint:  There is a tick symbol in the Marlett font under the lower case b key.

Step 6:  Create final images

Remember what we said about showing and hiding individual layers?  Here's where it becomes useful.  We are going to save this image three times.  Each time we save it we will make a different combination of laters visible before saving. 

So first make sure your two gradient background images are showing plus only one of the text layers.  Start with showing the Question Mark text layer and not the Red X layer or the Green Tick later, then choose Save For Web from the File menu.  Choose a file name like InformationBar_Question.jpg.

Your choices for GIF vs. JPEG vs PNG, compression etc are all for the purposes of this exercise personal taste.  Depending on if the image will end up on a web form or a windows form your choices here could matter a lot!

Now repeat the process showing only one other text layer at a time and naming accordingly.

Here are my three final images:

InformationBar_Error.gif:
InformationBar_OK.gif:
InformationBar_Question.gif:

Advanced Class:  A few extra things to try

  • Make a new Gradient out of as many colours as you want.  Ninja skills in making a gradient is the art of being able to make something look like is concave or convex on the page.  Most important in this is making sure all UI elements on a page cast the same shadown (direction, colour and spread) and apear to be lit from the same direction.
  • To keep things on-brand, grab existing on-brand grapics and CSS sheets and stealresearch colour information on them. 
  • Use the sRGB colour space when targeting the web.
  • Save your master image as a PSD file to preserve the layers and make it easy to maintain if things change in the future. 

Listening to:  Perry Farrell - Song yet to be sung

Wednesday, March 21, 2007 12:55:20 AM (AUS Eastern Daylight Time, UTC+11:00)  #    Disclaimer  |  Comments [0]  | 
# Monday, March 19, 2007

I've been looking for a nice graphical representation of the access modifiers.  Couldn't find exactly what I want so, with the help of the very cool class designer in Visual Studio 2005 I have done my own.


RED means public access from even outside the project
BLUE here is protected access, for visibility in classes that descend from the class with the declaration.
GREEN represents friend access, for visibility in other classes in the project.

The one to note is FleaCount:  Protected Friend is the union of both Protected and Friend.

Monday, March 19, 2007 6:36:36 PM (AUS Eastern Daylight Time, UTC+11:00)  #    Disclaimer  |  Comments [0]  | 

Today's link propagation is for a page called: patterns & practices Security Checklists Index over at MSDN:

http://msdn2.microsoft.com/en-us/library/ms998392.aspx

This page describes itself as:

This page provides an index of patterns & practices Security Checklists organized by categories using multiple views.

It's slightly dated, but still current.  One for the bookmarks.

Does anyone have any other .NET checklists they can share?

Monday, March 19, 2007 1:24:20 PM (AUS Eastern Daylight Time, UTC+11:00)  #    Disclaimer  |  Comments [0]  | 
# Friday, March 16, 2007
...snipped from a discussion earlier today with someone who should know about C#!

btw, I found one conclusive reason as why to use C# over VB.Net when doing unit tests with NMock
in C# the line is:

Expect.Once.On(mockFetcher).Method("GetDatabases").Will(Return.Value(mockReturnValue));

in VB it is:

Expect.Once.On(mockFetcher).Method("GetDatabases").Will(NMock2.Return.Value(mockReturnValue))

Return is a NMock2 type as well as (with caps) a VB reserved word!  so you have to add a qualifier.

taking into consideration the semicolon that's 6 extra keystrokes!

Friday, March 16, 2007 5:23:05 PM (AUS Eastern Daylight Time, UTC+11:00)  #    Disclaimer  |  Comments [0]  | 
# Sunday, March 11, 2007

So I came across a problem using a old CSS trick to make a tab strip/menu bar on a site from a Unordered List inside a Div

To create the tabs the trick is you can use some CSS like this:

#nav

   float:left

   margin: 0

   padding: 10px 0 0 46px

   list-style: none
;
}

#nav
li


   font-size: 150%

   float: left

   margin:0

   padding: 0
;
}

#nav
a


   float: left

   display: block

   margin: 0 1px 0 0

   padding: 4px 8px

   color: #333

   text-decoration: none

   border: 1px solid #949494

   border-bottom: none

   background: #686868

   color: #949494
;
}

...to turn a <UL> with the ID="nav" into some tabs.  For example we add the following to the master page:

<ul id="nav" > 
   <li id="navHome"><a href="default.aspx">Home</a></li> 
   <li id="navProducts"><a href="products.aspx">Products</a></li> 
   <!-- ...and so on... -->
</ul>

OK so far.  The thing with tabs is you always want the tab of the current page to glow a little or something to indicate what page the user is up to.

A common way is to assign an ID attribute to the body tag of each page and include some CSS that looks at the body ID and overrides the #nav a  for the current page.

The trick comes when the pages all descend from an ASP.NET 2.0 Master Page - the body tags are going to be identical on each page.

As luck would have it, CSS lets us select based on any attribute.  Once the page is rendered each page will get its own form tag where the Action attribute represents the current loaded page as so:  

<form name="aspnetForm" method="post" action="Default.aspx" id="aspnetForm">

Which would let us add the following to our stylesheet:

#nav a:hover, body form[action="Default.aspx"] #NavPlaceHolder #nav #navHome a
{
   background: #ffffff
;
}

OK So far but the problem is it is case sensitve!  We would need - If the page loads as default.aspx our tab won't highlight.  This just won't do!

The trick is to add a new ContentPlaceHolder in our master page straight above the UL listed above

<asp:ContentPlaceHolder runat="server" id="pageIdPlaceHolder" ></asp:ContentPlaceHolder>

Each page that descends from our master page should then override that ContentPlaceHolder to add an empty Span with a page-specific ID as so:

<asp:Content ID="pageIdContent" ContentPlaceHolderID="pageIdPlaceHolder" runat="server" >
   <
span id="ProductsPage"></span> <!-- This page will show the Products tab as selected -->
</
asp:Content>

and then we can add a rule to our CSS for each page that would read:  Whenever there is an element with the ID="ProductsPage" right next to one with the ID="nav" do the following.  The + operator selects the next adjacent node in the document as so:

#ProductsPage + #nav #navProducts a
{
   background: #ffffff;
}

And there you have it!  Accessable and maintainable nav bars from a UL using CSS, plus keeping all the time saving layout features of Master Pages.

Listening To:  The History Of Breaks

CSS | UX | ASP.Net
Sunday, March 11, 2007 12:26:14 AM (AUS Eastern Daylight Time, UTC+11:00)  #    Disclaimer  |  Comments [0]  | 
# Thursday, March 08, 2007

It is probabbly wise to remember domain rules in the database when considering Intent Insurance.

A common scenario for a check constraint is to ensure the range of values in a field in cases where no foreign key constraint is available to constrain the values.  The typical example is the case of a [State] field, where a check constraint on the table may look like:

[State] IN ('NSW', 'QLD', 'VIC', 'ACT', 'TAS', 'NT', 'SA', 'WA')

To contrast this with the foreign key constraint, you may be guided by personal taste or circumstance to not create a lookup table of States since it is not expected to change any time soon and would then require a JOIN every time we needed to see the State with any address. 

Now to the point of the post, I just came across an example that is worth mentioning.  Consider the following simple and neatly anonymous table of clients:

Imagine a business rule exists as follows:

  • If this client is known to us by an online referal, they must have an email address
  • Any client, online referal or not, can have an email address

You would naturally expect this rule to exist programatically in the presentation tier.  The Intent Insurance mindset would then be to also add a check constraint to the table:

([online_referal] = 0) OR ([online_referal] = 1 AND [email_address] IS NOT NULL)

And there you have it, in one line another little parachute in the application asserting your intent.  Sleep a little easier tonight :-)

Thursday, March 08, 2007 9:47:03 AM (AUS Eastern Daylight Time, UTC+11:00)  #    Disclaimer  |  Comments [0]  | 
# Tuesday, March 06, 2007

Some times I just clap when I'm excited.  It's a bit Rain man-has-spoken.  Maybe I was stung by a bee when I was a child, I can't explain it. I clapped for the Litware HR overview doco.

According to SaaS Sample Application.pdf, the following bits that are required:

  • Windows Server 2003 SP1 or R2
  • IIS 6.0
  • SQL Server 2005 Full Cream or Express editions
  • Microsoft Visual Studio 2005 Pro or Team Suite (recommended)
  • .NET Framework 3.0
  • Visual Studio 2005 extensions for .NET Framework 3.0 (Windows Workflow Foundation)
  • Visual Studio 2005 extensions for .NET Framework 3.0 (WCF & WPF), November 2006 CTP
  • Enterprise Library for .NET Framework 2.0
  • Active Directory Application Mode (ADAM) SP1
  • Guidance Automation Extensions
  • Guidance Automation Toolkit  

...and that is all that you need to take over the world.

ADAM is what jumped out at me.  I have managed to not have much exposure before now however get the feeling ADAM and I are going to get a bit better aquainted before this exercise is done.

I'm building up a VPC image with all the bits now and I'm taking Friday off work to sit at home with my DeLonghi Metropolis 1385 Espresso machine and the coolest SaaS guidance around and I'm not coming out until I have a business plan or my head explodes.  (I'd say you will know by Monday, if you are curious)

Tuesday, March 06, 2007 1:11:34 PM (AUS Eastern Daylight Time, UTC+11:00)  #    Disclaimer  |  Comments [4]  | 

Amazing.  I just noticed that the term SaaS has only had the most fleeting of references on my blog thus far, and then only in jest.

This is amazing to me because as anyone who has had a yarn with me about the ISV space over a couple of glasses of wine knows its hard to shut me up about it.

What does this prove?  I don't blog after a couple of glasses of vino.  :-)

Situation rectified 

Listening to: Kings of Leon

Tuesday, March 06, 2007 12:47:50 PM (AUS Eastern Daylight Time, UTC+11:00)  #    Disclaimer  |  Comments [0]  | 
# Tuesday, February 27, 2007

Just a follow-up on my post of yesterday about the intent of exposing a read-write property of an Array/Collection type.

Practical Guidelines and Best Practices has the following guidance (i'm paraphrasing)

13.13 To protect the state of a backing var for properties that return arrays, consider

get { return DirectCast(_arr.Clone(), string()); }

which is good protection, but less efficient.  Or, provide indexed access to the array by an indexed property or function.  Getting close...

13.14 Properties that return a collection should be read-only to prevent clients replacing the whole array with a new one, or with a null reference as that could break assumptions elsewhere in the class. Disco! 

Thanks lads!

Tuesday, February 27, 2007 8:53:36 AM (AUS Eastern Daylight Time, UTC+11:00)  #    Disclaimer  |  Comments [0]  | 
# Monday, February 26, 2007

This one came up in discussion last week and could be worth mentioning.

Consider the following property:

private List<SchemaAttribute> _attributes = new List<SchemaAttribute>();
public List<SchemaAttribute> Attributes
{
   get
   {
      return _attributes;
   }
   set
   {
      _attributes = value;
   }
}

NCover/NCoverExplorer reliably inform me that this property has only 50% coverage
in my tests.

By hereto unexplored forces of nature, every consumer of this class calls
.Attributes.Add()

or, itterates over it as so:
foreach (SchemaAttribute element in instance.Attributes)
{
   // ...
}

The keen eye will notice that neither of these ways to access the property actually hit the setter! 

Two possible paths present themselves:

  1. Write a test that just exercises the setter.  This seems to me pointless. 
    If we are to consider that our tests can serve to express our intention
    in a repeatable fashion, what is our intent?  To see if a Generic List still works?
  2. Consider makeing the property read-only.  Including braces I could own three lines less code.  Yay!
    And also now I have had cause to think about it, it's not my intention that the
    List be set as a list, for this class it just dosen't make sense right now.

Next step?  Consult Francesco & Giuseppe!

Sleep.  The best 10 hours of the week.

Listening to:  Nick Cave and the Bad Seeds - Abattoir Blues

Monday, February 26, 2007 2:02:40 PM (AUS Eastern Daylight Time, UTC+11:00)  #    Disclaimer  |  Comments [0]  | 
# Wednesday, February 21, 2007
Rob Farley posted a very interesting codegen post on scripting objects this morning, and as a reforming codegen junky I just couldn't let it go without comment :-)

Firstly, I modified his query as so:

select quotename(si.name) as "@IndexName", quotename(ss.name) AS "@SchemaName", quotename(so.name) AS "@ObjectName",
    stuff((select ',' + quotename(sc.name)
            from sys.index_columns sic
            join sys.columns sc
                on sc.column_id = sic.column_id
            where so.object_id = sic.object_id
                and sic.index_id = si.index_id
                and sc.object_id = so.object_id
            order by sic.key_ordinal
            for xml path('')),1,1,'') as "@IndexColumns"
from sys.indexes si
join sys.objects so
    on so.object_id = si.object_id
join sys.schemas ss
    on ss.schema_id = so.schema_id
where so.type = 'U' and si.type = 1
for xml path('index'), root('indexes')


Things I'll point out:
  • Adding the extra for xml clause, this time specifying a better row element name than 'row' and also adding a document node
  • Once the outter for xml clause is in place, we can alias the columns using @ to have them come out as elements in the results
We can feed the results of that query straight into the following XSLT:

<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text"
/>
   
    <xsl:template match='/'>
    <xsl:apply-templates select ='indexes/index'
/>
    </xsl:template>

  <xsl:template match='index' >
create index <xsl:value-of select='@IndexName'
/>
  on <xsl:value-of select='@SchemaName'
/>.<xsl:value-of select='@ObjectName'/> (<xsl:value-of select='@IndexColumns'/>)
  </xsl:template>
 
</xsl:stylesheet>

I'm not claiming this method is superior, just different, which I think is in keeping with the spirit of Rob's post :-)  and I have learned more about the improvements of the for xml clause in SQL Server 2005 in the process.

I'll leave final judgment on the utility of this approach as an exercise to the reader; my instinctive reaction is to include it as a build step to help you snapshot schema changes between check-ins, or as Rob suggests to take objects from one database and create slightly different objects in another database – there are no wrong answers and if you think of something interesting please leave a comment :-)

Grab the source files here: ScriptObjects.zip (.78 KB)
Wednesday, February 21, 2007 6:32:28 PM (AUS Eastern Daylight Time, UTC+11:00)  #    Disclaimer  |  Comments [2]  |