Oh God How Did This Get Here, I Am Not Good With Computers

Senseless rambling about .NET, SQL and Application Development

  Home  |   Contact  |   Syndication    |   Login
  6 Posts | 0 Stories | 10 Comments | 0 Trackbacks

News

All statements in this blog are my personal opinions and do not reflect the opinions of my employer.

Locations of visitors to this page

Add to Google Reader or Homepage

 Subscribe

Tag Cloud


Archives

Post Categories

Wednesday, December 05, 2007 #

Whether you do web design or desktop application development, you're eventually going to need to include icons in your interface. Not all software developers are handy with image editing software, we can use it, like most people know how to use a paint brush, it's just that we have trouble using the tool to make something that would meet our users expectations. Luckily there are a lot of free resources out there that provide premade icons created by people who have artistic talent.

Icon Finder - Provides a Google like image search that only returns icons
Tango Desktop Project - A pack of icons available in every format and resolution you would ever need, licensed under GPL.
Famfamfam - A collection of icons with a simple and minimalistic style to them. There's also another pack in the same style available here.
Pixel Resort - Fairly new site, with a rapidly growing collection of icons.
Deviant Art - Probably one of the largest collections available, however it's really hit or miss, you can find some really nice sets if you can spare the time.

It's very likely you will find an icon that you like but isn't provided in the size or format you're looking for. In this case I recommend downloading Paint.NET which is a free image editing program that has excellent plugin support. If you're working on a desktop application and require icons in .ICO format you can download the ICO plugin for Paint.NET here.

I guess now would also be a good time to talk about what image format to use for you icons. The best format for most applications is PNG. It's small, no visible compression artifacts and supports alpha transparency. Applications built in Visual Studio support PNG icons along with transparency as do all modern browsers. As is usually the problem, Internet Explorer is the black sheep of the crowd and needs special attention when it comes to PNGs. There are still quite a few people using IE6 which does support PNGs but not PNG transparency. There is a solution to this using javascript within your CSS file to load the appropriate libraries to handle the transparency, however there are some major drawbacks to doing this as discussed here. IE7 introduces PNG transparency support, however still has the problem of incorrectly rendering colors in PNG files, seemingly oversaturating them. The next alternative is the GIF format, which also doesn't have visible compression artifacts and allows for simple non-alpha transparency. Avoid using JPEGs whenever possible.

As far as what you should look for when choosing an icon, try to keep a consistent theme amongst your application, mixing icons from different collections and artists can give the application a rough and inconsistent feel. Apple has an article aimed at developers outlining their guidelines for icon selection and placement. While some of it is Mac specific, many ideas can be related to all applications regardless of platform or OS. Here are some highlights from the article:
  • The icon should contain a tool that communicates the type of task the application allows the user to accomplish. The Preview icon, for example, uses a magnification tool to help convey that the application can be used to view pictures. If you include a supportive tool element, it should closely relate to the base object that it rests upon.
  • Some applications that represent objects, such as QuickTime Player and Calculator, are most easily recognized by the objects themselves. When creating icons for such applications, it’s more aesthetically pleasing to create a simplified, idealized representation of the object, instead of using an actual screen shot of the software. Re-creating the object is particularly important when users could confuse the icon with the actual interface.
  • Because utility applications are normally focused on a narrow set of tasks, it’s best to keep the number of elements in the icon to a minimum. The focus should be a single object that represents what the utility does.
  • The primary purpose of a toolbar is to provide users with easy access to frequently used commands. Although toolbar icons should conserve screen real estate they should be inviting and easy to identify. Ideally, each toolbar icon should represent a unique object or action that is directly related to the command it represents. A toolbar can also contain icons that represent recognizable interface elements from elsewhere in the system when they make sense in the context of the application. If you choose to include an icon such as an Info button, be sure to preserve its meaning. Users expect such icons to mean the same thing in every context, so you should not redefine them when you use them in your toolbar.
  • Do not use a system icon, such as the yellow caution icon, in your toolbar. A system icon provides important information to the user in a specific context, such as in an alert window; using it in a toolbar blurs its meaning and dilutes it effectiveness in the system.
  • Making each toolbar icon distinct helps the user associate it with its purpose and locate it quickly. Variations in shape, color, and image all help to differentiate one toolbar icon from another. At the same time, however, an application's toolbar icons should harmonize together as much as possible in their perspective, use of color, size, and visual weight.
  • Creating a family of visually related toolbar icons can strengthen the user's perception of your application as being well-integrated and well-designed. One way to do this is to start with a consistent theme for the style and appearance of the icons, then introduce variations when it makes sense.

