We have a hit a slightly annoying problem where we want to enable/disable a form's Save button based on whether or not the collection object bound to it is valid or not. With a non-collection object this is a simple case of using the CurrentItemChanged event and code such as
btnSave.Enabled = myObject.IsValid;
Even better is that you can set your bound control's Data Source Update Mode to trigger the event on PropertyChanged rather than on Validation - meaning that the Save button reacts straight away to an edit rather than having to tab out of the control for the button to update. With a DataGridView you can delete the content of a cell thus violating a validation rule that says the field concerned is required. But the CurrentItemChanged event will not fire until the user presses enter or clicks another cell, and this means the user can click Save when the object is not valid. I realise we can easily check the objects state within the code behind the save button, but it is so much better to be able to disable the button the moment the object becomes not Valid. There doesn't seem to be a way to set the Data Source Update Mode for a DataGridView, so is there some other solution?
TIA
Hi everyone,
I spent several hours trawling forums and threads in an attempt to solve this one. Now I have something that appears to work, thought that I would share the solution, in view of all of the help that this forum has given me.
The solution applies to VS2005 with SP1 and CSLA 2.1.4. Haven't tested yet with VS2008 or CSLA 3.0 and above. Initially I went down the wrong road of attempting to change the data source update mode for the datagridview to OnPropertyChanged, soon realising that the mode applies to properties at the grid level, and not on individual rows, columns and cells. In particular I wanted to enable/disable command buttons when a list became dirty/invalid.
The first step is to code the ListChanged event of the binding source control in a similar fashion to Rocky's article http://www.lhotka.net/Article.aspx?area=4&id=5faaee8e-8496-4845-86f7-787c6b64096c.
Private Sub bdgAccountClosedReasons_ListChanged( _
ByVal sender As Object, _
ByVal e As System.ComponentModel.ListChangedEventArgs) _
Handles _
bdgAccountClosedReasons.ListChanged
If AccountClosedReasons.CanEditObject Then
DisableControls()
Else
Throw New System.Security.SecurityException( _
"User not authorized to edit an account closed reason")
End If
The next step is to use the CurrentCellDirtyStateChanged event of the datagridview to allow an immediate update to the underlying datasource. Credit for this goes to one particular MS forum thread that I am still trying to relocate . The handling of atomic operations (checkbox/combobox) is straightforward, but I have four validation rules that I wanted to trigger on a text field in the grid, particularly the StringRequired rule. I wanted the OK/Apply buttons to become enabled as soon as user entered a value. The solution that seems to work follows:
Private Sub dgvEditAccountClosedReasonList_CurrentCellDirtyStateChanged( _
ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles _
dgvEditAccountClosedReasonList.CurrentCellDirtyStateChanged
If dgvEditAccountClosedReasonList.IsCurrentCellDirty Then
If TypeOf dgvEditAccountClosedReasonList.CurrentCell Is DataGridViewCheckBoxCell _
Or TypeOf dgvEditAccountClosedReasonList.CurrentCell Is DataGridViewComboBoxCell Then
' Commit the changes to the data source.
dgvEditAccountClosedReasonList.CommitEdit( _
DataGridViewDataErrorContexts.Commit)
ElseIf TypeOf dgvEditAccountClosedReasonList.CurrentCell Is DataGridViewTextBoxCell Then
' Temporarily halt events raised from data changes.
bdgAccountClosedReasons.RaiseListChangedEvents = False
' Commit the changes to the data source.
dgvEditAccountClosedReasonList.CommitEdit( _
DataGridViewDataErrorContexts.Commit)
' Manually call the event method to enable/disable command buttons.
bdgAccountClosedReasons_ListChanged(Nothing, Nothing)
' Re-enable binding source events.
bdgAccountClosedReasons.RaiseListChangedEvents = True
End If
End If
End Sub
I am approaching this with a certain amount of trepidation as I am aware that I am effectively validating a text field on every keypress. However, in my favour is the fact that my object is small, six business properties only (four of which are readonly), with a total of only four validation rules, all on the text field. In the real world I would not expect more than a couple dozen records in the list. So far testing has not revealed any perceptible slow down in responsiveness to keypresses, though I would hesitate before applying the solution to larger, more complex business objects.
Steve
Copyright (c) Marimer LLC