Sunday, December 3

Using attributes to specify the contents of a menu

Most people think it is a good idea to separate business and presentation logic in your application, so you can update one without changing the other. When you create an application with a menu bar, the menu items will often call functions in your business logic, and if you add a new business logic function you will need to add a handler in your presentation layer and the business logic function in your business logic layer.


This article describes how you can add attributes in your business logic layer and have these dynamically bound to a menu when your application runs. The attributes will look like the following:


   public class MenuHandler
{
[MenuOption ("Menu Option 1")]
public void MenuHandler(object sender,
EventArgs e)
{
//Business Logic
}
}


Creating your Attribute

The first thing you need to do is to set up your attribute. Create a class derived from System.Attribute as follows.


    [AttributeUsage(AttributeTargets.Method)]
public class MenuOptionAttribute :
System.Attribute
{
private string MenuText;

public MenuOptionAttribute(String
menutext)
{
MenuText = menutext;
}

public string GetMenuText
{
get
{
return MenuText;
}
}
}


The first line of this specifies your attribute can be applied to methods (and not to classes). The constructor takes one argument, which is a string representing the menu text.


Applying your attribute to your business logic

Next, you write your business logic layer to use the MenuOption attribute. All you need to do is to put this before any functions you want on your menu, with the text to go on your menu:


    public class BusinessLogic
{
[MenuOption ("Menu Option 1")]
public void Option1(object sender,
EventArgs e)
{
// Option 1 code
}

[MenuOption("Option 2")]
public void Option2(object sender,
EventArgs e)
{
// Option 2 code
}

You can add as many of these functions as you like.


Setting up your menu

Finally, you need to create your presentation layer. You can drag a MenuStrip to a form and add a title for your drop down menu. If you call the title "Main", when you add the title, it will create a ToolStripMenuItem, called "mainToolStripMenuItem" by default.


You need to use reflection to iterate over all the methods in the MenuHandler class, and see which of those methods have the MenuOptionAttribute attribute set.


If it is set, you need to call the GetMenuText() function on the attribute to find out the text to display on the menu bar and add the menu item.


Next, you need to create a delegate to your method.


Finally, you add the event handler to your newly created menu item. It's easier to understand this if you read the code example.


  public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
BusinessLogic bl = new BusinessLogic();

mainToolStripMenuItem.DropDownItems.
Clear();
foreach (MethodInfo method in
(typeof (BusinessLogic)).GetMethods())
{
foreach (object attribute in
method.GetCustomAttributes(true))
{
if (attribute is
MenuOptionAttribute)
{
ToolStripItem newitem =
mainToolStripMenuItem.DropDownItems.Add((
attribute as MenuOptionAttribute).GetMenuText);
EventInfo ci = typeof
(ToolStripItem).GetEvent("Click");
Type tdelegate = ci.
EventHandlerType;
Delegate del = Delegate.
CreateDelegate(tdelegate,bl,method);
ci.AddEventHandler(
newitem, del);
}
}
}
}
}

You could add some code to your attribute to specify its position, and use this to order your menu items. You might want to specify more than one string, one for the heading and one for the item itself, so you can put menu items in under different headings.

Saturday, October 21

InternalsVisibleToAttribute and Unit Testing

When creating unit tests to be run with NUnit, I like to keep my tests in a separate assembly, which is not delivered when my project is deployed.

This can make it difficult to create unit tests for classes that should not be visible outside of that assembly. One of my classes uses one of 3 strategy classes that encapsulate a feature of the business logic. I would normally declare these with the intern access modifier, but to test each strategy works I've needed to make them publically available.

If, however, I add the [assembly: InternalsVisibleTo("UnitTests")] line to my AssemblyInfo.cs file, any objects declared with the intern access modifier can be seen by the UnitTests assembly.

Tuesday, October 10

Processes and Practices

Jared Richardson describes a process he wants his team to follow. Many others have attempted the same and have reached similar conclusions, and I can't disagree with any of Jared's thoughts.

However, I think he misses out the most important part of the process which should be to regularly think about your process and what you have done and how you could have done it better. All processes and practices should be adaptable. Different people have different strengths and new technologies mean we have to develop things in different ways.

A lot has been written about technical debt, where if not enough time is invested in developing high quality software, it becomes harder to maintain and costs of new developments increase over time. We should also think about process debt, where if not enough time is spent improving and adapting our processes, they become less efficient and costs of new developments also increase.