Friday, November 30, 2007 #

By default, the search service is disabled in WSS 3.0. In order to enable it, goto your SharePoint Central Administration page and under the Operations tab, goto the "Services on the Server" list and click on WSS Search.
  • Service Account - You cannot use any built in accounts when specifiying the Service Account, however you can use local accounts. One account you can choose to use is the default Administrator account on your server, "myserver\Administrator".
  • Content Access Account - Use an account that has full read access to your SharePoint Content Databases. As a security precaution, try not to use an account that has write permissions on the database. For example: "mydomain\sqlserveruser".
  • Search Database - In most cases the default settings will work, change them as required.
  • Indexing Schedule - No new files will be searchable until they are indexed, depending on the load you wish to put on the server you can set this to be done as often as you want.
Upon submitting the form, the search service should start and begin indexing the content in your SharePoint site. Now would be a good time to install the Adobe IFilter to allow your search service be able to index PDF files, the earlier you install it the better, since it will only recognize PDF files added after the addon was installed.



Wednesday, November 28, 2007 #

I'm sure it has happened to all of us. You're using a new application, and you know what you want to do, but have trouble finding which button or window lets you do it. You probably start by going through the drop down menus one by one, scanning through the options for something that is remotely related to what you want to do. Bingo, you find what you're looking for but the button is greyed out. You're then left guessing what you need to do to get that button to allow itself to be clicked. Maybe you need to select this item first? Or maybe have this option enabled? Perhaps you need to open a different file? 

Having the user go on a treasure hunt to unlock the functionality they want from the application wastes their time and is a bad design decision on the part of the developer. This is something that is all too common in applications today, and put simply is BAD BAD BAD.

Developers take for granted that if they want to disable a feature in a program they simply grey it out. And while it is effective in communicating that the feature is unavailable, it does nothing to tell the user why it's unavailable. You might say, "Well maybe they should RTFM before they used my application!". And while that would be a fair argument 10-20 years ago, today it's not a valid excuse. Users today don't expect to read a manual before using an application, and rather expect the developer to do a good job designing the interface to make it intuitive and easy to use on the first go. When you buy an iPod, do you get a 100 page manual outlining how to use it? No, you get a tiny pamphlet that pretty much tells you to download iTunes, plug it into your computer and have at it. That's because Apple designed the interface to behave intuitively with the user's initial expectations of how the product should work, and considering their success they're on the right track.

So what better way is there to do this? Simple, rather than greying out the button, have it trigger a message box that communicates to the user that first of all, the feature they are trying to use is currently unavailable, and second, what is it they need to do in order to make it available. From a coders perspective this isn't really anymore work. For example, suppose you have a delete button in your application. Rather than disabling the control when there is nothing selected to delete, which in C# is as simple as mycontrol.Enabled = False; replace that line with MessageBox.Show("You must first select an item from the list to delete.");. That wasn't so hard now was it.

Of course, this doesn't mean that greying out buttons is the wrong thing to do in all cases. There are functions that are so basic that they don't require justification as to why they are disabled. For example, greying out the save button if there are no changes to save is prefectly acceptable. Also, greying out the paste button because there's nothing in the clipboard to paste is also correct. These functions are universal to most applications and most users are expected to understand what they do.

Making the decision as to whether or not you should communicate with the user why something is disabled comes down to what you expect your users to know and find intuitive. In a professionally designed interface one of the first steps that are taken in the design process is evaluating the audience for your application. If you're designing an automated checkout system for WalMart, you would assume that some of your users may have never even touched a computer before, and therefore have the application hold their hand and guide them through the entire checkout process. On the other hand if you're building a tool meant to be used by other developers you would expect them to already understand a lot about how the application works, and would only make the user feel stupid by pointing out something that is already obvious to them.

The bottom line is, before you write the code to disable a button, ask yourself, "Will it be painfully obvious to the user why this button is disabled?". Of course it's obvious to you because you're the one designing the application, the challenge is to put yourself in someone elses shoes, and assume the things they expect from your application in order to answer this question correctly. Once you have the answer you will know whether to simply grey it out, or leave it enabled and communicate to the user why the action cannot be executed.

