I have a listbox with some cells of type DesktopListbox.CellTypes.TextField.
These cells are user-editable, but I want to validate the entries and either reformat them or reject the edit outright.
To do this, I have saved the cell’s original value in the .CellTagAt property.
However, in the CellTextChange event, if I try to change the CellTextAt(Row, Column) property and change the text of the cell, the change has no effect.
I think this is because my cell change is happening behind the .ActiveTextControl, which doesn’t know anything about my new .CellTextAt and overwrites it as soon as the edit is finished.
Trying to change the text in Me.ActiveTextControl.Text causes a stack overflow (because changing the text in that event triggers a new TextChanged event on itself).
This is a common dilimma of How do I change a TextField in code without triggering an event. You don’t. The solution is to set a flag ChangingViaCode before you update the textfield and clear it after. In the TextChanged event, check the flag and return if true, otherwise, process the change.
I got intrigued by this and I have slapped together a basic solution that will get you started in the right direction.
Delete your entire approach and start with a fresh Listbox. Add the DoublePressed event:
Sub DoublePressed() Handles DoublePressed
'What row did the user double-click?
lastEditRow=me.SelectedRowIndex
'Save the content for future rollback
lastPreEditContent=me.CellTextAt(lastEditRow, 0)
'Start editing the row text
me.EditCellAt(lastEditRow, 0)
'Attach a handler to the text edit control's Focus Lost event
AddHandler me.ActiveTextControl.FocusLost, addressof self.ListboxEditFinished
End Sub
Add this method to the window:
Public Sub ListboxEditFinished(theTextControl as DesktopTextControl)
'Do whatever validation/rollback/etc is necessary here
'Save the value to the control, because whatever is in that control will be sent to the listbox
theTextControl.Text=lastPreEditContent
End Sub
Add two properties to the window:
lastEditRow as integer
lastPreEditContent as string
Basically, you’re attaching an event handler to the internal text edit field and catching its FocusLost event - which is fired when editing is finished. Then, you do whatever manipulations you need to the edit field’s text directly, and the Listbox will grab that text and put it in place.
One way I set properties on the ActiveTextControl is in the CellFocusReceived event. You should be able to manage this that way. IIRC, CellAction is raised when a change is committed, so you could validate there.
This is a really interesting approach, thanks for this. There are a few challenges.
A field can be edited without a double-click. A single click on the currently selected row will also initiate an edit without firing the MouseDown/MouseUp events for the Listbox.
An edit can also be completed without triggering the ActiveTextControl’s FocusLost event, if the user presses Return. Only if they click another row does FocusLost fire.
I’d love it if the Listbox would have an event like EditCellAboutToBegin, where you could return a custom subclass of DesktopTextControl that could manage the editing process, instead of wiring it up during runtime using AddHandler. This would be a much cleaner, more flexible approach.
This is the event I was looking for, thank you Anthony! In CellAction I can check the new value against the saved value in the celltag and validate accordingly.