Monday, July 04, 2011

WPF 4's DataGrid - bound template columns, anybody?

I recently had occasion to use the WPF DataGrid component introduced in .NET 4. And here's something really stupid: all the built-in primitive column types (text, checkbox etc.) derive from a DataGridBoundColumn base type. This allows you to specify a binding for the cell content - very logical and WPFy.

The DataGridTemplateColumn is the sole exception. I reflected the code and noticed that they're using a ContentControl internally to present the cell - so how hard would it be to simply apply the binding to the Content property? I'll tell you how hard - it's one line of code.

It's seems like they've done more work and added an extra base class simply to make the template column less useful.

Stupid.

Labels: , , , ,

Configuration-driven IoC why?

I've recently gone off configuration-driven IoC in a big way. It certainly has it uses (particularly in web applications, I've found, and especially if you're selling them on). However it makes no sense at all for desktop apps. Usually you find that if you change the configuration, you're redeploying the app anyway - so why not have the configuration done centrally in code? I've never seen a non-programmer change configuration, so this costs you nothing and saves you a ton of supporting infrastructure.

Also, config-driven code tends to lead to the phenomenon of too much loose coupling. Since you have to obey the container's rules about initialization, you can't make compile-time assertions about dependencies, which can lead to a sort of "fire away and hope it's there" style of programming. And plenty of mysterious errors in apparently unrelated parts of the application.

Just my two cents, I'm not feeling particularly vehement about this one.

Saturday, July 10, 2010

Global events - follow-up and sample

After having been asked several times for an example of the new architecture I spoke about in my previous post, I've posted a sample at CodeProject - The Service Tree Model.

It's a short sample, and it's missing the all-important wiring-builder, but you can get an idea of how the configuration is generated and how the app uses it.

Wednesday, June 23, 2010

Global events considered harmful

The “global event architecture” is what could loosely be described as an architectural pattern used principally in composite applications (such as those built using the Smart Client Software Factory or, lately, Prism). It is characterized by loosely-coupled components communicating by means of broadcast events, typically marshaled through an event broker. The events are “broadcast” events in three senses: there is little or no expectation by the sender of what will be receiving the event; a given event may originate in multiple components; and the receipt of events is usually) application-wide, i.e. not confined to any particular context. Also characteristic of this pattern is that senders are connected to recipients implicitly, by the name or the type of the event.

In my article, "Global events considered harmful", I attack the global event architecture often found in composite applications. Having previously worked on three major applications that used the Smart Client Software Factory architecture or derivatives, I recently was privileged to be able to design a completely new architecture suitable for WPF applications.

By far the largest source of problems in the composite applications I had worked on is what I call the "global event architecture". The specific problems I identified were:

  • Implicit contracts are formed by the events and their arguments.
  • The contracts tend to overlap in function and meaning.
  • State is scattered throughout the application.
  • It is difficult to get a picture of the interdependencies of components, as they are implicitly coupled by the events.
  • It can be difficult to localize the events (e.g. to the scope of a single dialog or view)
  • Hanging subscriptions can cause serious application errors, and are hard to find and solve.
  • Event receipt/publication requirements in one module may force code to be written in one or more apparently unrelated modules, introducing nasty cross-cutting concerns and delocalized code.

The new architecture solved these problems, and proved to be far easier to develop on and maintain. It is based around the idea of having a "wiring builder". The wiring builder essentially writes a script to run just one application-level controller. The controller constructs containers, binds component properties (including exported commands and command dependencies), and manages workflow where necessary. From the wiring builder, we can generate dependency diagrams between components, as well as workflow diagrams indicating the transitions between application states.

The bulk of the application consists of many simple components and services, each with a single function. The components are written so that their dependencies can easily be mocked; this means that contracts between components are well-defined and testable, and also that it is easy to develop the components in isolation. This also improves reusability.

This new architecture I call the service-tree model, and it has the following advantages:

  • Having a wiring manager means that applications can quickly be reconfigured in form and function, without the necessity of a binary release.
  • The wiring manager can give you a view of dependencies and workflows that corresponds to what you would see in a functional spec.
  • It's much easier to develop the individual components, because they do not rely on external events to drive their state.
  • The code has much better locality.
  • The application configuration is centralized.
  • State contracts are explicit.

If the article seems one-sided, it's because experience with the new architecture has left me absolutely convinced that I'll never again use a non-local event. Hopefully you'll have the same experience.

Read: Global events considered harmful.

Labels: , , ,

Thursday, May 13, 2010

CrossThreadAction

Here's another handy class in the spirit of WeakEventHandler. It's useful when you want to run work on a background thread, but wish to provide a callback that will be run on your own (usually UI) thread. Apparently Silverlight users will find this particularly useful, there being no Application.Current.Dispatcher in Silverlight, but as an almost-exclusively WPF user, I use it as helpful shorthand for "call me back on my own thread".

The class:


public class CrossThreadAction<TData> : DependencyObject
{
Action<TData> target;

public CrossThreadAction(Action<TData> target)
{
this.target = target;
}

private void Invoke(TData data)
{
Dispatcher.BeginInvoke(target, data);
}

public static implicit operator Action<TData>(
CrossThreadAction<TData> cta)
{
return cta.Invoke;
}
}