Monday, November 26, 2007 #

Suppose you want your users to submit a list of items through your web page. These items could be inputted through many means such as a text box, combobox, listbox, etc. There are many ghetto solutions you could use to implement this like comma delimited lists or multiple postbacks. There are however much more elegant, and suprisingly easier ways of doing this using javascript. As an example we will start with an input box with a link below it that allows you to spawn several more input boxes. In addition, each spawned input box comes with its own delete link, allowing the user to remove items if they choose to. Each input box is given a unique incremented ID that can be easily accessed later on through postback.

Here's the javascript that makes it work:
<script type="text/javascript" language="javascript">     
var software_number = 1;
function addSoftwareInput()
{
var d = document.createElement("div");
var l = document.createElement("a");
var software = document.createElement("input");
software.setAttribute("type", "text");
software.setAttribute("id", "software"+software_number);
software.setAttribute("name", "software"+software_number);
software.setAttribute("size", "50");
software.setAttribute("maxlength", "74");
l.setAttribute("href", "javascript:removeSoftwareInput('s"+software_number+"');");
d.setAttribute("id", "s"+software_number);

var image = document.createTextNode("Delete");
l.appendChild(image);


d.appendChild(software);
d.appendChild(l);

document.getElementById("moreSoftware").appendChild(d);
software_number++;
software.focus();
}

function removeSoftwareInput(i)
{
var elm = document.getElementById(i);
document.getElementById("moreSoftware").removeChild(elm);
}
</script>
And the tiny amount of html to get it to show up:
<input type="text" name="software0" id="software0" size="50" maxlength="74" />                
<div id="moreSoftware"></div> <div id="moreSoftwareLink" style="display:block;"><a href="javascript:addSoftwareInput();">Add Another Item</a></div>
You can paste the above HTML and javascript into your own webpage to see it in action.

