Hi everyone,
Does anyone know how to do Simple Databinding using the Forms Designer in VS? For example, bind "IsDirty" of a BusinessBase or BusinessListBase BO to the "enabled" property of a button on the form?
Thanks.
BBM
IsDirty is marked as Browsable(False), so it isn't directly eligible for data binding. This is true of IsValid and IsSavable as well. What I suggest as a solution is to create a UI helper class like the one below. In your form you create an instance of this object and use it as a binding source for your controls (buttons, etc).
Imports System.ComponentModel
Public Class EventObserver
Implements INotifyPropertyChanged
Public Event PropertyChanged( _
ByVal sender As Object, _
ByVal e As System.ComponentModel.PropertyChangedEventArgs) _
Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
Private mObject As Csla.Core.IEditableBusinessObject
Private WithEvents mNotifyObject As INotifyPropertyChanged
Private mIsValid As Boolean
Private mIsDirty As Boolean
Private mIsSavable As Boolean
Public Sub New(ByVal obj As Csla.Core.IEditableBusinessObject)
mObject = obj
' init event handling
mNotifyObject = CType(obj, INotifyPropertyChanged)
' init fields
mIsValid = obj.IsValid
mIsDirty = obj.IsDirty
mIsSavable = obj.IsSavable
End Sub
Private Sub mNotifyObject_PropertyChanged( _
ByVal sender As Object, _
ByVal e As System.ComponentModel.PropertyChangedEventArgs) _
Handles mNotifyObject.PropertyChanged
Dim validChanged As Boolean
Dim dirtyChanged As Boolean
If mIsValid <> mObject.IsValid Then
mIsValid = mObject.IsValid
RaiseEvent PropertyChanged( _
Me, New PropertyChangedEventArgs("IsValid"))
End If
If mIsDirty <> mObject.IsDirty Then
mIsDirty = mObject.IsDirty
RaiseEvent PropertyChanged( _
Me, New PropertyChangedEventArgs("IsDirty"))
End If
If (validChanged OrElse dirtyChanged) _
AndAlso mIsSavable <> mObject.IsSavable Then
mIsSavable = mObject.IsSavable
RaiseEvent PropertyChanged( _
Me, New PropertyChangedEventArgs("IsSavable"))
End If
End Sub
Public ReadOnly Property IsValid() As Boolean
Get
Return mObject.IsValid
End Get
End Property
Public ReadOnly Property IsDirty() As Boolean
Get
Return mObject.IsDirty
End Get
End Property
Public ReadOnly Property IsSavable() As Boolean
Get
Return mObject.IsSavable
End Get
End Property
End Class
I've got this implemented but I'm confused about one thing. What sets validChanged and dirtyChanged in mNotifyObject_PropertyChanged?
Thanks,
Dave Erwin
I just got off the phone with a friend of mine at Microsoft. We did some testing and determined for certain that binding doesn't work when browsable(false) is on the property. Yes, you all knew this, but he thinks it is actually a bug, so it might get fixed in the future at some point. Browsable(false) is only supposed to surpress the IDE, not the runtime, but it is doing both.
So that doesn't help us really, because even if it got fixed, it wouldn't be for months or longer...
The solution I have on my web site is certainly workable - handling the bindingsource control's CurrentItemChanged event.
http://www.lhotka.net/Article.aspx?area=4&id=5faaee8e-8496-4845-86f7-787c6b64096c
But we got to talking further. My friend thinks that I should take off the browsable(false) attributes on the IsXYZ methods (IsNew, IsDeleted, IsSavable, IsDirty, IsValid) so people can bind to them as desired. And I could do that.
BUT, the reason I made them browsable(false) to start with is that usually you don't want to bind to them. And in that case you have to set the default control to none in the Data Sources window, which is a manual process and can be a serious PITA if you have a lot of business classes.
My friend then pointed out that just getting a lot of business classes into the Data Sources window is a tedious, manual process in the first place. His idea, and I think it may be a good one, is to create a utility that would reflect against a business assembly and create .datasource files for each business class. Such a utility could use a custom attribute so it would know to pre-define which controls should be used for each property. The IsXYZ methods could have this custom attribute to tell the utility that "none" should be defaulted for them. Then they'd be bindable, but they wouldn't bind by default - at least through drag-and-drop.
This utility would also need to modify the project file (presumably for the UI project) to add the references to the .datasource files. The end result is that the Data Sources window would be auto-populated with all your classes, and the IsXYZ properties would be auto-set to use no control.
Taking this idea one step further, this new custom attribute could be abstract. Rather than using it to specify actual control types (Windows.Forms.TextBox, etc.), it could just say "TextBox" and a translation table could be used to translate that to the right control for Windows Forms, and a different control for WPF (when WPF gets equivalent designer support). The attribute would merely suggest the broad control type, and the translation would be used to get the specific control type.
I don't have time to tackle this idea right now, but I did want to put it out here on the forum. That way the idea is available, and anyone wanting to build such a utility could certainly do so - and maybe put it on CSLAcontrib to share with others.
Hi Brian,
Thanks for the tip. I was able to get your suggestion to work, but it seems overly complex to me to have to add an additional class for each BO type - just so I can bind to some boolean properties to enable / disable buttons.
Generics may help me here (I guess potentially you could get by with just two wrapper classes, one for BusinessBase BO's and one for BusinessListBase), but I'll need to study some more to make this approach work. Two extra classes wouldn't be too bad, but it still rubs me the wrong way.
Are there any other approaches to doing this?
BTW, my "wrapper" class (to bind to a BusinessListBase derived class) follows for anybody interested. Comments / criticisms are welcomed.
Thanks again.
BBM
Imports
System.ComponentModelPublic
Class BindableWrapper Inherits Csla.Core.BusinessBase Private mBO As BusinessListBase(Of DescriptorFamilyTypes, DescriptorFamilyType) Private mBindableIsSavable As Boolean = False Public Property BindableIsSavable() As Boolean ' Bind this property to "Save" and "Cancel" buttons on the UI Get Return (mBO.IsDirty And mBO.IsValid)End Get
Set(ByVal value As Boolean) If value <> mBindableIsSavable ThenmBindableIsSavable = value
' Notify Databinding that the property has changedPropertyHasChanged()
End If End Set End Property Public Sub New(ByVal wo As BusinessListBase(Of DescriptorFamilyTypes, DescriptorFamilyType))mBO = wo
' Listen for the ListChanged event AddHandler mBO.ListChanged, New ListChangedEventHandler(AddressOf List_PropertyChanged) End Sub Private Sub List_PropertyChanged(ByVal sender As Object, _ ByVal e As System.ComponentModel.ListChangedEventArgs) ' If the underlying list has changed update the value of our "Bindable" IsSavable propertyBindableIsSavable = (mBO.IsDirty
And mBO.IsValid) End SubEnd
ClassCopyright (c) Marimer LLC