Rockford Lhotka's Blog

Home | Lhotka.net | CSLA .NET

 Wednesday, September 28, 2005

This is the conclusion (for now) of the issue with Windows Forms data binding that I discussed in an earlier entry.

 

To recap, the issue occurs in detail forms built using data binding. The current control’s value isn’t refreshed when the user tabs off the control. If the data source (business object or smart DataTable with a partial class) changed the value due to business logic that change is not reflected in the UI, even though the changed value is in the data source.

 

In talking to the data team at Microsoft, it turns out that this is a bug and will likely be fixed in some future service pack. At this late stage of the game however, it won’t be fixed for release of VS 2005.

 

Fortunately they were able to come up with a decent workaround in the meantime. The workaround is done in the UI and involves hooking an event from each Binding object, then in the event handler forcing the current value to be refreshed from the data source.

 

To set this up, add the following code to your form:

 

  Private Sub HookBindings()

 

    For Each ctl As Control In Me.Controls

      For Each b As Binding In ctl.DataBindings

        AddHandler b.BindingComplete, AddressOf ReadBinding

      Next

    Next

 

  End Sub

 

  Private Sub ReadBinding(ByVal sender As Object, _

    ByVal e As BindingCompleteEventArgs)

 

    e.Binding.ReadValue()

 

  End Sub

 

Then just make sure to call HookBindings in your form’s Load event or constructor. Ideally this is the kind of thing you’d do in a base form class, then make all your normal forms just inherit from that base so there’s no extra code in each actual form.

 

The HookBindings method loops through all controls on the form, and all Binding objects for each control. Every Binding object’s BindingComplete event is hooked to the ReadBinding method – making it the event handler for all Binding objects.

 

The ReadBinding method handles all BindingComplete events for the form. Any time that event is raised this method merely forces data binding to read the current value from the data source and refresh the control.

 

Since BindingComplete is raised after the user has tabbed off a control and the binding is “complete”, this refresh of the display value ensures that the current control does actually contain the value from the data source even if the data source changed the value.

Wednesday, September 28, 2005 11:53:53 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  | 

Here’s a helpful little feature of VB 2005. The following is valid code:

 

    <NotUndoable()> _

    <NonSerialized()> _

    Private mParent As Core.IEditableCollection

 

Notice that both attributes are independent – no need to use the harder syntax from 2003:

 

    <NotUndoable(), NonSerialized()> _

    Private mParent As Core.IEditableCollection

 

Very nice little nugget!

Wednesday, September 28, 2005 11:14:22 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  | 
 Sunday, September 25, 2005

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).

Sunday, September 25, 2005 8:50:02 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  | 
 Friday, September 23, 2005

 

