Extending WinForms controls

Sunday, 2 February 2014

I've been looking at ways to extend and enhance .NET WinForms controls recently and thought I'd summarise what I've found/learned so far.

Inheritance

The most obvious way to extend a control is to create a new class that inherits from the existing class. You then have full access to add new properties and methods and access any protected methods from the base class.

It does mean that if you've already used the control in your application, you're going to have to replace references to the base class with your new class – not always easy.

IExtenderProvider

Properties window showing Tooltip propertyAnother option is to create a class that implements the IExtenderProvider interface. You can then create a component that can be added to a form or user control that extends specific types. An example of this that ships in the framework is the ToolTip class. When you drag this control onto a form it doesn't add a visible control to the design surface. Instead it adds a new ToolTip property to appropriate controls on the same form.


Custom ComponentResourceManager

Visual Studio designer showing properties window with Localizable set to trueA more specific area of WinForms controls that you might want to customise is the localisation support. Localisation for controls on a form is enabled by setting the Localizable property of the form to true. This also alters the designer-generated InitializeComponent method to add a new resources variable of type System.ComponentModel.ComponentResourceManager. This allows you to use .resx resource files to load locale-specific values for properties. This is the standard way that you would provide alternate language translations for your application.

A question on Stack Overflow asked about replacing this implementation with another that could load resources from an alternate location (such as an XML file or a database). The accepted answer pointed to some samples from Guy Smith-Ferrier's book, .NET Internationalization: The Developer's Guide to Building Global Windows and Web Applications.

Impressively, even though the book was published in 2006 Guy has been releasing regular updates to the code samples at http://www.dotneti18n.com/Downloads.aspx. His sample for a custom ComponentResourceManager includes a ResourceManagerSetter class that has the DesignerSeralizer attribute. This was new to me, but it turns out this is the mechanism that generates the code that appears in a forms's .Designer.cs file. This uses the CodeDom to generate code that inserts the replacement ComponentResourceManager instance.

private void InitializeComponent()
{
    System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(UserControl1));
    this.resourceManagerSetter1 = new Internationalization.Resources.ResourceManagerSetter();
    this.label1 = new System.Windows.Forms.Label();
    this.SuspendLayout();
    Internationalization.Resources.ResourceManagerProvider.GetResourceManager(typeof(UserControl1), out resources);
    // 
    // label1
    // 
    resources.ApplyResources(this.label1, "label1");
    this.label1.Name = "label1";
    // 

Using CodeDom means that the code is generated for the appropriate language automatically. The only requirement is that this component must be the first one added to the form. If you're adding this to an existing form with controls, just go to the Designer.cs file and move the line calls the constructor to the top of the InitializeComponent method. (The order of the calls to the constructors seems to determine the order in which the serialized code generators are called)

You can see from the code sample above that the original instantiation is still there, but is effectively replaced by the call to the GetResmourceManager method.

MSDN has more info on Globalizing Windows Forms.

Tour Down Under 2014

Saturday, 25 January 2014

We made it!

Me and Dad, about to head for the start at UnleyI'm glad to report that unlike last year, my back was behaving itself and yesterday I was able to take part in the Bupa Challenge Tour with my Dad. The Challenge Tour is a chance to ride the same route that the professionals do for one day of the Tour Down Under. Like previous years, we rode as part of the Mud, Sweat & Gears team.


Riders, Looking north up King William RdWe rode the full course of 154km from Unley to Victor Harbor. That's the longest distance I think I've ever ridden, and boy did my feet and my behind know about it – especially the last 30km or so! It took just under 8 hours (7:54 according to the Endomondo app I used on my phone), which included time at the rest stops.

The start of the ride took us up the South-Eastern Freeway through the Heysen Tunnels. Normally cyclists aren't allowed along here, so that was quite a novelty – though we agreed that the tunnels themselves are quite stuffy.

