Tommy Elliott
Tommy Elliott

Reputation: 491

Need to extend DataGridNumericalColumn for Telerik UWP

I have a need to create a custom version of the UWP DataGridNumericalColumn that allows customization of the RadNumericBox properties (ValueFormat, ButtonsVisibility, SmallChange, LargeChange, Value) as well as the ability to edit the value as cents (199) without decimal place while editing, but display as normal dollars with decimal cents (1.99) while not editing. I've tried two different approaches to extend existing controls, neither of which I can seem to get to work fully for me.

1) Tried deriving from DataGridNumericalColumn - impossible due to inaccessible internal members down the line, even with full source code from GitHub available.

2) Tried deriving from DataGridTemplateColumn - somewhat workable for initial display, but everything relating to inline edit mode vs display mode and validation message display on the cell seems beyond reach (not override-able) and I can't seem to use CellContentTemplateSelector to choose between the inline Edit mode RadNumericBox display and the normal TextBlock display because I can't seem to detect when Edit mode is applied to the cell.

It is starting to seem like the only way I can achieve what I need is to fork the GitHub code-base so I can derive from DataGridNumericalColumn with access to internal code.

What approach could I take to achieve my desired customizations?

(I am using Telerik UI For Universal Windows Platform, version 2017.1.301.45, at the time of this writing.)

Upvotes: 0

Views: 297

Answers (1)

Tommy Elliott
Tommy Elliott

Reputation: 491

I eventually worked out workarounds that let me get past the main difficulties in extending this functionality using the derive from DataGridTemplateColumn approach. Here are the updates and customizations I made - they are described mostly at the conceptual level, but it should be enough for others to duplicate this sort of customization for themselves.

UPDATE1: An update as I've been working on this: Continuing along with the approach of deriving from DataGridTemplateColumn, I found that I can successfully change my displayed markup for edit mode vs display mode by creating custom commands for editing operations in the grid (CustomBeginEditCommand, CustomCancelEditCommand, and CustomCommitEditCommand very similar to the ones in http://docs.telerik.com/devtools/universal-windows-platform/controls/raddatagrid/features/commands/editing-commands/datagrid-editingcommands-begineditcommand ) along with an interface IItemAwareOfEditMode, applied to the ViewModel items for the Grid's data, that has a single bool property IsInEditMode that I set to true or false appropriately in the custom commands, which is then used in a custom DataTemplateSelector to decide when to apply my edit markup vs my display markup. This uses (DataTemplate)XamlReader.LoadWithInitialTemplateValidation(editControlMarkup) for translating dynamically created markup strings to DataTemplates. For my implementation, I create the markup in a PropertyChangedCallback for my custom column's PropertyNameProperty dependency property.

However, I still am having issues with validation and displaying the validation messages, and reverting values when the user cancels edit. I have the ViewModel for the grid row items implemented such that they derive from ValidateViewModelBase, and so they add/remove errors appropriately according to the documentation on validation at http://docs.telerik.com/devtools/universal-windows-platform/controls/raddatagrid/features/validation . If I use the DataGridNumericalColumn (not customized) with the same data, the validation messages do appear pointing to the cell when the data is invalid, but with my custom column, HasErrors is true on the items, but the validation messages don't appear. Looking at the validation code in https://github.com/telerik/UI-For-UWP/blob/master/Controls/Grid/Grid.UWP/View/Columns/TypedColumns/DataGridTypedColumn.cs in the CreateEditorContainer function, it seems there is an EditRowHostPanel and ValidationControl involved along with the editor content, but I don't have access to pieces needed to implement the container exactly as is done there.

What can I do to get the validation messages to appear as they do in the DataGridNumericalColumn?

Also, what can I do to make cancelling an edit (clicking the blue X for the row when in edit mode) actually revert my custom column's value to what it was prior to entering edit mode?

UPDATE2: Another update as I've been working on this: Continuing along with the approach of deriving from DataGridTemplateColumn, I've managed to successfully display validation messages for the edit mode by including a ValidationControl in the edit mode template markup, which references the RadNumericBox from the template (by Name) using the ControlPeer property, and giving its DataItem property a value of "{Binding}", and appropriately populating its PropertyName.

This is getting close to what I need, but it seems that my CustomCancelEditCommand, which uses

Owner.CommandService.ExecuteDefaultCommand(CommandId.CancelEdit, context);

, does not appropriately update the display of the cell to its previous value. It correctly doesn't call the CustomCommitEditCommand when the inline row edit is cancelled; however, it displays as the modified value (not reverted to the value prior to edit). Even if you edit again, the value remains as the modified value when displayed in the grid.

I see that in https://github.com/telerik/UI-For-UWP/blob/master/Controls/Grid/Grid.UWP/View/Services/Commands/Editing/CancelEditCommand.cs in the Execute method, that it executes its base implementation, followed by

Owner.editService.CancelEdit(context.TriggerAction)

, which I don't understand (RadDataGrid does not contain a definition for editService and I can't derive from that CancelEditCommand class because it is internal).

What can I do to make cancelling an edit (clicking the blue X for the row when in edit mode) actually revert my custom column's value to what it was prior to entering edit mode?

UPDATE3: I have finally managed an EXTENSIVE workaround that does revert my custom columns' value on cancel.

My workaround for the cancel functionality involved: 1) Created a CustomRadDataGrid, which derives from RadDataGrid.

2) Gave my CustomRadDataGrid class a CustomEditingService property, which is a CustomEditingService, which is copied and modified code from EditingService (mostly commenting out unneeded parts, but also changing InitializeEditOperation's implementation and changing CancelEdit to have an out parameter of the operation's OriginalValues dictionary), and which derives from CustomServiceBase<RadDataGrid>, which is copied and modified code from ServiceBase (changed IsOperational to return Owner.DataContext != null), which derives from CustomAttachableObject<T> where T : RadControl, which is copied code from AttachableObject.

3) Added GetActualValueForInstance function and SetActualValueForInstance method to my custom column, which uses reflection to get/set the data row instance's value for this column (based on using my PropertyName Dependency Property's value), and made the InitializeEditOperation of my CustomEditingService just save original values of my custom columns, and made the CancelEdit of my CustomEditingService return that dictionary of original values in an out variable.

4) Made my CustomBeginEditCommand call BeginEdit on the grid's CustomEditingService after calling Owner.CommandService.ExecuteDefaultCommand(CommandId.BeginEdit, context) - that allows my custom column original values to be stored.

5) Made my CustomCommitEditCommand call CommitEdit on the grid's CustomEditingService after calling Owner.CommandService.ExecuteDefaultCommand(CommandId.CommitEdit, context) - that allows my custom editing service to properly track its editing state.

6) Made my CustomCancelEditCommand call CancelEdit on the grid's CustomEditingService AND for each original value dictionary item, use the Key (column, as my custom column) SetActualValueForInstance passing in context.CellInfo.Item and the Value (previously stored original value), BEFORE calling Owner.CommandService.ExecuteDefaultCommand(CommandId.CancelEdit, context) - that restores my custom column original values prior to the standard cancel actions occurring.

Done! Whew... It seems that this library needs a lot of changes to allow for better extend-ability. This has been logged as a feature request at Telerik according to a reply to my support ticket with them on this subject.

I think other people will want to be able to extend Telerik's various DataGridColumn controls too, so I shared my struggle and (eventually) successful customization here.

Upvotes: 1

Related Questions