C# collection classes performance

I was looking for a table summarizing how different C# generic and non-generic collection classes performed, relative to one another, thought there would be hundreds available, but could not find one. So here one is for my future reference, and for anyone else who reads this.



O(1) = constant time
O(log n) = time proportional to the log of the number of elements in the collection
O(n) = time proportional to the number of elements in the collection

Some collections are better for smaller collections, but don't scale to larger ones. The List, LinkedList, SortedList, Queue and Stack classes are better for smaller collections than the Dictionary, Hashtable and SortedDictionary classes.

Wednesday, October 4

The type or namespace name 'Properties' could not be found

I was getting an error message when building my project that said "The type or namespace name 'Properties' could not be found".

This was because I had changed the default namespace in the properties for my project to make it "project.name" instead of "name". The class that did not build was already in the namespace "project.name".

The error message was on a line that said

"global::name.Properties.Resources...."

Changing this to

"global::project.name.Properties.Resources...."

seemed to fix the problem.

Thursday, September 14

Export to excel from SQL Server Express

Once upon a time, a big bad grizzly bear wanted to write a program to export a view from an SQL Server Express database (containing the names and addresses of sweet fluffy things he wanted to eat) to an Excel file. As this was SQL server express, he tried to use DTEXEC and integration services, but growled ferociously when he realised that integration services was not supplied with excel, promising to sharpen his claws and tear apart Bill Gates.

However, a passing raccoon came to his rescue, and told him about the following trade secret way to do this (subject to removing the raccoons name and address from his database):

1) Go to the Surface Area configuration tool, select "Surface Area configuration for features". Select "Ad Hoc Remote Queries", and turn on "Enable OPENROWSET and OPENDATASOURCE support".

2) Create a template excel file, with the first row containing the column names in your view. You can use integration services to export a file to Excel, and then delete the data from it, leaving the first row.

3) From your program, use the File.Copy command to copy the template file to your destination file.

4) Run the following SQL query to populate the Excel file

insert into OPENROWSET('Microsoft.Jet.OLEDB.4.0', 'Excel 8.0;Database=c:\\aaa.xls;', 'SELECT * FROM [Query]') select * from ExportView

where c:\\aaa.xls is your file name, "Query" is the name of your worksheet and "ExportView" is the name of your table or view you want to export.

Friday, August 11

Shrinking an SQL Server 2000 Transaction Log

I have a database in which the transaction log grew to 19GB, taking up the entire disk space on the server it was on, and which could not be shrunk. Most of the things I found on the net didn't shrink this very much.

However, I found the following script here which worked well, reducing it from 19GB to 52 MB.

http://www.thescripts.com/forum/thread79839.html


use database_name
go
create table shrinkfile(
col1 int,
col2 char(2048)
)

dump tran database_name with no_log
dbcc shrinkfile(logical_name_of_log, 50, TRUNCATEONLY)
go


set nocount on
declare @i int
declare @limit int

select @i = 0
select @limit = 10000

while @i < @limit
begin
insert into shrinkfile values(@i, 'Shrink the log...')
select @i = @i + 1
end

drop table shrinkfile

Thursday, August 10

Updating .NET user controls on the screen

I have some user controls that I need to add a lot of data to. This can be slow as the screen display is updated when each item of data is added. It also makes the dislpay flicker.

I couldn't find an equivalent of the BeginUpdate and EndUpdate functions in .NET 2.0, but it is possible to call the Windows API functions as follows.


private const int WM_SETREDRAW = 11;

[System.Runtime.InteropServices.
DllImport("user32.dll")]
static extern bool SendMessage(IntPtr
hWnd, Int32 msd, Int32 wParam, Int32
lParam);

///
/// Stop updates while we are
/// filling a control with data
///

protected void BeginUpdate()
{
SendMessage(this.Handle, WM_SETREDRAW, 0, 0);
Cursor.Current = Cursors.WaitCursor;
}

///
/// Restart updates
///

protected void EndUpdate()
{
SendMessage(this.Handle, WM_SETREDRAW, 1, 0);
if (Parent != null)
{
Parent.Invalidate(true);
}
Cursor.Current = Cursors.Default;
}


I included these in a base class used by all my user controls, and added code to change the cursor to a wait cursor whilst the control was updating.

Wednesday, August 9

Image List Control

I've had a problem with putting images on a tab control. They appeared fine in the designer, but did not appear when the application was run.

