Warning Dialog Before Property ChangeWarning Dialog Before Property Change
Old forum URL: forums.lhotka.net/forums/t/3379.aspx
josh.kodroff posted on Tuesday, August 14, 2007
I have a property that when changed needs to display the user with a warning message to confirm or cancel the change. In order to present the warning message and allow the user to cancel the change, I removed databinding from the field and took care of getting and setting the BO property manually .
The problem is that by removing databinding, I also removed the ErrorProvider control's hookup to the validation rules.
Any ideas on how to get both requirements:
1. A warning message before changing a property that can cancel an edit to a property (the properties that need warnings are bound to comboboxes and checkboxes).
2. An error provider that reads the broken rules associated with the property.
triplea replied on Tuesday, August 14, 2007
I think it would be better to move all the logic into a customer control (e.g. ComboboxEx and CheckboxEx each extending the base controls). You could then display the visual warning on the appropriate event (avoiding all this removing of bindings etc which always leads to troubles for me). Also, regarding (2), you can always ask your object (which should always be of type MyBusinessBase) for the asociated broken rules by exposing an appropriate public method.
I have done something similar (e.g. limiting number of characters types in a textbox, enabling/disabling based on security etc) and it shouldn't be too much trouble.
ajj3085 replied on Tuesday, August 14, 2007
This is probably the best approach. Its the UIs responsiblity to warn the user of any adverse consequences of changing a property value, not the business layers. Its also probably best not to remove databinding, but instead cancel the change even before. how you do this depends on your control.
SonOfPirate replied on Tuesday, August 14, 2007
Not sure if this needs to be very complicated. We've come up with what I believe to be a relatively simple and flexible solution to this exact problem: we added a PropertyChanging event with the PropertyChangingEventArgs derived from CancelEventArgs. Using the INotifyPropertyChanging interface, UI forms, etc. can register handlers for the event then poll the property being changed, display a confirmation dialog and set the EventArgs.Cancel property accordingly. Within the property, we simply check if e.Cancel == false before updating the property's variable with the new value. Has worked without a problem for us thus far.
HTH
Patrick.Roeper replied on Tuesday, August 14, 2007
So if you were firing an event from within the business object, and the UI is handling the event, is this one of the situations where serialization fails for a system using remoting? I vaguely remember reading a blog that rocky wrote a little while back that said there was a problem with serialization and events being handled from non-serializable objects (like a form).
I know there was an easy solution like marking the event with [field: NonSerializable] in C#... I dont remember the VB implementation, I just remember it being the majority of the blog post..
triplea replied on Wednesday, August 15, 2007
SonOfPirate:
Using the INotifyPropertyChanging interface, UI forms, etc. can register handlers for the event then poll the property being changed, display a confirmation dialog and set the EventArgs.Cancel property accordingly.
I guess that works fine as well but isn't that a bit sneaky since you shift UI logic in your BO? I occasionally use some of my BOs to run batch updates (e.g. load customer info from a spreadsheet and call my CustomerEdit object, modify and call Save()) in which case i wouldn't want notifications to come up.
I still think its not too complicated to create a custom control :-)
McManus replied on Wednesday, August 15, 2007
triplea: SonOfPirate:
Using the INotifyPropertyChanging interface, UI forms, etc. can register handlers for the event then poll the property being changed, display a confirmation dialog and set the EventArgs.Cancel property accordingly.
I guess that works fine as well but isn't that a bit sneaky since you shift UI logic in your BO? I occasionally use some of my BOs to run batch updates (e.g. load customer info from a spreadsheet and call my CustomerEdit object, modify and call Save()) in which case i wouldn't want notifications to come up.
I still think its not too complicated to create a custom control :-)
Triplea,
We use exactly the same method as SonOfPirate. Ín my opinion you don't shift UI logic into your BO. Showing a confirmation dialog is done in the UI (more precisely in the event handler for the PropertyChangingEvent). The BO only makes it possible to have this functionality in the UI by raising the cancelable event just before the property is changed.
Cheers,
Herman
SonOfPirate replied on Wednesday, August 15, 2007
Yep, McManus has it right. All we are doing is making it possible for the UI to capture what is happening. Nothing in the BO says that anything has to happen or that it has to be in the UI - anything can handle the event and do anything with it that it wants.
As for the question about batch updates, you are correct that firing multiple events is undesirable. I did fail to mention that we make use of the RaisePropertyChangedEvents property in our OnPropertyChanging method to filter when and when not to raise the event. Our thinking is that if you don't want to raise post-change events then you probably don't want to raise pre-change events. But, if that logic breaksdown, you can always add a RaisePropertyChangingEvents property and distinguish between the two. For batch updates, I would think you'd want them both off so this would suffice.
HTH
triplea replied on Wednesday, August 15, 2007
OK I think you guys have convinced me that the approach is fine. I guess I stuck similar logic in my user controls since I have CSLA specific controls (e.g. CslaTextbox, CslaCombobox etc) that expect to be databound with an object of type MyBusinessBase and do lots of UI specific work within there. So to keep things consistent in my projects I would refrain from using the events but otherwise I like the approach and might consider it in the future :-)josh.kodroff replied on Monday, August 27, 2007
There's a few gotchas in SonOfPirate's solution, which I suspect he left out deliberately because he knew I was a Penn State fan. (j/k) Actually, the solution's not too hard, but a little intimidating if you're inexperienced with custom delegates as I am.
Here's the code for the complete solution:
First,. define your interface and args and delegate stuff:
Public Interface INotifyPropertyChanging
Event PropertyChanging As PropertyChangingEventHandler
End Interface
Public Class PropertyChangingEventArgs
Inherits System.ComponentModel.CancelEventArgs
Private mPropertyName As String
Private mProposedValue As Object
Public Sub New(ByVal propertyName As String, ByVal proposedValue As Object)
mPropertyName = propertyName
mProposedValue = proposedValue
End Sub
Public ReadOnly Property PropertyName() As String
Get
Return mPropertyName
End Get
End Property
Public ReadOnly Property ProposedValue() As Object
Get
Return mProposedValue
End Get
End Property
End Class
Public Delegate Sub PropertyChangingEventHandler(ByVal sender As Object, ByVal e As PropertyChangingEventArgs)
Then you need to put this stuff in the class that's going to raise the event:
Implements INotifyPropertyChanging
<NotUndoable()> <NonSerialized()> Private mNonSerializableHandlers As PropertyChangingEventHandler
Private mSerializableHandlers As PropertyChangingEventHandler
Public Custom Event PropertyChanging As PropertyChangingEventHandler Implements INotifyPropertyChanging.PropertyChanging
AddHandler(ByVal value As PropertyChangingEventHandler)
If value.Method.IsPublic AndAlso _
(value.Method.DeclaringType.IsSerializable OrElse value.Method.IsStatic) Then
mSerializableHandlers = DirectCast(System.Delegate.Combine(mSerializableHandlers, value), PropertyChangingEventHandler)
Else
mNonSerializableHandlers = DirectCast(System.Delegate.Combine(mNonSerializableHandlers, value), PropertyChangingEventHandler)
End If
End AddHandler
RemoveHandler(ByVal value As PropertyChangingEventHandler)
If value.Method.IsPublic AndAlso _
(value.Method.DeclaringType.IsSerializable OrElse value.Method.IsStatic) Then
mSerializableHandlers = DirectCast(System.Delegate.Remove(mSerializableHandlers, value), PropertyChangingEventHandler)
Else
mNonSerializableHandlers = DirectCast(System.Delegate.Remove(mNonSerializableHandlers, value), PropertyChangingEventHandler)
End If
End RemoveHandler
RaiseEvent(ByVal sender As Object, ByVal e As PropertyChangingEventArgs)
Dim nonSerializableHandlers As PropertyChangingEventHandler = mNonSerializableHandlers
If nonSerializableHandlers IsNot Nothing Then
nonSerializableHandlers.Invoke(sender, e)
End If
Dim serializableHandlers As PropertyChangingEventHandler = mSerializableHandlers
If serializableHandlers IsNot Nothing Then
serializableHandlers.Invoke(sender, e)
End If
End RaiseEvent
End Event
Finally, to raise the event on the property whose change you need to warn the user on:
Public Property DangerousProperty() As Integer
Get
Return _dangerousProperty
End Get
Set(ByVal value As Integer)
If _dangerousProperty <> value Then
Dim e As New PropertyChangingEventArgs("DangerousProperty", value)
RaiseEvent PropertyChanging(Me, e)
If e.Cancel = False Then
__dangerousProperty = value
' other effects of property change
PropertyHasChanged()
End If
End If
End Set
End Property
Copyright (c) Marimer LLC