In some languages such as PHP, accessing these dynamically created elements after postback isn't an issue, it's a simple matter of using a for loop to iterate through each element. However this becomes a challenge if you're using ASP.NET. The problem is that these new elements are created on the client side, and once submitted, the server no longer sees them. The workaround is to use javascript to collect the contents of each element and stuff them into a hidden element in a delimited format. In the case of ASP.NET this hidden element would have to be declared within the page that is originally sent to the client. Then on postback we can collect the contents of this single element, break up each list item by the delimiter and deal with them as we normally would. First we create our hidden attribute to dump our item contents on submit:
<input id="ninjainput" type="hidden" name="ninjainput" />
We'll call it "ninjainput", because it's stealthy and mysterious, like a ninja. Next, we'll insert an additional javascript function that will populate our ninjainput when the user submits the page.
function populateStaticInput()
{
var n = document.getElementById("ninjainput");
var allsoftware = "";
for( var i = 0; i < software_number; i++)
{
var currentele = document.getElementById("software"+i);
if(currentele != null)
{
if(currentele.value.length > 0)
{
if(currentele.value.length > 74)
currentele.value = currentele.value.substring(0, 74);
allsoftware = allsoftware + "~<>~" + currentele.value;
}
}
}
n.value = allsoftware;
}
Notice that we are delimiting each item with a "~<>~". Make sure to add this attribute to your form tag: onsubmit="javascript:populateStaticInput();" This way the javascript will run and populate our ninjainput control before the page is sent to the server. Lastly, we will need a function (the example below is in C#) that will cut up the submitted list of items into a usable format, in this case an array of strings.
public string[] GetAllSoftwareInList(string rawsoftlist)
{
string[] asoft = rawsoftlist.Split("~<>~".ToCharArray(), System.StringSplitOptions.RemoveEmptyEntries);
return asoft;
}
And there you have it, the user doesn't have to endure multiple postbacks or keep track of a comma delimited list. It's presented in an organized and intuitive manner to the user and not too painful to implement for the developer.

Friday, November 23, 2007 #

When developing an application that uses a SQL Server Compact Edition database, you may run into a problem getting your application to build if you frequently compile it to test changes. Specifically the following error:
Problem generating manifest. The process cannot access the file 'C:\...\mydb.sdf' because it is being used by another process.
The problem is that your application didn't properly release its lock on the SQLCE database file the last time you ran it. I find this especially happens when you're debugging and hit an unhandled exception. Since your application runs as a child of the devenv.exe (Visual Studio) process, closing and reopening Visual Studio will release the lock on the SDF file and allow you to successfully compile again. Obviously, restarting Visual Studio everytime you want to test your application isn't very convenient.

There is an easier solution to this problem. You'll need to download Process Explorer, a free utility provided by Microsoft. According to the website, "Process Explorer shows you information about which handles and DLLs processes have opened or loaded". This is precisely what we need to release the SDF file that Visual Studio has taken hostage.

So open up Process Explorer, and using the "Find Handle or DLL..." feature search for "sdf". You may end up with several results, but what you're looking for is the SDF that you use in your application. Once you find it, double click it. The file will then appear highlighted on the bottom half of the window, right click it and select "Close Handle". The lock on the file will be destroyed, allowing you to successfully build your application without getting manifest generation errors.

Thursday, November 22, 2007 #

Say you have an ASP web site that you've deployed and want to be notified when and why your website craps the bed. Since it's live you probably have something like this in your web.config file:
<customErrors mode="RemoteOnly" defaultRedirect="error.aspx"/>
And while it hides all the exception details from your users, serving them a pretty error page, it has the side effect of returning the generic and completely useless System.Web.HttpUnhandledException. So the question is how do you, the developer, recieve the full stack trace and exception details when a user happens to come upon an error using your application? You might feel tempted to use the Application_Error event in Global.asax to catch the exception, parse out all the exception details and email them to yourself. Unfortunately that won't work because ASP will still hide the stack trace from you. Instead of wasting your time writing code from scratch that will do this, you can accomplish the same thing by adding a few lines to your web.config file.
    <system.net>
        <mailSettings>
            <smtp deliveryMethod="Network">
                <network host="yoursmtpserver"/>
            </smtp>
        </mailSettings>
    </system.net>
    <system.web>
        <healthMonitoring enabled="true" heartbeatInterval="0">
            <providers>
                <add name="ErrorEmailProvider" type="System.Web.Management.SimpleMailWebEventProvider" to="you@email.com" from="donotreply@you.com" buffer="false" subjectPrefix="[APP ERROR]"/>
            </providers>
            <rules>
                <add name="EmailErrors" eventName="All Errors" provider="ErrorEmailProvider" profile="Default" minInstances="1" maxLimit="Infinite" minInterval="00:01:00" custom=""/>
            </rules>
        </healthMonitoring>
With those blocks in your web.config file you will recieve an email containing all the information you need to track down which section of code failed. And at the same time your user recieves a friendly error page and is none the wiser. Here's a sample of a simple IndexOutOfRangeException.
** Application Information **
---------------
Application domain: /LM/W3SVC/1/Root/HSR-1-128402415931345878
Trust level: Full
Application Virtual Path: /HSR
Application Path: C:\Inetpub\wwwroot\HSR\ Machine name: myserver


** Events **
---------------
Event code: 3005
Event message: An unhandled exception has occurred.
Event time: 11/22/2007 2:47:40 PM
Event time (UTC): 11/22/2007 9:47:40 PM
Event ID: 051ece02de474c8ea153eb0ffc058f25 Event sequence: 6 Event occurrence: 1 Event detail code: 0

Process information:
Process ID: 4092
Process name: w3wp.exe
Account name: NT AUTHORITY\NETWORK SERVICE

Exception information:
Exception type: System.IndexOutOfRangeException
Exception message: Index 0 is either negative or above rows count.

Request information:
Request URL: http://myserver/HSR/requeststatus.aspx?request=22
Request path: /HSR/requeststatus.aspx
User host address: 000.000.000.000
User: someuser
Is authenticated: True
Authentication Type: Negotiate
Thread account name: NT AUTHORITY\NETWORK SERVICE

Thread information:
Thread ID: 1
Thread account name: NT AUTHORITY\NETWORK SERVICE
Is impersonating: False
Stack trace: at System.Data.DataView.GetElement(Int32 index)
at System.Data.DataView.get_Item(Int32 recordIndex)
at requeststatus.Page_Load(Object sender, EventArgs e) in c:\Inetpub\wwwroot\HSR\requeststatus.aspx.cs:line 27
at System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr fp, Object o, Object t, EventArgs e)
at System.Web.Util.CalliEventHandlerDelegateProxy.Callback(Object sender, EventArgs e)
at System.Web.UI.Control.OnLoad(EventArgs e)
at System.Web.UI.Control.LoadRecursive()
at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)