Nasty winforms bug

I've been fighting this bug all day and it annoyes me more with every minute I spend on it. Here's the deal: I have a ListView control and a Textbox control on a winforms form (.NET 1.1). With the Textbox you can edit a field of the object on the current selected row in the ListView. Easy right? I thought so too .

Not all values filled into the Textbox are valid. So I added a handler of the Validating event of the Textbox. When I tab away from the Textbox, the event gets fired and validation kicks in. If validation succeeds, the value is set in the object, otherwise validation fails and focus stays with the Textbox. When the SelectedIndex changes in the ListView and there is still a row selected, I fill the Textbox with the value of the particular field of the object currently being selected. This should work as Validating always should kick in when focus moves away from the Textbox.

But now the misery begins. Question: in which order are the following events fired when I'm editing the text of the Textbox and I click on another row in the ListView control: Textbox.Validating, Textbox.Leave, ListView.SelectedIndexChanged?

Answer: ListView.SelectedIndexChanged, Textbox.Leave, Textbox.Validating.

I'm sorry, but this is semantically wrong. What if validation fails? Focus should never move away from the Textbox before the Validating event. I now have to write some code which I consider a hack to work around this by keeping track of which object was previously selected in the ListView and I can't overwrite the 'currently selected row' when an edit is in progress, however I have to store somewhere that there is now a new selected object so after validation succeeds I have to set the Textbox to a new value.

When events were fired in the right order, this would be all fine. The low level reason for this error is the asynchronous behaviour of winform controls: under the hood they're (most of the time, there are exceptions) Win32 controls which solely respond to Win32 messages. So if you click on the ListView, the control receives a message from Windows, it acts on it and fires an event. Focus moves away from the Textbox after this and this will cause a message to be delivered for the Textbox, which will cause a .NET event. But much too late.

So if you set up a similar setup, be aware of this.

5 Comments

  • Please highlight this in your post "asynchronous behaviour of winform controls". The whole event system suffers from this behavior. We've had similar problems and hacking around those darn events makes the whole even messier.

  • Haha, be glad you don't develop for the Compact Framework. An example:



    Controls in the CF have a validating event (with CancelEventArgs) just like their Winforms brothers. When calling e.Cancel in the eventhandler nothing happens and everything continues like it's business as usual. Why would you want to cancel something in a Validating event anyway? :)

  • You should post this in the WINFORMS Listserv. Lots of MS folks 'listen' and reply. I'd be curious to here their reply.

  • This actually happens when using the win32 api as well. It is just the order that the events get sent through the message pump...

  • Jeff: true, but that is an asynchronous, somewhat lowlevel environment, not the synchronous environment of .NET. I'd expect from .NET that it would be handled properly.

Comments have been disabled for this content.