Hello there,
We have a need to display personal information in redacted form.( ie SSN would display XXXXX1234, but we deal with other personal information and have worked out a component that will mask the data on simple controls.)
However, we use a lot of UltraWinGrid controls as well as UltraCombo controls with tabular displays. The data for these comes from DataSets and I don't really want to change the DataSet as some users will see full information and some will not. At the same time, if I redact the display, I don't want the ADO.NET GetChanges() method on the dataset to trigger something has changed so we need to stick to the display area on this. Here is the code I would LIKE to write, but cells don't have setters, see the final line where I would like to assign a value to row.Cells[_columnIndex].
public UltraGrid MaskedUltraGrid { get { return _grid; } set { _grid = value; if( MaskingEnable ) { var masking = new Mask(); UltraGridLayout layout = this._grid.DisplayLayout; UltraGridBand band = layout.Bands[_bandIndex]; UltraGridColumn column = band.Columns[_columnIndex]; foreach( var row in _grid.Rows ) { if( Convert.ToInt32( row.Cells["Type_ID"] ) == 1 || Convert.ToInt32(row.Cells["Type_ID"]) == 8 )
row.Cells[_columnIndex] = masker.MaskString( row.Cells[_columnIndex].ToString(), MaskingCharacter );
} } }
Does anyone have an alternative to using my masking control in a way that will permit it's use I prefer not to use the UltraGridMaskedEdit since it can't conditionally set the mask as in the above if condition?
Thanks in advance if anyone can point me the right way,
Kent
By the by...
UltraGridLayout layout = this._grid.DisplayLayout; UltraGridBand band = layout.Bands[_bandIndex]; UltraGridColumn column = band.Columns[_columnIndex];
are a red herring... I was searching through the components looking for something useful but are not required in above code.
Hi Kent,
This is going to be a bit trickier than that.
What I would do is hide the "real" column of data and then add an unbound column to display to the user. Then you handle the InitializeRow event of the grid and copy the "real" data into the unbound column and you can do whatever masking you want at that point.
If the data needs to be editable, you could also handle the BeforeRowUpdate event and copy the unbound cell data into the "real" cell so that it gets saved.
I realize that this solution isn't very abstract, since you will have to do the same thing for every column you want masked on all grids, combos, and dropdowns. But it's really the safest way to do it.
Another approach you could take it to modify the display using a CreationFilter. This would change the displayed text in the cell without affecting the underlying data. But there are a number of problems with this approach. For one thing, it will only affect the on-screen display. That means it will not affect copy & paste and it won't work if the cell goes into edit mode. It also won't affect exporting (although printing will work). Copying and exporting could be handled using events of the grid or the exporter like BeforeMultiCellOperation and ExportStarted. But there's no way to handle editing the cell as far as I can see. So this second approach might be better since it's a bit more encapsulated, but it requires more code and it won't work if the cell is editable.
Ok... well you gave me the information I needed, but I solved it in an unexpected way that leaves it pretty abstract. I made an UltraGrid extension. First I wrote the extension class:
public static class MaskingAccountAliasUltraGridExtension { private static UltraGrid _grid; private static bool _maskingEnable; private static string _maskingCharacter; private static string _maskedColumnKey; private static int _bandIndex; public static void MaskedUltraGrid( this UltraGrid grid, int bandIndex, string maskedColumnKey, bool maskingEnable, string maskingCharacter ) { if( grid == null ) throw new ArgumentNullException( "grid" ); if( bandIndex < 0 ) throw new ArgumentOutOfRangeException("bandIndex"); if( maskedColumnKey == null ) throw new ArgumentNullException( "maskedColumnKey" ); if( maskingCharacter == null ) throw new ArgumentNullException( "maskingCharacter" ); _bandIndex = bandIndex; _maskedColumnKey = maskedColumnKey; _maskingEnable = maskingEnable; _maskingCharacter = maskingCharacter; _grid = grid; _grid.InitializeRow += new InitializeRowEventHandler( InitializeRowHandler ); _grid.InitializeLayout += new InitializeLayoutEventHandler( InitializeLayoutHandler ); } private static void InitializeRowHandler( object sender, InitializeRowEventArgs e ) { e.Row.Cells[_maskedColumnKey].Hidden = true; var masker = new Mask(); if( _maskingEnable && ((int)e.Row.Cells["Account_Alias_Type_Id"].Value == 1 || (int)e.Row.Cells["Account_Alias_Type_Id"].Value == 2 || (int)e.Row.Cells["Account_Alias_Type_Id"].Value == 4 || (int)e.Row.Cells["Account_Alias_Type_Id"].Value == 8 || (int)e.Row.Cells["Account_Alias_Type_Id"].Value == 9 || (int)e.Row.Cells["Account_Alias_Type_Id"].Value == 11 || (int)e.Row.Cells["Account_Alias_Type_Id"].Value == 14 || (int)e.Row.Cells["Account_Alias_Type_Id"].Value == 15 || (int)e.Row.Cells["Account_Alias_Type_Id"].Value == 16 ) ) { var masking = new Mask(); e.Row.Cells["Masked Column"].Value = masker.MaskString( e.Row.Cells[_maskedColumnKey].Value.ToString(), _maskingCharacter ); } else { e.Row.Cells["Masked Column"].Value = e.Row.Cells[_maskedColumnKey].Value.ToString(); } } private static void InitializeLayoutHandler( object sender, InitializeLayoutEventArgs e ) { UltraGridLayout layout = _grid.DisplayLayout; UltraGridBand band = layout.Bands[_bandIndex]; UltraGridColumn column = band.Columns[_maskedColumnKey]; column.Hidden = true; band.Columns.Add( "Masked Column", column.Header.ToString() ); var maskedColumn = band.Columns["Masked Column"]; maskedColumn.Header.Caption = column.Header.Caption; } }
Then I added just a little code after the InitializeComponent() method on the form's constructor:
InitializeComponent(); MaskingAccountAliasUltraGridExtension.MaskedUltraGrid( maskedGrid, 0, "Acct_Number", true, "X" ); maskedGrid.InitializeLayout += new InitializeLayoutEventHandler( maskedGrid_InitializeLayout ); var table = CreateDataSet().Tables["Account_Aliases"]; maskedGrid.DataSource = table;
I have to work out editing in some areas and I think I can do this through the extension. However, this will do nicely for 90% of what I am setting up. What's nice is I can add more InitializeLayout or InitializeRow handlers without having to worry about my formatting code. Any formatting for the extension's unbound column can be accessed with the unbound column name so this should be great.
Thanks for the assistance,