I recently discovered an issue with Windows Forms data binding when a form is bound against a smart data container like a DataTable with partial class code or a CSLA .NET –style object. The issue is pretty subtle, but nasty – and my current workaround doesn’t thrill me. So after reading this if you have a better work around I’m all ears – otherwise this could end up in the books I’m current writing, since both are impacted… :(

 

So here’s the problem:

 

Create a smart data source – defined as one that includes business logic such as validation and manipulation of data. In this case, it is the manipulation part that’s the issue. For instance, maybe you have a business rule that a given text field must always be upper case, and so you properly implement this behavior in your business object or partial class of your DataTable to keep that business logic out of the UI.

 

In a business object for instance, this code would be in the property set block – perhaps something like this in a CSLA .NET 2.0 style class:

 

  Public Property LastName() As String

    Get

      VerifyGetProperty("LastName")

      Return mLastName

    End Get

    Set(ByVal value As String)

      VerifySetProperty("LastName")

      If mLastName <> value Then

        mLastName = value.ToUpper

        PropertyHasChanged("LastName")

      End If

    End Set

  End Property

 

(in CSLA .NET 2.0 the PropertyHasChanged method raises the appropriate PropertyChanged event via the INotifyPropertyChanged interface and also calls MarkDirty)

 

Or a simpler class that implements INotifyPropertyChanged:

 

  Public Property LastName() As String

    Get

      Return mLastName

    End Get

    Set(ByVal value As String)

      mLastName = value.ToUpper

      RaiseEvent PropertyChanged(Me, _

        New PropertyChangedEventArgs("LastName"))

    End Set

  End Property

 

Specifically notice that the inbound value is changed to upper case with a ToUpper call in the set block.

 

Now add that data container to the Data Sources window and drag it onto your form in Details mode. Visual Studio nicely creates a set of controls reflecting your properties/columns (damn I love this feature!!) and sets up all the data binding for you. So far so good.

 

Now run the application and enter a lower case value into the Last Name text box and tab off that control. Data binding automatically puts the new value into the object and runs the code in the set block. This means the object now contains an upper case version of the value you entered.

 

Notice that the TextBox control does not reflect the upper case value. The value from the object was never refreshed in the control.

 

Now change another value in a different control and tab out. Notice that the Last Name TextBox control is now updated with the upper case value.

 

So that’s the problem. Data binding updates all controls on a form when a PropertyChanged event is raised, except the current control. You can put a breakpoint in the property get block and you’ll see that the value isn’t retrieved until some other control triggers the data binding refresh.

 

I did report this bug to Microsoft, but it has been marked as postponed - meaning that it won't get fixed for release. An unfortunate side-issue is that this issue makes data binding in 2.0 work differently than in .NET 1.x, so any .NET 1.x code that binds against a smart data container like a business object will likely quit working right under 2.0.

 

Now to my workaround (of which I’m not overly proud, but which does work). My friend Ed Ferron should get a serious kick out of this – feel free to laugh all you’d like Ed!

 

The problem is that data binding isn’t refreshing whatever control is currently being edited when the PropertyChanged event is raised. The obvious question then is how to get a PropertyChanged event raised after the property set block has completed. Because at that point data binding will actually refresh the control.

 

The easiest way to get some action to occur “asynchronously” without actually using multi-threading (which would be serious overkill) is to use the System.Windows.Forms.Timer control. That control runs on your current thread, but provides a simulation of asynchronous behavior. (On the VAX this was called an asynchronous trap, so the concept has been around for a while!)

 

Putting the Timer control into your data object is problematic. The Timer control implements IDisposable, so your object would also need to implement IDisposable – and then any objects using your object would need to implement IDisposable. It gets seriously messy.

 

Additionally, a real application may have dozens or hundreds of objects. They can’t each have a timer – that’d be nuts! And the OS would run out of resources of course.

 

But there’s a solution. Use a shared Timer control in a central location. Like this one:

 

Public Class Notifier

 

#Region " Request class "

 

  Private Class Request

 

    Public Method As Notify

    Public PropertyName As String

 

    Public Sub New(ByVal method As Notify, _

      ByVal propertyName As String)

 

      Me.Method = method

      Me.PropertyName = propertyName

 

    End Sub

 

  End Class

 

#End Region

 

  Public Delegate Sub Notify(ByVal state As String)

 

  Private Shared mObjects As New Queue(Of Request)

  Private Shared WithEvents mTimer As New System.Windows.Forms.Timer

 

  Public Shared Sub RequestNotification(ByVal method As Notify, _

    ByVal propertyName As String)

 

    mObjects.Enqueue(New Request(method, propertyName))

    mTimer.Enabled = True

 

  End Sub

 

  Shared Sub New()

 

    mTimer.Enabled = False

    mTimer.Interval = 1

 

  End Sub

 

  Private Shared Sub mTimer_Tick(ByVal sender As Object, _

    ByVal e As System.EventArgs) Handles mTimer.Tick

 

    mTimer.Enabled = False

    While mObjects.Count > 0

      Dim request As Request = mObjects.Dequeue

      request.Method.Invoke(request.PropertyName)

    End While

 

  End Sub

 

End Class

 

This could alternately be implemented as a singleton object, but the usage syntax for this is quite nice.

 

This Notifier class really implements the RequestNotification method, allowing an object to request that the Notifier call the object back in 1 tick of the clock (about 100 nanoseconds) on a specific method.

 

The reason the class maintains a list of objects to notify is because even in a single threaded application you can easily envision a scenario where multiple notification requests are registered within 100 nanoseconds – all you need is programmatic loading of data into an object.

 

Now your data object must implement a method matching the Notify delegate and register for the callback. For instance, here’s the updated code from above:

 

  Public Property LastName() As String

    Get

      Return mLastName

    End Get

    Set(ByVal value As String)

      mLastName = value.ToUpper

      Notifier.RequestNotification(AddressOf Notify, "LastName")

    End Set

  End Property

 

  Public Sub Notify(ByVal propertyName As String)

    RaiseEvent PropertyChanged(Me, _

     New PropertyChangedEventArgs(propertyName))

  End Sub

 

Rather than raising the event directly, the property set block now asks the Notifier to call the Notify method in 1 tick. So 1 tick later – after the property set block is complete and data binding has moved on – the Notify method is called and the appropriate PropertyChanged event is raised.

 

With this change (hack) data binding will now refresh the Last Name TextBox control as you tab out of it. Though there’s a 100 nanosecond delay in there, it isn’t something the user can actually see and so you can argue it doesn’t matter.

 

I can hear Ed laughing already…

Friday, September 23, 2005 5:46:01 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  | 

You know how Microsoft collects all that information every time an application or the OS crashes? It is a cool idea and very valuable. You may have noticed that video drivers don't crash systems nearly as much as they did just a couple years ago. Wonder why? Because all that feedback has allowed the card vendors to make higher quality drivers.

You may have wished to implement something like this in your applications too. It may be possible to do it through Microsoft's mechanism, I honestly don't know - but I expect it wouldn't be easy or free. http://www.exceptioncollection.com appears to be one attempt to provide such a service on the web for free. I haven't used it, so I can't speak for how well it works, but I gotta say that the concept sounds quite attractive for some types of application.

Friday, September 23, 2005 4:02:22 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  | 
 Monday, September 19, 2005

Upon returning from the Microsoft PDC conference I found that my laptop could no longer access the nodes on my LAN. I could ping machines by IP address, but local name resolution was not working. After much discussion and running many network commands as suggested by various colleagues, my co-workers at Magenic hit upon the answer.

Apparently it is possible for your machine's network node type to be switched by any DHCP server. My DHCP server doesn't set the node type at all, but the one at PDC apparently set it to Peer-to-Peer, which I'm told forces your machine to only use a WINS server for name resolution on a LAN. I don't have a WINS server, so all name resolution was shot to hell when I got back from PDC.

The solution was to change my node type from Peer-to-Peer to almost any other value: b-node, Hybrid or Unknown are all good. Since there's no end user tool to do this, regedit is your friend as noted in this blog entry.

Situations like these remind me why I'm a software and not an infrastructure person. I find troubleshooting and solving this stuff to be frustrating and not at all fun... It is probably an aftertaste from my brief stint (18 months or so) of managing a corporate help desk many years ago - because that wasn't at all fun either.

Monday, September 19, 2005 2:01:36 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  | 
 Wednesday, September 07, 2005
I have started a document where I have and will post changes to CSLA .NET from version 1.5 to 2.0.
Wednesday, September 07, 2005 9:04:39 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  | 
 Tuesday, September 06, 2005

Geekdom comes in various forms - in my case both fandom and the computer world.

Over in fandom there's a targeted effort by fans to provide housing and assistance to the fans affected by Katrina, including having noted authors Mercedes Lackey and Larry Dixon offering up their home and time to host people and to coordinate others willing to host people or donate food, time or money. My wife's blog has more details and links - so if you are a fan here's a great way to help!

And in the computer world there's the Katrina Data Project, which I blogged about a couple days ago. Computer geeks of the world have similarly generously volunteered their time and resources to make this work as well. Here's part of an email from the project, along with contact info if you want to get involved as well:

Some quick data on what we've accomplish so far:

We're housing data on over 65,000 searching or safe individuals... the only larger datastore I am aware of is the ICRC Red Cross site.
We've received data that even the Red Cross hasn't aggregated yet... files put together by volunteers on the ground in shelters 
We're listed on the homepage of "Emergency web terminals" being deployed in the disaster area.
I just met today with the Salvation Army IT team... we will be bringing in over 50,000 more records from their data sources. We will be helping them capture additional data going forward... their system is currently transporting and storing data via email.
We are organizing a network of volunteers who will be traveling to their local shelters and help us receive data
We've got 500 volunteers in a callcenter waiting for access to our data so they can begin an outbound calling effort.
Much much more than I can type right now.
John Galloway
The Katrina Data Project
 
Public Search and Registry: http://www.searchkatrina.com 
Data Interchange Site: http://www.katrinadataproject.com

e: john@katrinadataproject.com
AIM: KDataProject

 

Tuesday, September 06, 2005 7:04:33 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  | 
 Sunday, September 04, 2005
We're a few local (ATL) ASP.NET programmers who have created a central datastore, collection web interfaces, and processing system for missing/safe person data on people affected by hurricane katrina.
 
 
The idea caught on quick... we've only got 2 guys working on this full time and a third helping out when he can. Since going online we've ben contacted by LOTS of groups who want to work with us. We're overwhelmed and need help... we've got plans for a mobile device interface to our data, and are working with a callcenter fielding calls from disaster victims (the callcenter needs some special enhancements to the interface we can't get to fast enough.). We're being listed as the central survivors database on the emergency web terminals being deployed in disaster zones (http://www.publicwebstations.com/)
 
We've got people on the ground at shelters trying to gather lists and transmit them to our datastore electronically. We're also contacting almost 50 other websites which have lists or data collection systems... they are starting to use our MSP file spec and transmit data to us.
 
If you could get the word out that we are looking for volunteer programmers who can hit the ground running and take ownership of some of the special projects we have WE WOULD APPRECIATE IT.. but there are people out there looking for loved ones who would appreciate it even more.
 
Current experience we need:
  • .net mobile framework using our data classes to collect and process data
  • string data parsing experience... there's a lot of data in forum posts we can't use but could triple our data if we could parse into useful fields
  • Mass emailing system... as our system processes incoming bulk data, it makes matches between searchers and safe persons, flagging them as needed contact via email. We need a system to read from a database table containing these contact requests and send them out
  • any general ASP.NET (we're using VB) experience to help build special lookups and interfaces
I hate to make such a request on a holiday weekend, but we are swamped.
 
Please spread the word if you can.
 
Thanks!
 
-John
Developer, The Katrina Data Project
 
 
Sunday, September 04, 2005 2:48:46 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  | 
On this page....
Search
Archives
Feed your aggregator (RSS 2.0)
August, 2014 (2)
July, 2014 (3)
June, 2014 (4)
May, 2014 (2)
April, 2014 (6)
March, 2014 (4)
February, 2014 (4)
January, 2014 (2)
December, 2013 (3)
October, 2013 (3)
August, 2013 (5)
July, 2013 (2)
May, 2013 (3)
April, 2013 (2)
March, 2013 (3)
February, 2013 (7)
January, 2013 (4)
December, 2012 (3)
November, 2012 (3)
October, 2012 (7)
September, 2012 (1)
August, 2012 (4)
July, 2012 (3)
June, 2012 (5)
May, 2012 (4)
April, 2012 (6)
March, 2012 (10)
February, 2012 (2)
January, 2012 (2)
December, 2011 (4)
November, 2011 (6)
October, 2011 (14)
September, 2011 (5)
August, 2011 (3)
June, 2011 (2)
May, 2011 (1)
April, 2011 (3)
March, 2011 (6)
February, 2011 (3)
January, 2011 (6)
December, 2010 (3)
November, 2010 (8)
October, 2010 (6)
September, 2010 (6)
August, 2010 (7)
July, 2010 (8)
June, 2010 (6)
May, 2010 (8)
April, 2010 (13)
March, 2010 (7)
February, 2010 (5)
January, 2010 (9)
December, 2009 (6)
November, 2009 (8)
October, 2009 (11)
September, 2009 (5)
August, 2009 (5)
July, 2009 (10)
June, 2009 (5)
May, 2009 (7)
April, 2009 (7)
March, 2009 (11)
February, 2009 (6)
January, 2009 (9)
December, 2008 (5)
November, 2008 (4)
October, 2008 (7)
September, 2008 (8)
August, 2008 (11)
July, 2008 (11)
June, 2008 (10)
May, 2008 (6)
April, 2008 (8)
March, 2008 (9)
February, 2008 (6)
January, 2008 (6)
December, 2007 (6)
November, 2007 (9)
October, 2007 (7)
September, 2007 (5)
August, 2007 (8)
July, 2007 (6)
June, 2007 (8)
May, 2007 (7)
April, 2007 (9)
March, 2007 (8)
February, 2007 (5)
January, 2007 (9)
December, 2006 (4)
November, 2006 (3)
October, 2006 (4)
September, 2006 (9)
August, 2006 (4)
July, 2006 (9)
June, 2006 (4)
May, 2006 (10)
April, 2006 (4)
March, 2006 (11)
February, 2006 (3)
January, 2006 (13)
December, 2005 (6)
November, 2005 (7)
October, 2005 (4)
September, 2005 (9)
August, 2005 (6)
July, 2005 (7)
June, 2005 (5)
May, 2005 (4)
April, 2005 (7)
March, 2005 (16)
February, 2005 (17)
January, 2005 (17)
December, 2004 (13)
November, 2004 (7)
October, 2004 (14)
September, 2004 (11)
August, 2004 (7)
July, 2004 (3)
June, 2004 (6)
May, 2004 (3)
April, 2004 (2)
March, 2004 (1)
February, 2004 (5)
Categories
About

Powered by: newtelligence dasBlog 2.0.7226.0

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2014, Marimer LLC

Send mail to the author(s) E-mail



Sign In