I fixed this by re-ordering the generated InitializeComponent function, and put the code for the ImageList before the code for the tab control.

Monday, July 24

When developers at the seaside get too competitive, projects suffer from pier pressure.

At a recent London .NET users group meeting, Dr Neil Roodyn spoke about Extreme Programming. He mentioned the core values for this now include "Respect", and the people involved in a project should be considered before the methodology or technology.

I take the term "Respect" to mean that we should all work to ensure that every person or organisation involved with a project perceives they benefit from the project, and not selfishly to gain at the expense of others.

It is easy to assume that people are “like us” and want the same things. However, this is not the case and we should understand what everyone's individual motivations are. I think motivations can be divided into three areas, quality of life in the workplace, qualify of life outside the workplace and prospects for the future, and there are several things you need to understand in each of these areas.

Quality of life in the workplace
  • Is the stakeholder working with people they like? (which may be different from people you like).
  • Is the stakeholder using technologies and methodologies they enjoy using? (which may be different from technologies and methodologies you enjoy using).
  • Does the stakeholder perceive the working environment to be pleasant to them? (we place different values of things like fast computers, comfortable chairs etc).

Quality of life outside the workplace
  • Does the stakeholder have time for a high quality life outside the workplace (are working hours reasonable, do they need to spend a long time travelling, and do they have to work away from home?)
  • Does the stakeholder have money for a high quality life outside the workplace

How will these change in future?
  • Does the stakeholder have the opportunity to learn skills to develop their career in the way they want to (in some cases this will be to move into senior management, in others to learn technologies, but this is different in individual cases)?
  • Does the stakeholder perceive their situation to be secure (the answer to this question may be different for someone with family commitments who cannot easily move to another area)?

Wednesday, July 12

Sorting a Data-Bound ComboBox

I have set up a view with an order by clause and bound it to a ComboBox using ADO.NET 2.0. The combo box did not take any notice of the order by clause, and seemed to display items in the order they appeared in the database tables that made up the view.

To fix this, I had to manually set the Sort property of the column I wanted the combo box to be ordered by on the BindingSource used by the control as follows before calling the Fill method on the table adapter :-


myViewBindingSource.Sort = "ID";
myViewTableAdapter.Fill(myDataSet.myView);


Why can't it pick up the order by clause in my view automatically?

Friday, June 30

Web 2.0 – Four Key Metrics

Many people have put up web sites with the aim of selling a product or a service, want to improve things to sell more of their product but target the wrong things (such as page hits), or make random improvements without really understanding how effective they are.

Four things that can be easily tracked, the results of the tracking understood, and improved.



1) When someone enters your website, which actions do they perform.

You need to understand the percentage of people who visit your web site who perform different types of action.

Actions may include things like viewing detailed product descriptions, looking at product comparison charts and sending a message to a customer service representative.

2) Does each action drive sales

You need to know the percentage of people who undertake an action who buy from you, and compare it with the percentage of people who enter your site.

How to measure 1 and 2
  • When someone enters your site you can start a session and record the ID of the session to a database table.
  • When someone performs an action, you record the ID of the session and a code for the type of action.
  • When someone buys your product, you record the ID of the session and a code representing "sale".


How to use this to improve sales
  • If lots of people undertake action 1, but action 1 is not positively correlated with sales, then action 1 needs to be developed to ensure people buy your product as a result of taking it.
  • If a few people undertake action 2, but action 2 is positively correlated with sales, you need to draw attention to action 2 to ensure that people see it.
  • If few people take action 3, and action 3 does not drive sales, the action can probably be dropped.



3) Convert Sales into Buzz

People buy based on recommendations from their friends. You need to ensure people tell their friends when they have had good service and track this.

How to measure this
  • Search for some of your company's sales terms using a search engine. If you sell blue widgets, search for "blue widgets".
  • See what percentage of these contain positive "buzz" about your company.


How to use this to improve sales
  • Develop user communities and ensure people can see them.
  • Encourage people to write about these communities and your products in blogs.
  • Track how the number of sales compares with the number of web sites with buzz about your products.
  • Note which user communities and ways of encouraging people to write about your service are successful and do the same in future.


4) Ensure customers remember their user experience and come back for more.

It is easier to keep an existing customer than generate a new one.

How to measure this

Track the percentage of sales that are repeat orders.

