Todays productivity tip for Winforms developers. This may work in other UI technologies, but I haven't checked. Here's the 10 second way of resetting the tab order on your control without resorting to manually changing the property on every control.
1. Open your form/control in the Visual Studio designer
2. Go to View->Tab Order
3. The current tab orders are now highlighted for each control.
4. Proceed to click on each control in the order you want them to tab.
5. Done.
If only I could roll back a decade knowing this, I wouldn't drink as much as I do today!!
There's now no excuse for poor tab orders, no matter how tight the schedule.
Thursday, 17 December 2009
Thursday, 19 November 2009
Linux 2009
Every year or so I give linux another go. I've spent untold hours over the last decade or so using it, and even had it as my main OS for a while back in the Redhat 6.2 days, but I've never been totally comfortable with it.
Still, variety is the spice of life, and my netbook (Lenovo S10) needed a rebuild, so I thought I'd give the new optimised Ubuntu Netbook Remix a go. This is my story.
Bonus points for being able to find the download link from the official site.
After the included Bootable USB tool on the .iso failed to display my selected ISO, I ended up using the also-referenced Unetbootin tool to copy and make bootable my USB stick. Except it didn't work. No errors reported, but it failed to boot (Command manager not found).
Trial and error led me to format my USB as Fat32 rather than NTFS, when Unetbootin still ran without error, but this time booted correctly and like a dream, up comes UNR.
I have to say the polish and attention to detail has improved leaps and bounds in the last few years, and the default display is very usable.
The bottom row of game icons is only half displayed, but you can't have everything.
Oh, and support for my wireless card isn't enabled by default, but fortunately the drivers are included, and you're prompted that they may be of use. I haven't yet been able to get them installed. The machine has either hung while restarting after applying the driver (I thought only windows had to restart after driver installs), or hung prior to the drivers being activated.
But generally, it ran well enough off the USB stick for me to blow everything away and install it on the HDD. The install went flawlessly, and is certainly a nicer experience than older distros I've tried.
I moved back to my main desk to use the wired internet connection in an attempt to get wireless drivers downloaded. USB keyboard/mouse worked perfectly. 2nd monitor was detected and configured correctly, but rendered ugly white stripes outside the inbuilt monitor res of 1024*600, and failed to respond to any mouse clicks other than a restart. After a restart the menus render perfectly, and everything is fine and dandy in monitor land.
Except apps only open on the leftmost monitor (my large one) which is good, but can't seem to be moved to the smaller monitor (note, just found unmaximise tucked away in a context menu), which also has the taskbar etc on it, so swapping windows involves a mouse trip to tiny font land on the netbook. Admittedly you could argue a netbook focussed distro may not have the nicest support for dual monitors, but then I'd think most people with a little netbook like this would plug into proper peripherals as often as possible.
Firefox runs fine, albeit it pretty slowly compared to windows. Gmail works fine, visit Youtube and get prompted to install Flash. Fair enough.
Adobe site suggests four ways of getting flash, one (.deb) marked for Ubuntu 8+, download, open with default application... apparently i don't have libnspr4-dev, whatever that is. Oh well, at least I won't get spammed with flash ads.... or youtube vids.
There's very nice, easy access to a stack of software via the Ubuntu Software Centre. Everything I've looked at so far says it's "not available in the current data", and can't be installed.
I dunno, I keep giving linux it's chance, to the point where a fairly pro-linux flatmate of mine throw up his hands trying to get a distro to work on my old cobbled together PC. But I'd really hoped that on stock hardware, with a a targeted release things would be better than this by now. I want linux to be fantastic, I want to have another option than Windows, but I can't see this install lasting any longer than a week.
It's just not ready yet.
Still, variety is the spice of life, and my netbook (Lenovo S10) needed a rebuild, so I thought I'd give the new optimised Ubuntu Netbook Remix a go. This is my story.
Bonus points for being able to find the download link from the official site.
After the included Bootable USB tool on the .iso failed to display my selected ISO, I ended up using the also-referenced Unetbootin tool to copy and make bootable my USB stick. Except it didn't work. No errors reported, but it failed to boot (Command manager not found).
Trial and error led me to format my USB as Fat32 rather than NTFS, when Unetbootin still ran without error, but this time booted correctly and like a dream, up comes UNR.
I have to say the polish and attention to detail has improved leaps and bounds in the last few years, and the default display is very usable.
The bottom row of game icons is only half displayed, but you can't have everything.
Oh, and support for my wireless card isn't enabled by default, but fortunately the drivers are included, and you're prompted that they may be of use. I haven't yet been able to get them installed. The machine has either hung while restarting after applying the driver (I thought only windows had to restart after driver installs), or hung prior to the drivers being activated.
But generally, it ran well enough off the USB stick for me to blow everything away and install it on the HDD. The install went flawlessly, and is certainly a nicer experience than older distros I've tried.
I moved back to my main desk to use the wired internet connection in an attempt to get wireless drivers downloaded. USB keyboard/mouse worked perfectly. 2nd monitor was detected and configured correctly, but rendered ugly white stripes outside the inbuilt monitor res of 1024*600, and failed to respond to any mouse clicks other than a restart. After a restart the menus render perfectly, and everything is fine and dandy in monitor land.
Except apps only open on the leftmost monitor (my large one) which is good, but can't seem to be moved to the smaller monitor (note, just found unmaximise tucked away in a context menu), which also has the taskbar etc on it, so swapping windows involves a mouse trip to tiny font land on the netbook. Admittedly you could argue a netbook focussed distro may not have the nicest support for dual monitors, but then I'd think most people with a little netbook like this would plug into proper peripherals as often as possible.
Firefox runs fine, albeit it pretty slowly compared to windows. Gmail works fine, visit Youtube and get prompted to install Flash. Fair enough.
Adobe site suggests four ways of getting flash, one (.deb) marked for Ubuntu 8+, download, open with default application... apparently i don't have libnspr4-dev, whatever that is. Oh well, at least I won't get spammed with flash ads.... or youtube vids.
There's very nice, easy access to a stack of software via the Ubuntu Software Centre. Everything I've looked at so far says it's "not available in the current data", and can't be installed.
I dunno, I keep giving linux it's chance, to the point where a fairly pro-linux flatmate of mine throw up his hands trying to get a distro to work on my old cobbled together PC. But I'd really hoped that on stock hardware, with a a targeted release things would be better than this by now. I want linux to be fantastic, I want to have another option than Windows, but I can't see this install lasting any longer than a week.
It's just not ready yet.
Thursday, 30 July 2009
Perspective
From the Supersite blog:
Given I've never met anyone with a Zune, that really puts the number of Mac fan boys (I jest) into perspective!
In a survey conducted last fall, IDC's Kevorkian said only 4.8% of those with a portable media player reported having a Zune, while 61% had some sort of iPod.
So, in late 2008, the Zune actually had 50 percent more usage share in the MP3 player market than the Mac did in the worldwide PC market.Given I've never met anyone with a Zune, that really puts the number of Mac fan boys (I jest) into perspective!
Wednesday, 15 July 2009
.NET 4 Data Annotations
I'm confined to quarters for a week or so, so I figured it's about time I had a play with the new RIA services stuff for Silverlight, and in a lot of ways it feels like it's really close to something really awesome, but unfortunately not quite there yet. Bear in mind I'm looking at it as a replacement to building ASP.Net web apps for internal line-of-business users.
Some Good Things:
End-user feel is pretty nice out of the box
Isn't tied to any underlying data/object technology... I demo'd against an Entity Framework, and also POCO + sprocs
Whilst it can be hard trying to work out how it all hangs together (because of the code gen involved there's a bit of magic to wade through) I don't think it'll take too long to get a feel for it.
Unfortunately it seems to fall down as soon as you step beyond the trivial samples done as online tutorials, and into the real-world of writing enterprise apps. The rest of this post is going to revolve around complex validation. And not crazy 'I need to call a web service to determine if this is valid at this point in time' type complex validation... something really pretty simple in business apps - a conditionally mandatory field.
Imagine a text field, which is required to be entered if and only if a flag is set on another property... it can be optionally populated whenever you want, but if the flag is set, it better be there.
public bool IsFunkyRequired{get;set;}
public string Funky {get;set;}
As the new DataAnnotations suite coming in .Net 4 is attribute based, it suffers the same problems as the Entlib Validation block (and the original Avanade library): because the validation is based on a single property via the attribute, any sort of cross-property validation is surprisingly difficult.
Funky can't get marked [Required()], because it's not required all the time.
Adding logic of the form
if (String.IsNullOrEmpty(Funky) && IsFunkyRequired) { throw new ValidationException(blah blah); }
into the property setters causes issues when initially loading data from the database (eg, when the flag is set but the text field hasn't yet been loaded), and doesn't really fit in with the whole attribute based way of doing things.
The next logical step is to write your own validator attribute, by (funnily enough) inheriting from ValidationAttribute. After realising that the obvious IsValid(object value) method is not sufficient (as it is only useful if you care about the value set in the property, which isn't enough in this case), and fighting with the wonder that is the ValidationResult class, you eventually reach the point where you have a reasonably concise validation method, as follows.
And this surprisingly works really well, no data is committed to the database if the object is in an invalid state.... of course you don't get any error messages raised either.
Spelunking into the genned code shows that our custom attribute hasn't been applied to the genned Entity class for the client side, hence no client-side validation when tabbing off fields.
Luckily Guy Burtstein has some older info showing some ways around this. Note that the [Shared] attribute doesn't seem to exist any more, you just rely on a well named .shared.vb or .shared.cs file name.
At this point, we have a custom client+server validator that runs quite happily.
Except it doesn't work.
You see validation is applied prior to the property being set, so if we clear the Funky field on an entity the validator returns success, as the property is still set... this applies when adding data to a previously invalid item: the error can't be cleared.
Changing the validator to operate on the passed value (ie, the new value we're attempting to set) only works if we handle each property type it's applied to, for example the following works:
Starting with the flag and field both populated, you clear the field, adding an error to the Funky control. You then clear the check box, placing the object in a valid state, but because the error is on a separate field, the old error remains in place on the screen. There isn't a tester known to man that would let me slip that mickey to the users.
Better yet, because the change to clear the textbox was considered an invalid move, it never really took place. The Validator.ValidateProperty method called by the genned Entity throws a ValidationException if any validation on that property fails, meaning the property setter is never called (validation occurs before the setter is called remember).
This all means when you submit changes despite there being an error on the screen, the checkbox is cleared, but the textbox still has it's original value in it.
[Side Rant]
Why or why are we throwing exceptions for what is obviously a very common path through the system? It fails both of Rico's almost rules regarding exceptions.
[/Side Rant]
I've only been playing with this for a day or so, so it's entirely possible I'm missing some really obvious stuff that would make all these go away, and to be fair it's not at a go-live stage yet, but I'm really worried this is taking us down a path we don't want to be on.
These issues all seem to stem from two basic premises (and I'm not sure which started the other, or if they were unrelated):
1. Validation is a per-property behaviour, as opposed to per-object behaviour
2. Entities are not allowed to enter an invalid state, rather than not being allowed to be saved in an invalid state
I don't have any solution to this yet, but I suspect the CSLA Light stuff might be able to be jury-rigged in here somehow.
Some Good Things:
End-user feel is pretty nice out of the box
Isn't tied to any underlying data/object technology... I demo'd against an Entity Framework, and also POCO + sprocs
Whilst it can be hard trying to work out how it all hangs together (because of the code gen involved there's a bit of magic to wade through) I don't think it'll take too long to get a feel for it.
Unfortunately it seems to fall down as soon as you step beyond the trivial samples done as online tutorials, and into the real-world of writing enterprise apps. The rest of this post is going to revolve around complex validation. And not crazy 'I need to call a web service to determine if this is valid at this point in time' type complex validation... something really pretty simple in business apps - a conditionally mandatory field.
Imagine a text field, which is required to be entered if and only if a flag is set on another property... it can be optionally populated whenever you want, but if the flag is set, it better be there.
public bool IsFunkyRequired{get;set;}
public string Funky {get;set;}
As the new DataAnnotations suite coming in .Net 4 is attribute based, it suffers the same problems as the Entlib Validation block (and the original Avanade library): because the validation is based on a single property via the attribute, any sort of cross-property validation is surprisingly difficult.
Funky can't get marked [Required()], because it's not required all the time.
Adding logic of the form
if (String.IsNullOrEmpty(Funky) && IsFunkyRequired) { throw new ValidationException(blah blah); }
into the property setters causes issues when initially loading data from the database (eg, when the flag is set but the text field hasn't yet been loaded), and doesn't really fit in with the whole attribute based way of doing things.
The next logical step is to write your own validator attribute, by (funnily enough) inheriting from ValidationAttribute. After realising that the obvious IsValid(object value) method is not sufficient (as it is only useful if you care about the value set in the property, which isn't enough in this case), and fighting with the wonder that is the ValidationResult class, you eventually reach the point where you have a reasonably concise validation method, as follows.
Forgive the jump to VB, I'm trying to be equal opportunist here.
Public Class FunkyRequiredAttribute
Inherits ValidationAttribute
Protected Overrides Function IsValid(ByVal value As Object, ByVal validationContext As System.ComponentModel.DataAnnotations.ValidationContext) As System.ComponentModel.DataAnnotations.ValidationResult
If (TypeOf (validationContext.ObjectInstance) Is TestClass) Then
Dim obj As Class1 = DirectCast(validationContext.ObjectInstance, TestClass)
If (String.IsNullOrEmpty(obj.Funky) AndAlso obj.IsFunkyRequired) Then
Return New ValidationResult("Funky must be populated when flag is set.")
End If
End If
Return ValidationResult.Success
End Function
End Class
And this surprisingly works really well, no data is committed to the database if the object is in an invalid state.... of course you don't get any error messages raised either.
Spelunking into the genned code shows that our custom attribute hasn't been applied to the genned Entity class for the client side, hence no client-side validation when tabbing off fields.
Luckily Guy Burtstein has some older info showing some ways around this. Note that the [Shared] attribute doesn't seem to exist any more, you just rely on a well named .shared.vb or .shared.cs file name.
At this point, we have a custom client+server validator that runs quite happily.
Except it doesn't work.
You see validation is applied prior to the property being set, so if we clear the Funky field on an entity the validator returns success, as the property is still set... this applies when adding data to a previously invalid item: the error can't be cleared.
Changing the validator to operate on the passed value (ie, the new value we're attempting to set) only works if we handle each property type it's applied to, for example the following works:
Except it doesn't, really. Because this is considered two separate validations by the system (because it applies to two different properties), you have piss-poor usability.
Protected Overrides Function IsValid(ByVal value As Object, ByVal validationContext As System.ComponentModel.DataAnnotations.ValidationContext) As System.ComponentModel.DataAnnotations.ValidationResult
If (TypeOf (validationContext.ObjectInstance) Is TestClass) Then
Dim obj As TestClass = DirectCast(validationContext.ObjectInstance, TestClass)
If (TypeOf (value) Is String) Then
'changed the Funky field
Dim s As String = DirectCast(value, String)
If (String.IsNullOrEmpty(s) AndAlso obj.IsFunkyRequired) Then
Return New ValidationResult("Funky must be populated when flag is set.")
End If
ElseIf (TypeOf (value) Is Boolean) Then
'changed the flag
Dim b As Boolean = DirectCast(value, Boolean)
If (String.IsNullOrEmpty(obj.Funky) AndAlso b = True) Then
Return New ValidationResult("Funky must be populated when flag is set.")
End If
End If
End If
Return ValidationResult.Success
End Function
Starting with the flag and field both populated, you clear the field, adding an error to the Funky control. You then clear the check box, placing the object in a valid state, but because the error is on a separate field, the old error remains in place on the screen. There isn't a tester known to man that would let me slip that mickey to the users.
Better yet, because the change to clear the textbox was considered an invalid move, it never really took place. The Validator.ValidateProperty method called by the genned Entity throws a ValidationException if any validation on that property fails, meaning the property setter is never called (validation occurs before the setter is called remember).
This all means when you submit changes despite there being an error on the screen, the checkbox is cleared, but the textbox still has it's original value in it.
[Side Rant]
Why or why are we throwing exceptions for what is obviously a very common path through the system? It fails both of Rico's almost rules regarding exceptions.
[/Side Rant]
I've only been playing with this for a day or so, so it's entirely possible I'm missing some really obvious stuff that would make all these go away, and to be fair it's not at a go-live stage yet, but I'm really worried this is taking us down a path we don't want to be on.
These issues all seem to stem from two basic premises (and I'm not sure which started the other, or if they were unrelated):
1. Validation is a per-property behaviour, as opposed to per-object behaviour
2. Entities are not allowed to enter an invalid state, rather than not being allowed to be saved in an invalid state
I don't have any solution to this yet, but I suspect the CSLA Light stuff might be able to be jury-rigged in here somehow.
Wednesday, 3 June 2009
How to cause consternation in the ranks #134
Ask for an item of information from the Disaster Recovery Plan for your core system.
Monday, 6 April 2009
Another viewpoint on insanity
A workplace conversation regarding a project with a large lack of process:
Anon: "This project meets the defination of insanity, they keep doing the same thing over and over again expecting it to get better."
Me (in a flash of brilliance): "It's only insane if the process is deterministic."
What would you prefer, a structured automated process that will never work, or an ad-hoc manual process that consistently produces inconsistent results??
Maybe we could use this randomness with an evolutionary learning algorithm to get a working process?
I need more coffee.
Anon: "This project meets the defination of insanity, they keep doing the same thing over and over again expecting it to get better."
Me (in a flash of brilliance): "It's only insane if the process is deterministic."
What would you prefer, a structured automated process that will never work, or an ad-hoc manual process that consistently produces inconsistent results??
Maybe we could use this randomness with an evolutionary learning algorithm to get a working process?
I need more coffee.
Wednesday, 4 March 2009
Fiddler has been de-throned
Anyone that has worked with me on a web project knows my love of Fiddler, and yesterday I ran across a new tool, Hawkeye.
It's a runtime read-write debugger for .Net windows controls, allowing you to attach it to any .Net winforms control on a running application, and sneak a peek at it's innards, properties, fields, methods, events etc.
Not only that, you can change those properties (perfect for altering someones copy of an internal app to have personalised labels, April 1st is coming people!), invoke the methods etc.
Aaand, if you have Reflector running, it provides a nice hook to view the code for these objects without rummaging through the normal class hierarchy in Reflector.
Of course I'm still on a web project where Hawkeye is less than useless, so Fiddler has to stay close to my heart for now.
It's a runtime read-write debugger for .Net windows controls, allowing you to attach it to any .Net winforms control on a running application, and sneak a peek at it's innards, properties, fields, methods, events etc.
Not only that, you can change those properties (perfect for altering someones copy of an internal app to have personalised labels, April 1st is coming people!), invoke the methods etc.
Aaand, if you have Reflector running, it provides a nice hook to view the code for these objects without rummaging through the normal class hierarchy in Reflector.
Of course I'm still on a web project where Hawkeye is less than useless, so Fiddler has to stay close to my heart for now.
Thursday, 5 February 2009
Terminal Services Tip 'o' The Day
Hands up everyone who loves Terminal Services (Remote Desktop).
Keep your hand up if you love seeing the message "The terminal server has exceeded the maximum number of allowed connections" because windows only gives you 2 by default.
I don't expect many hands to be up here!
If you pass the /console switch to mstsc though, it'll use the actual console (dude at the keyboard) session to do it's magic, giving you a way in to go and boot all the other server-hogging RDP sessions off the server.
Don't thank me, thank mstsc!
Keep your hand up if you love seeing the message "The terminal server has exceeded the maximum number of allowed connections" because windows only gives you 2 by default.
I don't expect many hands to be up here!
If you pass the /console switch to mstsc though, it'll use the actual console (dude at the keyboard) session to do it's magic, giving you a way in to go and boot all the other server-hogging RDP sessions off the server.
Don't thank me, thank mstsc!
Friday, 23 January 2009
Visual Studio Debugging Tip
I recently acquired a copy of "Debugging Microsoft .NET 2.0 Applications" by John Robbins.
Debbuging .Net apps is effectively what I do every day, and have done for several years. I consider myself fairly proficient at it (compared to most people around me, I have no illusions that I'm just a babe in swaddling clothes compared to the true gurus).
I bought the book spur of the moment, and almost regretted it between when the money went out of my account, and I started reading. Never fear, it's a fantastic read, full of tips and information brand new to me. Not just "Oh yeah, I've seen that tip before but never use it, maybe I should start", but brand new, hardcore debugging info. Today's post is part book recommendation, and part actual tip to convince you to buy a copy:
Make Object ID
Scenario: You're working in an ASP.Net application, pages have controls, controls have subcontrols etc, and somewhere along way the value of a variable is going haywire.
You add a watch on this.Foo within the control, and it displays the value fine, but as soon as you step back up the control stack, your watch is worthless.... wouldn't be fantastic if we could keep that watch valid without having to add watches to this.Control.Foo, this.SubControl.Control.Foo, this.Parent.Control.Foo etc depending on where you are in the control hierarchy???
Welcome to Make Object ID!
If you right-click on your watch when it's in a valid state (ie, displaying it's value), and choose Make Object ID from the context menu, you should see a little '{1#}' appear next to the value. This is this object's unique id within the debugger, available for you to use in different tool windows.
If you add a watch on the variable '1#', you should see the same value as the original variable (because it is the original variable). If you step back up (or down) the control hierarchy though, you'll see the watch remains valid, allowing you to monitor the variable from anywhere in the codebase...
Have fun!
Wokket
Debbuging .Net apps is effectively what I do every day, and have done for several years. I consider myself fairly proficient at it (compared to most people around me, I have no illusions that I'm just a babe in swaddling clothes compared to the true gurus).
I bought the book spur of the moment, and almost regretted it between when the money went out of my account, and I started reading. Never fear, it's a fantastic read, full of tips and information brand new to me. Not just "Oh yeah, I've seen that tip before but never use it, maybe I should start", but brand new, hardcore debugging info. Today's post is part book recommendation, and part actual tip to convince you to buy a copy:
Make Object ID
Scenario: You're working in an ASP.Net application, pages have controls, controls have subcontrols etc, and somewhere along way the value of a variable is going haywire.
You add a watch on this.Foo within the control, and it displays the value fine, but as soon as you step back up the control stack, your watch is worthless.... wouldn't be fantastic if we could keep that watch valid without having to add watches to this.Control.Foo, this.SubControl.Control.Foo, this.Parent.Control.Foo etc depending on where you are in the control hierarchy???
Welcome to Make Object ID!
If you right-click on your watch when it's in a valid state (ie, displaying it's value), and choose Make Object ID from the context menu, you should see a little '{1#}' appear next to the value. This is this object's unique id within the debugger, available for you to use in different tool windows.
If you add a watch on the variable '1#', you should see the same value as the original variable (because it is the original variable). If you step back up (or down) the control hierarchy though, you'll see the watch remains valid, allowing you to monitor the variable from anywhere in the codebase...
Have fun!
Wokket
Thursday, 8 January 2009
DateTimes and the end of the Earth
Those of you that have worked with me know I tend to say "If your code involves anything to do
with Date/Time manipulation, review it thoroughly, because it almost certainly contains a bug".
I can't remember where I got the original premise for that, it was an article or something years ago (I certainly couldn't come up with something that succinct on my own), but K Scott Allen has a ripper post up today confirming the sense in a detailed reviews of date time work.
Rollover bugs due to missing leap seconds involving HE munitions, who wouldn't want to read about that?!?!
with Date/Time manipulation, review it thoroughly, because it almost certainly contains a bug".
I can't remember where I got the original premise for that, it was an article or something years ago (I certainly couldn't come up with something that succinct on my own), but K Scott Allen has a ripper post up today confirming the sense in a detailed reviews of date time work.
Rollover bugs due to missing leap seconds involving HE munitions, who wouldn't want to read about that?!?!
Subscribe to:
Posts (Atom)