Wednesday, December 9, 2009

Finding the root of all evil

Recently I was trying to fix a bug on a project I’m currently on, however in trying to fix it I found another 2-3 issues which I deemed more critical so I was slightly side tracked.

After fixing the other problems I returned to the bug I initially wanted to fix, only to find its already been fixed, great! Well it should have been.

The bug in question is when clicking a button on one of our wpf screens. A NullReferenceException was being thrown when attempting to load a dialog. The mischievous piece of code was this:

var startData = new ProductCodesDialog.StartData(hierarchicalCodeType)
{
DefaultSearch = productCodes.Trim().FinderSplit()
};

The problem? productCodes which is a string was null. This is fine and there’s probably nothing wrong with binding to null string values. But this piece of code was relying on the string being empty instead.

So the fix that I found nicely patched into place was the following:

if(string.IsNullOrEmpty(productCodes))
{
productCodes = string.Empty;
}

If something’s Null and we don’t want it to be null, then we just initialise it right? There was another piece of code in the actual textbox control which had something to say about that...

/*
* This fix below is for a strange issue whereby deleting all
* text in the textbox is not updating the source.
*/
if(Text != null && Text.Trim().Length == 0)
Text = null;

So this value is being constantly set to different values because dependant code is scared of throwing exceptions. Let’s take a look at what the value is initialised to so we have a starting point.

private String _value = String.Empty;

So whoever created the base type StringWithMatchType designed it with String.Empty being the starting value, IMO we should probably respect that as a heap of functionality has been built on top of this.

So why is it being set to null in the control? Apparently the bound object is not getting updated when someone selects all the text and deletes it. So let’s see if it’s the case... *a few minutes happened here* and to cut a long story short it does appear to be the case. So the new question becomes:

Why doesn’t my binding update to string.Empty when the textbox text is deleted? After a bit of Googling I found this:

http://jeffhandley.com/archive/2008/07/09/binding-to-nullable-values-in-xaml.aspx

So WPF is being carful not to null a bound property, not the ideal behaviour in this case. Ideally we want a string.Empty but perhaps WPF can’t be sure of what to do. Never-the-less luckily we are using .NET 3.5 SP1, and with that comes a handy binding property by the name of TargetNullValue

http://msdn.microsoft.com/en-us/library/system.windows.data.bindingbase.targetnullvalue.aspx

So while this issue doesn’t affect the code above, as it’s binding to a string and does correctly propagate a string.Empty, it does affect some other places we are using the same custom textbox control. Wherever we are binding to Nullable<T> values seems to be affected, whether WPF doesn’t update the bound object when the textbox is emptied. I can only assume it was for this case the Text property on the custom control was being nulled. Using the TargetNullValue we are able to get around this:

WPFControls:TextBoxWatermark Text="{Binding Path=Value,TargetNullValue={x:Static System:String.Empty},UpdateSourceTrigger=PropertyChanged}"

And when retesting this the Nullable property is correctly being set to null when the textbox is emptied.

A dash of curiosity coupled with an inquisitive nature and a distaste for quick hacks seems to have got this problem resolved in quite a satisfactory manner!

No comments:

Post a Comment