/// <summary> /// Create instance with alphabet code of position. (e.g. new CellPosition("A10")) /// </summary> /// <param name="address">an address string to locate the cell in spreadsheet. (e.g. 'A10')</param> /// <exception cref="ArgumentException">if address is not in correct format.</exception> /// <example>var pos = new CellPosition("A10");</example> public CellPosition(string address) { Match m = RGUtility.CellReferenceRegex.Match(address); if (!m.Success) { throw new ArgumentException("invalid address for cell: " + address, "id"); } this.row = 0; int.TryParse(m.Groups["row"].Value, out row); row--; this.col = RGUtility.GetNumberOfChar(m.Groups["col"].Value); this.positionProperties = 0; if (m.Groups["abs_row"].Success) { this.positionProperties |= PositionAbsoluteBits.StartRow; } if (m.Groups["abs_col"].Success) { this.positionProperties |= PositionAbsoluteBits.StartCol; } }
///// <summary> ///// Create filter on specified column. ///// </summary> ///// <param name="column">Column code that locates a column to create filter.</param> ///// <param name="titleRows">Indicates how many title rows exist at the top of worksheet, ///// title rows will not be included in filter range.</param> ///// <returns>Instance of column filter.</returns> //public AutoColumnFilter CreateColumnFilter(string column, int titleRows = 0, // AutoColumnFilterUI columnFilterUI = AutoColumnFilterUI.DropdownButtonAndPane) //{ // return CreateColumnFilter(column, column, titleRows, columnFilterUI); //} /// <summary> /// Create column filter. /// </summary> /// <param name="startColumn">First column specified by an address to create filter.</param> /// <param name="endColumn">Last column specified by an address to the filter.</param> /// <param name="titleRows">Indicates that how many title rows exist at the top of spreadsheet, /// title rows will not be included in filter apply range.</param> /// <param name="columnFilterUI">Indicates whether allow to create graphics user interface (GUI), /// by default the dropdown-button on the column and candidates dropdown-panel will be created. /// Set this argument as NoGUI to create filter without GUI.</param> /// <returns>Instance of column filter.</returns> public AutoColumnFilter CreateColumnFilter(string startColumn, string endColumn, int titleRows = 0, AutoColumnFilterUI columnFilterUI = AutoColumnFilterUI.DropdownButtonAndPanel) { int startIndex = RGUtility.GetNumberOfChar(startColumn); int endIndex = RGUtility.GetNumberOfChar(endColumn); return(CreateColumnFilter(startIndex, endIndex, titleRows, columnFilterUI)); }
/// <summary> /// Try get a named range by specified name /// </summary> /// <param name="name">name for range to be get</param> /// <param name="namedRange">output argument, range assoicated with specified name will be returned</param> /// <returns>true if specified name exists and the instance of range has been returned from spreadsheet, otherwise false</returns> public bool TryGetNamedRange(string name, out NamedRange namedRange) { if (!RGUtility.IsValidName(name)) { namedRange = null; return(false); } else { return(registeredNamedRanges.TryGetValue(name, out namedRange)); } }
/// <summary> /// Paste data from tabbed string into worksheet. /// </summary> /// <param name="startPos">Start position to fill data.</param> /// <param name="content">Tabbed string to be pasted.</param> /// <returns>Range position that indicates the actually filled range.</returns> public RangePosition PasteFromString(CellPosition startPos, string content) { //int rows = 0, cols = 0; //string[] lines = content.Split(new string[] { "\r\n" }, StringSplitOptions.None); //for (int r = 0; r < lines.Length; r++) //{ // string line = lines[r]; // if (line.EndsWith("\n")) line = line.Substring(0, line.Length - 1); // //line = line.Trim(); // if (line.Length > 0) // { // string[] tabs = line.Split('\t'); // cols = Math.Max(cols, tabs.Length); // for (int c = 0; c < tabs.Length; c++) // { // int toRow = startPos.Row + r; // int toCol = startPos.Col + c; // if (!this.IsValidCell(toRow, toCol)) // { // throw new RangeIntersectionException(new RangePosition(toRow, toCol, 1, 1)); // } // string text = tabs[c]; // if (text.StartsWith("\"") && text.EndsWith("\"")) // { // text = text.Substring(1, text.Length - 2); // } // SetCellData(toRow, toCol, text); // } // rows++; // } //} object[,] parsedData = RGUtility.ParseTabbedString(content); int rows = parsedData.GetLength(0); int cols = parsedData.GetLength(1); var range = new RangePosition(startPos.Row, startPos.Col, rows, cols); this.SetRangeData(range, parsedData); return(range); }
internal bool TryGetNamedRangePosition(string name, out RangePosition range) { if (!RGUtility.IsValidName(name)) { range = RangePosition.Empty; return(false); } else { NamedRange namedRange; registeredNamedRanges.TryGetValue(name, out namedRange); range = namedRange.Position; return(true); } }
/// <summary> /// Remove a highlighted range from specified address /// </summary> /// <param name="address">address to remove highlighted range</param> /// <returns>true if range removed successfully</returns> public bool RemoveHighlightRange(string address) { if (RangePosition.IsValidAddress(address)) { return(RemoveHighlightRange(new RangePosition(address))); } else if (RGUtility.IsValidName(address)) { if (registeredNamedRanges.TryGetValue(address, out var refRange)) { return(RemoveHighlightRange(refRange)); } } return(false); }
/// <summary> /// Select speicifed range on spreadsheet /// </summary> /// <param name="address">address or name of specified range to be selected</param> public void SelectRange(string address) { // range address if (RangePosition.IsValidAddress(address)) { SelectRange(new RangePosition(address)); } // named range else if (RGUtility.IsValidName(address)) { NamedRange refRange; if (registeredNamedRanges.TryGetValue(address, out refRange)) { SelectRange(refRange); } } }
/// <summary> /// Crearte and display a highlighted range at specified position on worksheet /// </summary> /// <param name="address">Address or name to locate a range on worksheet</param> /// <returns>Instance of highlight range on worksheet</returns> public HighlightRange AddHighlightRange(string address) { if (RangePosition.IsValidAddress(address)) { return(AddHighlightRange(new RangePosition(address))); } else if (RGUtility.IsValidName(address)) { NamedRange refRange; if (registeredNamedRanges.TryGetValue(address, out refRange)) { return(AddHighlightRange(refRange)); } } return(null); }
/// <summary> /// Convert position into relative address string. (format: A1) /// </summary> /// <returns>Related address in string.</returns> /// <seealso cref="ToAbsoluteAddress"/> public string ToAddress() { if (this.positionProperties == (PositionAbsoluteBits.StartRow | PositionAbsoluteBits.StartCol)) { return("$" + RGUtility.GetAlphaChar(col) + "$" + (row + 1)); } else if (this.positionProperties == PositionAbsoluteBits.StartCol) { return("$" + RGUtility.GetAlphaChar(col) + (row + 1)); } else if (this.positionProperties == PositionAbsoluteBits.StartRow) { return(RGUtility.GetAlphaChar(col) + "$" + (row + 1)); } else { return(RGUtility.GetAlphaChar(col) + (row + 1)); } }
/// <summary> /// Convert range into address string A1:B1 style. /// </summary> /// <returns>Address string converted from range position.</returns> public string ToRelativeAddress() { if (this.rows <= -1 && this.cols <= -1) { return("1:1048576"); } else if (this.cols <= -1) { return(string.Format("{0}:{1}", this.row + 1, this.EndRow + 1)); } else if (this.rows <= -1) { return(string.Format("{0}:{1}", RGUtility.GetAlphaChar(this.col), RGUtility.GetAlphaChar(this.EndCol))); } else { return(string.Format("{0}{1}:{2}{3}", RGUtility.GetAlphaChar(this.col), this.row + 1, RGUtility.GetAlphaChar(this.EndCol), this.EndRow + 1)); } }
/// <summary> /// Convert into absolute address. /// </summary> /// <returns>absolute address identifier.</returns> public string ToAbsoluteAddress() { if (this.rows <= -1 && this.cols <= -1) { return("$1:$1048576"); } else if (this.cols <= -1) { return(string.Format("${0}:${1}", this.row + 1, this.EndRow + 1)); } else if (this.rows <= -1) { return(string.Format("${0}:${1}", RGUtility.GetAlphaChar(this.col), RGUtility.GetAlphaChar(this.EndCol))); } else { return(string.Format("${0}${1}:${2}${3}", RGUtility.GetAlphaChar(this.col), this.row + 1, RGUtility.GetAlphaChar(this.EndCol), this.EndRow + 1)); } }
/// <summary> /// Convert position or range into address stringConvert position or range into address string /// </summary> /// <param name="row">Zero-based index number of row</param> /// <param name="col">Zero-based index number of column</param> /// <param name="rows">Zero-based number of rows</param> /// <param name="cols">Zero-based number of columns</param> /// <param name="absNum">Determine that which R1C1 format should be used.<br/> /// <ul> /// <li>1: [Absolute Row][Absolute Col] R1C1</li> /// <li>2: [Absolute Row][Relative Col] R1C[1]</li> /// <li>3: [Relative Row][Absolute Col] R[1]C1</li> /// <li>4: [Relative Row][Relative Col] R[1]C[1]</li> /// </ul> /// </param> /// <param name="a1style">true to use A1 style; false use the R1C1 style</param> /// <returns>position or range in address string</returns> public static string ToAddress(int row, int col, int rows, int cols, int absNum, bool a1style) { if (rows <= 1 && cols <= 1) { // pos if (a1style) { return(RGUtility.GetAlphaChar(col) + (row + 1)); } else { switch (absNum) { default: case 1: // absolute row, absolute col return(string.Format("R{0}C{1}", row, col)); case 2: // absolute row, relative col return(string.Format("R{0}C[{1}]", row, col)); case 3: // relative row, absolute col return(string.Format("R[{0}]C{1}", row, col)); case 4: // relative row, relative col return(string.Format("R[{0}]C[{1}]", row, col)); } } } else { // range int toRow = row + rows - 1; int toCol = col + cols - 1; return(ToAddress(row, col, absNum, a1style) + ":" + ToAddress(row, col, absNum, a1style)); } }
/// <summary> /// Convert position into absolute address string. (format: $A$1) /// </summary> /// <returns>Absolute address in string</returns> /// <seealso cref="ToAddress"/> public string ToAbsoluteAddress() { return(string.Format("${0}${1}", RGUtility.GetAlphaChar(this.col), this.row + 1)); }
/// <summary> /// Check whether or not the specified name is valid name /// </summary> /// <param name="name">Name to be checked</param> /// <returns>True if specified name is valid; otherwise return false</returns> public static bool IsValidName(string name) { return(RGUtility.IsValidName(name)); }
/// <summary> /// Copy data from Clipboard and put on grid. /// /// Currently ReoGrid supports the following types of source from the clipboard. /// - Data from another ReoGrid instance /// - Plain/Unicode Text from any Windows Applications /// - Tabbed Plain/Unicode Data from Excel or similar applications /// /// When data copied from another ReoGrid instance, and the destination range /// is bigger than the source, ReoGrid will try to repeat putting data to fill /// the destination range entirely. /// /// Todo: Copy border and cell style from Excel. /// </summary> public bool Paste() { if (IsEditing) { this.controlAdapter.EditControlPaste(); } else { // Paste method will always perform action to do paste // do nothing if in readonly mode if (this.HasSettings(WorksheetSettings.Edit_Readonly) // or selection is empty || this.selectionRange.IsEmpty) { return(false); } try { this.controlAdapter.ChangeCursor(CursorStyle.Busy); PartialGrid partialGrid = null; string clipboardText = null; #if WINFORM || WPF DataObject data = Clipboard.GetDataObject() as DataObject; if (data != null) { partialGrid = data.GetData(ClipBoardDataFormatIdentify) as PartialGrid; if (data.ContainsText()) { clipboardText = data.GetText(); } } #elif ANDROID #endif // WINFORM || WPF if (partialGrid != null) { #region Partial Grid Pasting int startRow = selectionRange.Row; int startCol = selectionRange.Col; int rows = partialGrid.Rows; int cols = partialGrid.Columns; int rowRepeat = 1; int colRepeat = 1; if (selectionRange.Rows % partialGrid.Rows == 0) { rows = selectionRange.Rows; rowRepeat = selectionRange.Rows / partialGrid.Rows; } if (selectionRange.Cols % partialGrid.Columns == 0) { cols = selectionRange.Cols; colRepeat = selectionRange.Cols / partialGrid.Columns; } var targetRange = new RangePosition(startRow, startCol, rows, cols); if (!RaiseBeforePasteEvent(targetRange)) { return(false); } if (targetRange.EndRow >= this.rows.Count || targetRange.EndCol >= this.cols.Count) { // TODO: paste range overflow // need to notify user-code to handle this return(false); } // check whether the range to be pasted contains readonly cell if (this.CheckRangeReadonly(targetRange)) { this.NotifyExceptionHappen(new OperationOnReadonlyCellException("specified range contains readonly cell")); return(false); } // check any intersected merge-range in partial grid // bool cancelPerformPaste = false; if (partialGrid.Cells != null) { try { #region Check repeated intersected ranges for (int rr = 0; rr < rowRepeat; rr++) { for (int cc = 0; cc < colRepeat; cc++) { partialGrid.Cells.Iterate((row, col, cell) => { if (cell.IsMergedCell) { for (int r = startRow; r < cell.MergeEndPos.Row - cell.InternalRow + startRow + 1; r++) { for (int c = startCol; c < cell.MergeEndPos.Col - cell.InternalCol + startCol + 1; c++) { int tr = r + rr * partialGrid.Rows; int tc = c + cc * partialGrid.Columns; var existedCell = cells[tr, tc]; if (existedCell != null) { if ( // cell is a part of merged cell (existedCell.Rowspan == 0 && existedCell.Colspan == 0) // cell is merged cell || existedCell.IsMergedCell) { throw new RangeIntersectionException(selectionRange); } // cell is readonly else if (existedCell.IsReadOnly) { throw new CellDataReadonlyException(cell.InternalPos); } } } } } return(Math.Min(cell.Colspan, (short)1)); }); } } #endregion // Check repeated intersected ranges } catch (Exception ex) { cancelPerformPaste = true; // raise event to notify user-code there is error happened during paste operation if (OnPasteError != null) { OnPasteError(this, new RangeOperationErrorEventArgs(selectionRange, ex)); } } } if (!cancelPerformPaste) { DoAction(new SetPartialGridAction(new RangePosition( startRow, startCol, rows, cols), partialGrid)); } #endregion // Partial Grid Pasting } else if (!string.IsNullOrEmpty(clipboardText)) { #region Plain Text Pasting var arrayData = RGUtility.ParseTabbedString(clipboardText); int rows = Math.Max(selectionRange.Rows, arrayData.GetLength(0)); int cols = Math.Max(selectionRange.Cols, arrayData.GetLength(1)); var targetRange = new RangePosition(selectionRange.Row, selectionRange.Col, rows, cols); if (!RaiseBeforePasteEvent(targetRange)) { return(false); } if (this.controlAdapter != null) { var actionSupportedControl = this.controlAdapter.ControlInstance as IActionControl; if (actionSupportedControl != null) { actionSupportedControl.DoAction(this, new SetRangeDataAction(targetRange, arrayData)); } } #endregion // Plain Text Pasting } } catch (Exception ex) { // raise event to notify user-code there is error happened during paste operation //if (OnPasteError != null) //{ // OnPasteError(this, new RangeOperationErrorEventArgs(selectionRange, ex)); //} this.NotifyExceptionHappen(ex); } finally { this.controlAdapter.ChangeCursor(CursorStyle.Selection); RequestInvalidate(); } if (AfterPaste != null) { AfterPaste(this, new RangeEventArgs(this.selectionRange)); } } return(true); }
/// <summary> /// Copy data from Clipboard and put on grid. /// /// Currently ReoGrid supports the following types of source from the clipboard. /// - Data from another ReoGrid instance /// - Plain/Unicode Text from any Windows Applications /// - Tabbed Plain/Unicode Data from Excel or similar applications /// /// When data copied from another ReoGrid instance, and the destination range /// is bigger than the source, ReoGrid will try to repeat putting data to fill /// the destination range entirely. /// /// Todo: Copy border and cell style from Excel. /// </summary> public bool Paste() { if (IsEditing) { this.controlAdapter.EditControlPaste(); } else { // Paste method will always perform action to do paste // do nothing if in readonly mode if (this.HasSettings(WorksheetSettings.Edit_Readonly) // or selection is empty || this.selectionRange.IsEmpty) { return(false); } try { this.controlAdapter.ChangeCursor(CursorStyle.Busy); PartialGrid partialGrid = null; string clipboardText = null; #if WINFORM || WPF DataObject data = Clipboard.GetDataObject() as DataObject; if (data != null) { partialGrid = data.GetData(ClipBoardDataFormatIdentify) as PartialGrid; if (data.ContainsText()) { clipboardText = data.GetText(); } } #elif ANDROID #endif // WINFORM || WPF if (partialGrid != null) { #region Partial Grid Pasting int startRow = selectionRange.Row; int startCol = selectionRange.Col; int rows = partialGrid.Rows; int cols = partialGrid.Columns; int rowRepeat = 1; int colRepeat = 1; if (selectionRange.Rows % partialGrid.Rows == 0) { rows = selectionRange.Rows; rowRepeat = selectionRange.Rows / partialGrid.Rows; } if (selectionRange.Cols % partialGrid.Columns == 0) { cols = selectionRange.Cols; colRepeat = selectionRange.Cols / partialGrid.Columns; } var targetRange = new RangePosition(startRow, startCol, rows, cols); if (!RaiseBeforePasteEvent(targetRange)) { return(false); } if (targetRange.EndRow >= this.rows.Count || targetRange.EndCol >= this.cols.Count) { // TODO: paste range overflow // need to notify user-code to handle this return(false); } // check whether the range to be pasted contains readonly cell if (this.CheckRangeReadonly(targetRange)) { this.NotifyExceptionHappen(new OperationOnReadonlyCellException("specified range contains readonly cell")); return(false); } DoAction(new SetPartialGridAction(new RangePosition( startRow, startCol, rows, cols), partialGrid)); #endregion // Partial Grid Pasting } else if (!string.IsNullOrEmpty(clipboardText)) { #region Plain Text Pasting var arrayData = RGUtility.ParseTabbedString(clipboardText); int rows = Math.Max(selectionRange.Rows, arrayData.GetLength(0)); int cols = Math.Max(selectionRange.Cols, arrayData.GetLength(1)); var targetRange = new RangePosition(selectionRange.Row, selectionRange.Col, rows, cols); if (!RaiseBeforePasteEvent(targetRange)) { return(false); } if (this.controlAdapter != null) { var actionSupportedControl = this.controlAdapter.ControlInstance as IActionControl; if (actionSupportedControl != null) { actionSupportedControl.DoAction(this, new SetRangeDataAction(targetRange, arrayData)); } } #endregion // Plain Text Pasting } } catch (Exception ex) { // raise event to notify user-code there is error happened during paste operation //if (OnPasteError != null) //{ // OnPasteError(this, new RangeOperationErrorEventArgs(selectionRange, ex)); //} this.NotifyExceptionHappen(ex); } finally { this.controlAdapter.ChangeCursor(CursorStyle.Selection); RequestInvalidate(); } if (AfterPaste != null) { AfterPaste(this, new RangeEventArgs(this.selectionRange)); } } return(true); }
public string ToAddress() { if (this.rows <= -1 && this.cols <= -1) { #region full rows and cols StringBuilder sb = new StringBuilder(); if (this.StartRowProperty == PositionProperty.Absolute) { sb.Append('$'); } sb.Append("1:"); if (this.EndRowProperty == PositionProperty.Absolute) { sb.Append('$'); } sb.Append("1048576"); return(sb.ToString()); #endregion // full rows and cols } else if (this.cols <= -1) { #region full cols StringBuilder sb = new StringBuilder(); if (this.StartRowProperty == PositionProperty.Absolute) { sb.Append('$'); } sb.Append(this.row + 1); sb.Append(':'); if (this.EndRowProperty == PositionProperty.Absolute) { sb.Append('$'); } sb.Append(this.EndRow + 1); return(sb.ToString()); #endregion // full cols } else if (this.rows <= -1) { #region full rows StringBuilder sb = new StringBuilder(); if (this.StartColumnProperty == PositionProperty.Absolute) { sb.Append('$'); } sb.Append(RGUtility.GetAlphaChar(this.col)); sb.Append(':'); if (this.EndColumnProperty == PositionProperty.Absolute) { sb.Append('$'); } sb.Append(RGUtility.GetAlphaChar(this.EndCol)); return(sb.ToString()); #endregion // full rows } else { #region normal address StringBuilder sb = new StringBuilder(); // start if (this.StartColumnProperty == PositionProperty.Absolute) { sb.Append('$'); } sb.Append(RGUtility.GetAlphaChar(this.col)); if (this.StartRowProperty == PositionProperty.Absolute) { sb.Append('$'); } sb.Append(this.row + 1); sb.Append(':'); // end if (this.EndColumnProperty == PositionProperty.Absolute) { sb.Append('$'); } sb.Append(RGUtility.GetAlphaChar(this.EndCol)); if (this.EndRowProperty == PositionProperty.Absolute) { sb.Append('$'); } sb.Append(this.EndRow + 1); return(sb.ToString()); #endregion // normal address } }
/// <summary> /// Sort data on specified column. /// </summary> /// <param name="columnAddress">Base column specified by an address to sort data.</param> /// <param name="order">Order of data sort.</param> /// <param name="cellDataComparer">Custom cell data comparer, compares two cells and returns an integer. /// Set this value to null to use default built-in comparer.</param> /// <returns>Data changed range</returns> public RangePosition SortColumn(string columnAddress, SortOrder order = SortOrder.Ascending, CellElementFlag moveElementFlag = CellElementFlag.Data, Func <int, int, object, object, int> cellDataComparer = null) { return(SortColumn(RGUtility.GetNumberOfChar(columnAddress), order, moveElementFlag, cellDataComparer)); }
/// <summary> /// Create range position from a range or cell address. /// </summary> /// <param name="address">Address to locate the range position.</param> /// <exception cref="ArgumentException">Throw when specified address is invalid.</exception> public RangePosition(string address) { this.positionProperties = 0; Match m = RGUtility.RangeReferenceRegex.Match(address); if (!m.Success) { m = RGUtility.SingleAbsoulteRangeRegex.Match(address); } if (!m.Success && CellPosition.IsValidAddress(address)) { var pos = new CellPosition(address); this.row = pos.Row; this.col = pos.Col; this.rows = 1; this.cols = 1; this.StartRowProperty = pos.RowProperty; this.StartColumnProperty = pos.ColumnProperty; this.EndRowProperty = pos.RowProperty; this.EndColumnProperty = pos.ColumnProperty; } else { if (!m.Success //|| (m.Groups["to_col"].Length <= 0 || m.Groups["to_row"].Length <= 0 //|| m.Groups["from_col"].Length <= 0 || m.Groups["from_row"].Length <= 0) ) { throw new ArgumentException("range is invalid: " + address); } int fromCol = 0, fromRow = 0, toCol = 0, toRow = 0; bool fullRows = false, fullCols = false; if (m.Groups["from_row"].Success) { if (!int.TryParse(m.Groups["from_row"].Value, out fromRow)) { throw new ArgumentException("range is invalid: " + address); } fromRow--; } else { fullRows = true; } if (!fullRows && m.Groups["to_row"].Success) { if (!int.TryParse(m.Groups["to_row"].Value, out toRow)) { throw new ArgumentException("range is invalid: " + address); } toRow--; } if (m.Groups["from_col"].Success) { fromCol = RGUtility.GetNumberOfChar(m.Groups["from_col"].Value); } else { fullCols = true; } if (!fullCols && m.Groups["to_col"].Success) { toCol = RGUtility.GetNumberOfChar(m.Groups["to_col"].Value); } if (fullCols) { this.row = Math.Min(fromRow, toRow); this.rows = Math.Max(fromRow, toRow) - this.row + 1; this.col = 0; this.cols = -1; } else if (fullRows) { this.row = 0; this.rows = -1; this.col = Math.Min(fromCol, toCol); this.cols = Math.Max(fromCol, toCol) - this.col + 1; } else { this.row = Math.Min(fromRow, toRow); this.col = Math.Min(fromCol, toCol); this.rows = Math.Max(fromRow, toRow) - this.row + 1; this.cols = Math.Max(fromCol, toCol) - this.col + 1; } if (m.Groups["abs_from_row"].Success) { this.positionProperties |= PositionAbsoluteBits.StartRow; } if (m.Groups["abs_from_col"].Success) { this.positionProperties |= PositionAbsoluteBits.StartCol; } if (m.Groups["abs_to_row"].Success) { this.positionProperties |= PositionAbsoluteBits.EndRow; } if (m.Groups["abs_to_col"].Success) { this.positionProperties |= PositionAbsoluteBits.EndCol; } } }
public string ToRelativeAddress() { return(RGUtility.GetAlphaChar(col) + (row + 1)); }