I have a grid column with a custom editor that inherits from `EditorWithCombo`, like so:
public class IDAndLabelEditorWithCombo<TDataObject> : EditorWithCombo where TDataObject : class { private readonly NLog.Logger Log = NLog.LogManager.GetCurrentClassLogger(); public UltraGridColumn Column { get; private set; } public Func<int, bool, TDataObject, string> GetDisplayTextFunc { get; private set; } // we don't actually override this; this is just to test that the value is in fact 'false' public override bool ComparesByValue(EmbeddableEditorOwnerBase owner, object ownerContext) { var result = base.ComparesByValue(owner, ownerContext); Log.Debug(result); return result; } public IDAndLabelEditorWithCombo(UltraGridColumn column) { Column = column; GetDisplayTextFunc = null; } public IDAndLabelEditorWithCombo(UltraGridColumn column, Func<int, bool, TDataObject, string> customDisplayTextFunc) { Column = column; GetDisplayTextFunc = customDisplayTextFunc; } public override IValueList ValueList { get { if (!IsInEditMode) return Column.ValueList; else return base.ValueList; } } protected override string GetElementText(EmbeddableUIElementBase element, bool ignorePasswordChar) { if (element.GetContext(typeof(UltraGridCell)) is UltraGridCell cell) return DataValueToText(cell.Value, element.Owner, cell.Row); return base.GetElementText(element, ignorePasswordChar); } public override string DataValueToText(object valueToConvert, EmbeddableEditorOwnerBase owner, object ownerContext) { var showIDs = AdminOverrides.ShowIDs; // just internal code that returns a bool if (ownerContext is UltraGridRow) { if (valueToConvert == null || valueToConvert == DBNull.Value) { return _DataValueToText(0, showIDs); } if (valueToConvert is int) { int intVal = (int)valueToConvert; if (intVal <= 0) return _DataValueToText(0, showIDs); var valueList = owner.GetValueList(ownerContext) as ValueList; if (valueList == null) { Log.Debug($"{nameof(valueList)} is null ({nameof(valueToConvert)}: {valueToConvert}, {nameof(owner)}: {owner})"); return _DataValueToText(intVal, showIDs); } var item = valueList.FindByDataValue(intVal); if (item != null) return _DataValueToText(item, showIDs); Log.Debug($"{nameof(item)} is null ({nameof(valueToConvert)}: {valueToConvert}, {nameof(owner)}: {owner})"); return _DataValueToText(intVal, showIDs); } } Log.Debug($"{nameof(ownerContext)} is not {nameof(UltraGridRow)}: {ownerContext} ({nameof(valueToConvert)}: {valueToConvert}, {nameof(owner)}: {owner})"); return base.DataValueToText(valueToConvert, owner, ownerContext); } private string _DataValueToText(int v, bool showIDs) { if (v == 0) return showIDs ? "[0]" : ""; return $"[{v}]"; } private string _DataValueToText(ValueListItem item, bool showIDs) { var dataValue = (int)item.DataValue; var dataObject = item.Tag as TDataObject; var func = GetDisplayTextFunc; if (dataObject != null && func != null) return func(dataValue, showIDs, dataObject); return item.DisplayText + (showIDs ? $"[{dataValue}]" : ""); } }
The grid has `DataSource` set to a DataView, and that table contains values that are numeric IDs; this custom editor makes it so the user-displayed text is instead shown as a string. Admins can enable `AdminOverrides.ShowIDs` to also see the IDs, if they like. Nothing too special.
Sorting by this column should sort by the displayed text. For example, given a table:
Andy should show first, not Sarah. Indeed, this mostly happens.
But sometimes, such as when deleting a row from the underlying DataTable, the grid forgets this sort and instead sorts by ID. Even if I explicitly call `RefreshSort` (`DisplayLayout.Bands[0].SortedColumns.RefreshSort(false)`), the grid insists that the column is correctly sorted by Value, not by Text. Unless, that is, the user explicitly clicks the column again to reverse the sort, and then clicks it another time; now, the sort is by Text again.
I've checked that:
If I instead add or edit rows (rather than deleting one), the behavior doesn't seem to happen (or at least not as reproducibly).
This is with 21.1.20211.62 on .NET Framework 4.7.2. I haven't yet checked if this is fixed in a newer release.
Hello Soren,
I have been investigating into the behavior you are reporting, and I am a bit confused on the setup part of this in this case. For example, I am unsure if the ID and Caption grid that you gave is a representation of your grid or the ValueList for your drop-down?
I am attaching a sample project that brought your custom editor in and removed / replaced the pieces that I don’t have locally. It would be most helpful if you could please modify it such that it reproduces the behavior you are seeing and send it back. Alternatively, if you have an isolated sample project of your own that you could attach, I would gladly take a look at that instead.
Please let me know if you have any other questions or concerns on this matter.
UltraGridCustomEditorDropDownTest.zip
Hello Andrew,
Thanks for the speedy response!
Andrew Goldenbaum said:I am a bit confused on the setup part of this in this case. For example, I am unsure if the ID and Caption grid that you gave is a representation of your grid or the ValueList for your drop-down?
Yeah, sorry for wording that poorly. I did indeed mean that ID/Caption are in the value list. The grid itself has a lot more columns.
I was able to play with your sample project and adapt it a bit to ours, and indeed couldn't reproduce the behavior in the sample. But it did lead me to the (probably issue).
TL;DR: yes, the `Editor` property was still set, but the `ValueList` was (momentarily) empty. However, this wasn't because of UltraGrid but rather because of our own code.
Roughly:
It seems I was calling RefreshSort() in the middle of step 3: the value list was (potentially) empty, so while the grid still knew there was supposed to be a ValueList, it didn't find any items and thus did a fallback to sorting by ID.
I've added another call to RefreshSort() after the value list is filled again, and my issue seems to be resolved!
Thanks again.