I am using an IEditorDataFilter to control how my data is displayed. The display of the data changes depending on whether the cell is active (i.e. in edit mode). This causes issues with the default behavior of UltraGrids sharing editors with other columns of the same style/among cells in the same column, as when the editor is in edit mode, it uses a cached result instead of getting the value from the IEditorDataFilter, even though the cached value is for a different cell.
For example, suppose I want the decimal 0 to be displayed as "0" when not in edit mode and as "0.0" when in edit mode, and both cell a and cell b have a value of 0. If cell a is in edit mode and I mouseover to cell b, then cell b will also display as "0.0" even though it is not in edit mode.
Currently, the only way to avoid this seems to be to set the editor of each cell to a separate object, which seems like a waste of memory. Is there a better way of accomplishing what I am trying to do?
Hi,
I think the problem must be in the way in which you are checking for Edit mode. The editor serves the entire column, so if you are simply checking conversionArgs.Editor.IsInEditMode then that will only tell if is a cell is in edit mode somewhere, not necessarily the cell the cell for which the DataFilter is currently firing.
I think what you need to do is cast conversionArgs.Context into an UltraGridCell and then check the cell's IsInEditMode property.
UltraGridCell cell = conversionArgs.Context as UltraGridCell; bool isInEditMode = cell.IsInEditMode;
I'm not entirely sure that the Context will always return a cell, though, so you should probably check for null and put in an assert to detect if it ever gets in there with some other object.
Here is a minimal test case. I set up my ultragrid with an ultradatasource with two decimal columns and two rows with 0s. I suggest having one cell not have a 0, otherwise there is no way to clear the cache. (I am using version 12.1.20121.2008)
public partial class Form1 : Form { public Form2() { InitializeComponent(); DF df = new DF(); foreach(UltraGridColumn uc in ultraGrid1.DisplayLayout.Bands[0].Columns) { uc.Editor.DataFilter = df; } } } class DF : IEditorDataFilter { public object Convert(EditorDataFilterConvertArgs conversionArgs) { switch(conversionArgs.Direction) { case ConversionDirection.EditorToDisplay: UltraGridCell cell = (UltraGridCell)conversionArgs.Context; if(cell.IsActiveCell && conversionArgs.Value is decimal) { conversionArgs.Handled = true; return ((decimal)conversionArgs.Value).ToString("0.0"); } break; } return conversionArgs.Value; } }
Thank you (belatedly ^^;) for taking the time to respond.
Hi Jon,
Okay, I see the issue, now. The DataFilter caches the conversions for efficiency. So once you convert "0" to "0.0", it assumes you will be doing the same conversion for any cell with a value of 0, and it doesn't know you are doing this based on some other state information.
There's currently no way to turn this caching off.
However, as long as the cells you are dealing with here are not using any masking, there might be an alternative solution.
private void ultraGrid1_AfterEnterEditMode(object sender, EventArgs e) { UltraGridCell cell = this.ultraGrid1.ActiveCell; if (cell == null) return; if (cell.Value is decimal) { EditorWithText editorWithText = cell.EditorResolved as EditorWithText; if (editorWithText == null) return; decimal d = (decimal)cell.Value; editorWithText.TextBox.Text = d.ToString("0.0"); } }
One good thing about this solution is that is only applies to cells that are actually in edit mode. The code you have here is dealing with the ActiveCell, but that's not really what you want. It's possible for a cell to be active and not be in edit mode.
The down side of this approach is that it marks the row dirty.
I thought of another potential solution, in case you don't like that the row is marked dirty. The caching is done on the editor, so you could get around the issue by applying a new editor to every cell, as you mentioned in your original post. That, of course, would be horribly inefficient.
But, in fact, you don't need an editor for every cell. What you need is one single editor that is applied to the active cell and only the active cell. So you could create an editor with the DataFilter attached and then assign that editor to the cell before it activates, and then clear it out when it deactivates.
public partial class Form1 : Form { EditorWithText editorWithTextWithDataFilter; public Form1() { InitializeComponent(); this.editorWithTextWithDataFilter = new EditorWithText(); DF df = new DF(); this.editorWithTextWithDataFilter.DataFilter = df; } private void ultraGrid1_BeforeCellActivate(object sender, CancelableCellEventArgs e) { UltraGrid grid = (UltraGrid)sender; UltraGridCell cell = e.Cell; if (cell == null) return; if (cell.Value is decimal) cell.Editor = this.editorWithTextWithDataFilter; } private void ultraGrid1_BeforeCellDeactivate(object sender, CancelEventArgs e) { UltraGrid grid = (UltraGrid)sender; UltraGridCell cell = grid.ActiveCell; if (cell == null) return; if (cell.Editor == this.editorWithTextWithDataFilter) cell.Editor = null; } } class DF : IEditorDataFilter { public object Convert(EditorDataFilterConvertArgs conversionArgs) { switch (conversionArgs.Direction) { case ConversionDirection.EditorToDisplay: if (conversionArgs.Value is decimal) { conversionArgs.Handled = true; return ((decimal)conversionArgs.Value).ToString("0.0"); } break; } return conversionArgs.Value; } }
I cannot find a way to differentiate between a cell being in edit mode or not with this method because it seems the first time I enter my filter both conversionArgs.Editor.IsInEditMode and ((UltraGridCell)conversionArgs.Context).IsInEditMode return false, and I don't seem to enter the filter when one is true (likely because it is using the cache at that point). Therefore, it seems I still need to clear the cache at least once, unless there is some other way you suggest my being able to differentiate when the activated cell is or is not going into edit mode.
Any cell that is activated will go into edit mode, unless is cannot.
There are several reasons why a cell would not be able to enter edit mode.
1) You set a property on that cell that disallows this. So you could check the various Activation/CellActivation properties on the row, cell, and column.
2) The data source does not allow editing.
3) The BeforeEnterEditMode event is cancelled.
You can detect the first two, but not the third one - since you can never be sure if your event handler fires before or after any other event listener.
It seems like the conversionArgs.Editor.IsInEditMode should work, though. Wasn't that working before? Or were you simply checking IsActive on the Cell before?
Mike Saltzman"] Does it really matter if the cell is in edit mode?
Does it really matter if the cell is in edit mode?
Perhaps not. It may just be a personal preference. If I'm about to edit a value, then I don't mind so much if it is displayed a bit differently when I click on it, but if I'm just moving around the grid with the keyboard instead of a mouse, then it doesn't seem as nice an interface to me.
As far as the caching, since it is only in effect if the editor is in edit mode, then I imagine I already lose a lot of the benefit if I'm always assigning a new editor to the cell being edited, anyway.
Thank you for your time on this subject. I'm experimenting with a hybrid of the BeforeCellActivate/Deactivate and the EditorWithText child class that clears the EditorToDisplay cache in calls to GetElementText. I imagine it would work the same without the events, but I feel more comfortable isolating the editor to the single cell since I'm using undocumented features.
Hm. Maybe assigning the Editor to the cell is triggering the DataFilter. So it's getting hit in BeforeCellActivate, before the cell has entered edit mode. I guess you could try moving the BeforeCellActivate code to AfterCellActivate or BeforeEnterEditMode. But I think I don't think this will work, since they both fire before the cell is in edit mode.
Does it really matter if the cell is in edit mode? Maybe you can just apply your filter to the active cell. In that case, you don't even need to the DataFilter to do ANY checking, since the DataFilter is only applied to the active cell, anyway.
Mike,
The cell does go into edit mode. However at the time it enters my DataFilter, it seems is not yet in edit mode. Then, once the editor is in edit mode, the cache takes over and my DataFilter is no longer used (until the editor leaves edit mode).
I had been testing if the cell was active and the grid was focused before for reasons I cannot recall ^^. At that time, I had not checked what would happen if the cell was active but not in edit mode (e.g. via pressing F2).