The weather this year was fantastic. It was a little drizzley early in the morning, but then remained comfortably cool for most of the day. It was overcast for most of the morning, which helped a lot. A gentle breeze earlier in the day became a little more blustery towards the end, but not as bad as some years.

We made three stops along the way – at Meadows, Mt Compass and Yankalilla. A chance to refill drink bottles with various colours of Powerade (each stop seemed to have a different colour) and grab a banana and fruit cake to refuel.

This year for the first time they scanned the RFID tags on our bikes as we progressed through the route. You could then log in to a website to obtain the results. Here are mine:

Split TOD Time
Unley 06:42 am 00:12:16
Unley Backup 07:09 am 00:39:30
Stirling 07:53 am 01:23:13
Meadows 09:03 am 02:33:57
Mt Compass 10:30 am 04:00:13
KOM 11:48 am 05:18:17
Yankalilla 01:13 pm 06:43:55
Finish 02:37 pm 08:07:25

Route map from Unley to Victor Harbor

The ride started at 6.30am, but you as can see it took us 12 minutes to pass through the start – not surprising as there were thousands of riders there.

Highlights:

Lowlights

Oddlights:

Team work

After the ride – waiting for the Pros (photo by Fiona)

Keep your focus on the goal

Grimacing Greipel, 150m to go before he won the stage (photo by Fiona)

Special thanks to Narelle's parents Rick and Margaret, who drove us to the start very early in the morning, and then met us at the finish to take me and our bikes home again.

An extraordinary day.

Tuesday, 24 December 2013

"Sad, funny, thoughtful, thankful"

I wrote that on Facebook this afternoon after having returned from Eudunda where I had the honour and privilege of farewelling the father of a dear friend.

Max was a much loved husband, father and grand-father. It was great to see so many people come together today at his funeral to pay their respects and celebrate his life.

I did not know Max that well, but to hear the story of his life today, and see his legacy in his four daughters and their families was both moving and inspiring.

A man of faith and integrity.

It gives you pause to consider that when it's your turn:

Chris Testa-O'Neill on Increasing Business and IT collaboration with SQL Server

Wednesday, 18 December 2013

 

Chris presenting on stage

I was one of the few who braved the 39°C heat outside to hear Chris Testa-O'Neill (@ctesta_oneill) speak at this month's Adelaide SQL Server User Group. Great to have Chris back in Adelaide again.

It proved to be a really interesting presentation about what BI tools are now available, and what tools are appropriate for what problems.

The talk concluded with a nice demonstration of setting up PowerPivot in Excel, highlighting what could be achieved by a business 'power user', and what areas they would require assistance from a BI expert.

We also met in a different room from normal which I think most people thought was a better space. Plus (as you may observe in the photo above), it has a grand piano – I'm sure we could find a use for that somehow!

TFS Best Practice Analyzer failing with "%TFSServerURLValidated%

Saturday, 7 December 2013

I've been having trouble running the Team Foundation Server Best Practise Analyser (TfsBpa for short). It was failing with the following warning:

Cannot validate the URL provided
The scan was generated using the corrected URL "%TFSServerURLValidated%".

I noticed in the "Other Reports" section that it listed the following additional errors:

23:03:07.351: Exception resetting Execution Policy in TFS PowerShell Object Processor:
System.Management.Automation.CmdletInvocationException
Access to the registry key 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell' is denied.
   at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input, Hashtable errorResults, Boolean enumerate)
   at System.Management.Automation.PipelineNode.Execute(Array input, Pipe outputPipe, ArrayList& resultList, ExecutionContext context)
   at System.Management.Automation.StatementListNode.ExecuteStatement(ParseTreeNode statement, Array input, Pipe outputPipe, ArrayList& resultList, ExecutionContext context)
System.Management.Automation
Script

Hmm.. I wonder if it's a problem related to PowerShell's execution policy setting?

I ran the following in an elevated PowerShell prompt:

Set-ExecutionPolicy RemoteSigned

And that's fixed it – now the BPA process runs as expected. Yay!