Use it as follows:


private void StartWork()
{
// using a server method with signature
// void DoWork(Action<string> callback);
server.DoWork(new CrossThreadAction<string>(FinishWork));
}

private void FinishWork(string result)
{
textBlock1.Text = result;
}


WPF users may make it slightly less heavy by inheriting from DispatcherObject instead of DependencyObject. Also, beware of storing an instance in an untyped variable; generally it's a good idea to cast it to Action<TData> as soon as you've created it.

Labels: ,

Friday, May 07, 2010

Electoral Reform in Britain

What’s wrong with the system


As anybody who has followed this election will have realised, it’s staggeringly unfair that the Liberal Democrats, with 23% of the popular vote, only got 8% of the seats. That’s not democratic, and it entrenches the two-party system. Do you often find it hard to distinguish between the respective policies of Labour and the Tories? You’re not alone.


The Lib Dems want a proportional representation system. However such systems have their own problems, the biggest being that MPs would be even less accountable to their constituencies than at present. Think safe-seat manoeuvres on steroids. I don’t think that will fly in a nation where constituents are used to being able to chuck out MPs they don’t like. MPs who are unpopular with the electorate will get in by virtue of their party’s popularity rather than their own. As Italians will attest, this leads to MPs being able to shrug off the most outrageous of scandals, provided only that they can make good with their party.


A sensible solution


Much better would be to implement democracy at a local level. Suppose that each constituency elected a small parliamentary council, proportionally representative of the candidates (a Single Transferrable Vote system would do very well for these councils). That parliamentary council would then elect a representative to sit in parliament, but – and here’s where the power really comes to the people – the council would have the power to instruct its representative on which way to vote. That means that votes in parliament would be on the basis of what the constituencies want, rather than what the party wants. Real democracy!


Why this will never happen


The problem is this: the parties simply won’t wear it. Being unable to whip their MPs into line, party policies would become not so much policies as guidelines. An MP might join a party to benefit from collective think-tanks on the issues, but the real power would be taken away from the parties and given instead to the constituencies. You like it, I like it, politicians hate it. After all, what’s the use in being Prime Minister if you can’t tell parliament what to do?


Nice thought, though


Credit for this idea goes, as far as I can tell, to Sir Antony Jay and Jonathan Lynn. Wouldn’t it be nice if politicians really did act in the interests of the people who elected them?

Monday, November 16, 2009

Slow file deletion on Windows 7 RTM

The picture below says it all:



Of course, it didn't actually take 23 hours. It actually took about 45 minutes - that's around 13 items per second. More than half of which was taken up by calculating how long it was going to take (estimating the quality of the calculation is left to the reader).

Now I understand the need for transactional semantics during a delete, the necessity of shared filed locking, and the possibility of file system watchers getting involved in their own idiosyncratic ways.

But 13 items per second? On a 10,000 RPM drive? Seriously, guys.

Friday, November 13, 2009

The WeakEventHandler

When your Observer class hooks an event on an Observable, it implicitly creates a strong reference from Observable to Observer. That means the Observer won't be collected until the Observable is. That means memory leaks at best; latent bugs due to out-of-date observers at worst. So I've written a little WeakEventHandler class that behaves like a weak delegate: it allows the Observer to be collected before the Observable is. Best of all, the syntax is almost identical to the original.

Instead of writing

observable.Event += new EventHandler<MyEventArgs>(this.HandleEvent);

you'd write

observable.Event += new WeakEventHandler<MyEventArgs>(this.HandleEvent);

and the delegate behaves as if it were weak. However there is one downside: the proxy object that is created to pass the event to the observer hangs around. This can cause problems if the event is attached to by a great many short-lived observers. Fortunately there is a solution:

observable.Event += new WeakEventHandler<MyEventArgs>(this.HandleEvent,
handler => observable.Event -= handler);

The implementation of WeakEventHandler is given below.

delegate void EventDetacher<TArgs>(EventHandler<TArgs> handler)
where TArgs : EventArgs;


public class WeakEventHandler <TArgs>
where TArgs : EventArgs
{
MethodInfo method;
WeakReference target;
EventDetacher<TArgs> detacher;


public WeakEventHandler(EventHandler<TArgs> realHandler, EventDetacher<TArgs> detacher)
{
this.method = realHandler.Method;
this.target = new WeakReference(realHandler.Target);
this.detacher = detacher;
}


public WeakEventHandler(EventHandler<TArgs> realHandler)
{
this.method = realHandler.Method;
this.target = new WeakReference(realHandler.Target);
this.detacher = null;
}


private void HandleEvent(object sender, TArgs args)
{
object realObject = target.Target;
if (realObject == null)
{
if (detacher != null)
detacher(this);
}
else
{
method.Invoke(realObject, new object[] { sender, args });
}
}

public static implicit operator EventHandler<TArgs>(WeakEventHandler<TArgs> proxy)
{
return proxy.HandleEvent;
}
}