Sunday, September 25, 2005
« Windows Forms data binding issue in VS 2... | Main | Nice little VB 2005 enhancement »

In my last post I discussed a nasty bug with Windows Forms data binding, along with one possible solution (which is most certainly a hack). This led to some discussion – including a couple people observing that this hack should probably be in the UI layer, not the business layer (which is where I’d put it).

 

In concept I agree with the idea that the hack could belong in the UI layer. However, that is a slippery slope for two reasons.

 

First, you could argue that all data binding support only exists for Windows Forms, since neither Web Forms and certainly Web services utilize any of the interfaces or event models required by data binding. Yet it is quite obvious that data binding requires that the data source itself implement these behaviors in order to work properly. The hack is merely overcoming a bug in the way data binding works, so it naturally fits along with all the other interfaces and eventing that already exist in the data source.

 

Second, it is a hack. One would hope that this is a temporary issue that Microsoft will fix. Remember that this will break all .NET 1.x code that uses data binding where the data source actually manipulates data! It would seem likely that it would be a high priority to fix this issue (we can only hope). Thus it is less important where the hack is placed, than it is how transparent it is and how easily it can be removed in the future when (if) Microsoft fixes this bug.

 

In the case of CSLA .NET 2.0 I can entirely embed the hack into the framework, meaning that removing the hack in the future would be a simple framework change. In the case of custom objects or smart DataTables a well-implemented hack could be removed by changing just a few lines (3-6) of code in each object.

 

But let’s consider the idea of solving this in the UI. The obvious solution in the UI is to add a LostFocus handler for every control and just updating the control at that point – thus directly overcoming the issue at hand. I’m sure there are various other possibilities, but the problem is merely that the value isn’t updated automatically at LostFocus, so it is hard to imagine a more direct or simple solution than to just update it directly.

 

That turns out to be brainless code in every form in the system. It is brainless enough that you could use a little reflection and generically wrap it up into a new base form class so when the form loads it hooks LostFocus for all events, and the event handler loops through all bindings on the control and refreshes any and all values associated with those bindings. This is effectively what data binding does anyway, so we’d be simply replicating what they should be doing for the control.

 

Wrapping this up into a new base form class is the way to go obviously – since it avoids writing this code in every form. However, it does require the use of reflection and forces all forms to inherit from this new base class – which is a PITA.

 

I think it would be a bit more invasive to remove the hack from the UI than from CSLA .NET. However, it may be less invasive to remove the hack from the UI than to remove it from custom objects or smart DataTable objects.

 

So I will likely show the UI hack in the smart DataTable book, and the Timer hack in the Business Objects books (barring some better hacks or a real fix from Microsoft).


Monday, September 26, 2005 11:59:48 AM (Central Standard Time, UTC-06:00)
Databinding and MS Windows has never - I repeat NEVER - worked properly. Therefore, I haven't used it in years and don't think I ever will again. Everytime I try I am dissapointed. Nice umm - hack (cough) though.
Mark Bonafe
Tuesday, September 27, 2005 10:37:41 AM (Central Standard Time, UTC-06:00)
I think I have a working solution made possible by using a custom PropertyDescriptor class that wraps a normal PropertyDescriptor of a BO's property.
The key implementation detail is:

Public Overrides Sub SetValue(ByVal component As Object, ByVal value As Object)
Dim oldValue As Object = GetValue(component)
_pd.SetValue(component, value)
If oldValue Is Nothing OrElse Not oldValue.Equals(value) Then
OnValueChanged(component, EventArgs.Empty)
End If
End Sub

Of course the BO now has to implement ICustomTypeDescriptor, but this can be put up into a base class. Since most of the implementation is delegated to TypeDescriptor's static methods. We only need to write our own code to return the custom PropertyDescriptor.

Public Function GetProperties() As System.ComponentModel.PropertyDescriptorCollection Implements System.ComponentModel.ICustomTypeDescriptor.GetProperties
Dim col As PropertyDescriptorCollection = TypeDescriptor.GetProperties(GetType(Person))
Dim props(col.Count) As PropertyDescriptor

For i As Integer = 0 To col.Count - 1
props(i) = New NotifyPropertyDescriptor(col(i))
Next

Return New PropertyDescriptorCollection(props)
End Function

So my BO's property now looks like this:
Public Property Name() As String
Get
Return _name
End Get
Set(ByVal value As String)
_name = value.ToUpper
End Set
End Property
The PropertyDescriptor takes care of the change notification event. A real BO would have more code in the setter, but just for simple WinForms data binding I see little reason to have to type loads more code. It's nice to let the PropertyDescriptor deal with all that.

If you want to know more drop me an email and I'll send over the source.
Wednesday, September 28, 2005 6:12:44 AM (Central Standard Time, UTC-06:00)
Rocky, in thinking more of your hack, I realized that this hack would preclude people (without changing it in some way or ripping it out) from using bindings where they call a method similar to BindField from your 1.0 framework where they are using a DataSourceUpdateMode of OnPropertyChanged. Since Petar's Active Objects actually mimicked this behaviour and several people on the forum seem to use that, I suspect many people will want to do this. With the forementioned hack in place it would cause problems whereby the framework would cause the current field to get updated on every keystroke which would be problematic.

Also, in response to Mark above, I've been one of the biggest naysayers of windows databinding for a very long time; every time it has been a big Microsoft marketing campaign and the developer every time has gotten the short end of the stick... even in 2002 and 2003 I believe MSFT fell way short, sure to do simple things it was simple, but toss in something a little more complex and it became complex (boy this sounds like an argument about VB... doh! I'm a VB guy : )... In 2005 they have FIXED 99% of everything that was every wrong with databinding and truly have a FANTASTIC feature! BIG time saver, way less code to write... and IT WORKS! So I would encourage you to take one more look at it...

Shoddy.
Friday, January 13, 2006 1:05:55 PM (Central Standard Time, UTC-06:00)
Sir Rocky,

I'm not sure if I will be making sense and if I understand the point exactly. In my experience in CSLA .NET 1.51 using Visual Studio 2003, I noticed that if I have a textbox entry shall we say Customer Name and try to edit the field and click Save button. It doesn't accept the changes made in the Customer Name field Event the textbox is binded with the equivalent BO property.

What I did, is to call Me.Validate() and it solves the problem.
Where:
Me is the name of the form.
------


Glenn Santos
Comments are closed.