How to improve this
  • Develop a catchy URL and promote it on all your sales materials
  • Remind users about your site, with carefully targeted promotions. You might want to target some promotions at half of your users, to better see if they result in improved sales.
  • Note which of these result in an increase in the percentage of sales that are repeat orders, and do the same in future.

Thursday, June 29

Dimensions of Quality, Scope, Budget and Timescale

There is an interesting discussion here about the variables that you can control to decide how to deliver a piece of software. Sam argues that there should be five variables of Scope, Time, People, Process and Risk. In a comment, Basil posted a link to his site, where he says he thinks you should consider four variables of time, resources, scope and quality. I agree with Basil, and think it is important to know what contributes to each of these variables. I came up with the following diagram.



The attributes that define quality are Availability, Performance, Scalability, Security, Accessibility, Extensibility, Deployability and Maintainability. You might decide that some of these are non-negotiable, and others can be traded against each other or against budget, scope or delivery deadlines. (I got these criteria from Ed Tittel's Book, where they are described in more detail)

The attributes that define scope are the numbers of features required and their complexity. Some features might be non-negotiable, but you might be able to simplify them to meet deadlines, budgets or quality requirements. Sometimes you might be able to leave features out.

The attributes that define budget are people, equipment and acceptable risk. You might have flexibility to change the numbers of people or quality of equipment on a project to meet other goals. You might also be able to think about the risks. If someone were to become ill just before release date, is it going to be acceptable to get an expensive consultant in for a few days to ensure the release happens, or should other people within the team know what to do so this won’t be necessary. Obviously it will take time to transfer knowledge amongst the team, meaning less can be done or quality has to be compromised.

Finally, the attributes that define time are a deadline, the acceptability of risk and the time taken to learn and reflect. It may be possible to negotiate the deadline. A degree of risk with the deadline may or may not be acceptable. If someone is ill, it may be acceptable to wait a few days and postpone the deadline, or it may not. You also need time to learn and reflect. The more time you spend learning and reflecting on what went well and what didn’t the faster future projects will be.

When you have thought about these attributes, you need to understand what can be changed and what is fixed. The more that is fixed, the more likely it is the project will fail. However, if there are lots of things that can be changed, you need to think about what the priorities are – is it more important to implement lots of features, or is having a few features of high quality preferable?

When you know this, you can choose a process, waterfall or agile, that is best for this particular project.

Friday, June 23

Attractive User Interfaces with Agile Development

Marc McNeill writes that agile methods result in cleaner and more elegant code than waterfall methods, but waterfall methods produce a more attractive and consistent user interface, giving users a more positive impression of your application.

When features of your application are developed iteratively as separate "objects" by different people, the temptation is for a developer to think about what the user interface needs to be for that object, and not how it fits in with the overall experience.

The way I like to approach this would be to make the first feature to be implemented the "user interface", where we can show users things like what the menu structure of your application should look like, and how in general things should work (e.g. should any dialog boxes be complex with lots of information on the same box, or should they be simplified and spread over several dialog boxes).

As new features are developed, developers can use the basic user interface for guidance and to ensure a consistent looking application. If a new feature needs a new dialog box or web form, the developer can look at the basic UI and see how this should be done. This will also mean that anyone writing documentation or producing sales materials can start earlier as the UI is less likely to change.

However, you need to be careful to make sure that users don't see a user interface and conclude your application is almost complete. Careful communication is important.

Monday, June 5

A bug fix is an enhancement

Dave Churchville writes that not all bugs should be blindly fixed without considering the consequences. I think the decision to fix or not fix bugs should be considered using the same criteria as a decision to enhance your software.

A decision to enhance your software should depend on the following:

1) The payback from implementing the change (in terms of increased sales or reduced operational costs)
2) The cost of making the change
3) The risk that the change will be unsuccessful or cost more than originally anticipated.

A decision to fix a bug should be based on the same principles :-

1) Increased sales or reduced operational costs if the bug is fixed
2) The cost of fixing the bug
3) The risk the fix will be unsuccessful, cause other things to break or be more difficult than anticipated.
4) The risk that if the bug is not fixed it will cause a problem.

Often, bugs are considered separate from enhancements, go into separate databases and decisions are based on different criteria. If they are considered the same thing and tracked and prioritised in the same database, the correct balance can be struck between delivering feature-rich software with lots of bugs or a product with few features but few bugs.

Thursday, June 1

What's on your bookcase?

The books below are currently on my office bookshelf. What is on yours? What does this say about me?