/// <summary> /// Sort data on specified column. /// </summary> /// <param name="columnIndex">Zero-based number of column to sort data.</param> /// <param name="startRow">First number of row to allow sort data.</param> /// <param name="endRow">Last number of number of row to allow sort data.</param> /// <param name="startColumn">First number of column to allow sort data.</param> /// <param name="endColumn">Last number of column to allow 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(int columnIndex, int startRow, int endRow, int startColumn, int endColumn, SortOrder order = SortOrder.Ascending, CellElementFlag moveElementFlag = CellElementFlag.Data, Func <int, int, object, object, int> cellDataComparer = null) { return(SortColumn(columnIndex, new RangePosition(startRow, startColumn, MaxContentRow - startRow + 1, endColumn - startColumn + 1), order, moveElementFlag, cellDataComparer)); }
/// <summary> /// Clear content inside specified range. /// </summary> /// <param name="addressOrName">Address or name to locate the range.</param> /// <param name="flags">Elements in cell specified by this flag to be removed.</param> public void ClearRangeContent(string addressOrName, CellElementFlag flags) { NamedRange namedRange; if (RangePosition.IsValidAddress(addressOrName)) { ClearRangeContent(new RangePosition(addressOrName), flags); } else if (this.TryGetNamedRange(addressOrName, out namedRange)) { ClearRangeContent(namedRange.Position, flags); } else { throw new InvalidAddressException(addressOrName); } }
/// <summary> /// Sort data inside specified range. /// </summary> /// <param name="columnIndex">Data will be sorted based on this column.</param> /// <param name="applyRange">Affect range.</param> /// <param name="order">Order of data sort.</param> /// <param name="cellDataComparer"></param> /// <returns></returns> public RangePosition SortColumn(int columnIndex, string applyRange, SortOrder order = SortOrder.Ascending, CellElementFlag moveElementFlag = CellElementFlag.Data, Func <int, int, object, object, int> cellDataComparer = null) { if (RangePosition.IsValidAddress(applyRange)) { return(this.SortColumn(columnIndex, new RangePosition(applyRange), order, moveElementFlag, cellDataComparer)); } else if (this.TryGetNamedRangePosition(applyRange, out var range)) { return(this.SortColumn(columnIndex, range, order, moveElementFlag, cellDataComparer)); } else { throw new InvalidAddressException(applyRange); } }
/// <summary> /// Sort data on specified column. /// </summary> /// <param name="columnIndex">Zero-based number of column to sort data.</param> /// <param name="applyRange">Data only be changed in this range during sort.</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(int columnIndex, RangePosition applyRange, SortOrder order = SortOrder.Ascending, CellElementFlag moveElementFlag = CellElementFlag.Data, Func <int, int, object, object, int> cellDataComparer = null) { var range = FixRange(applyRange); RangePosition affectRange = RangePosition.Empty; if (cellDataComparer != null) { this.cellDataComparer = cellDataComparer; } #if DEBUG Stopwatch sw = Stopwatch.StartNew(); #endif // DEBUG int[] sortedOrder = null; // stop fire events this.SuspendDataChangedEvents(); try { this.controlAdapter.ChangeCursor(CursorStyle.Busy); if (!this.CheckQuickSortRange(columnIndex, range.Row, range.EndRow, range.Col, range.EndCol, order, ref affectRange)) { throw new InvalidOperationException("Cannot change a part of range, all cells should be having same colspan on column."); } this.QuickSortColumn(columnIndex, range.Row, range.EndRow, range.Col, range.EndCol, order, ref affectRange, cellDataComparer == null ? (Func <int, int, object, int>)CompareCell : UserCellDataComparerAdapter, sortedOrder); #if DEBUG sw.Stop(); if (sw.ElapsedMilliseconds > 10) { Debug.WriteLine(string.Format("sort column by {0} on [{1}-{2}]: {3} ms", columnIndex, range.Col, range.EndCol, sw.ElapsedMilliseconds)); } #endif // DEBUG } finally { // resume to fire events this.ResumeDataChangedEvents(); } this.RequestInvalidate(); this.controlAdapter.ChangeCursor(CursorStyle.PlatformDefault); if (!affectRange.IsEmpty) { for (int c = affectRange.Col; c <= affectRange.EndCol; c++) { var header = this.cols[c]; if (header.Body != null) { header.Body.OnDataChange(affectRange.Row, affectRange.EndRow); } } this.RaiseRangeDataChangedEvent(affectRange); this.RowsSorted?.Invoke(this, new Events.RangeEventArgs(affectRange)); } return(affectRange); }
/// <summary> /// Sort data on specified column. /// </summary> /// <param name="columnIndex">Zero-based number of column to sort data</param> /// <param name="titleRows">Indicates that how many title rows exist at the top of worksheet, /// Title rows will not be included in sort apply range.</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(int columnIndex, int titleRows, SortOrder order = SortOrder.Ascending, CellElementFlag moveElementFlag = CellElementFlag.Data, Func <int, int, object, object, int> cellDataComparer = null) { return(SortColumn(columnIndex, titleRows, MaxContentRow, 0, MaxContentCol, order, moveElementFlag, cellDataComparer)); }
/// <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> /// Clear content inside specified range. /// </summary> /// <param name="range">The range to be clear.</param> /// <param name="flags">Elements in cell specified by this flag to be removed.</param> /// <param name="checkReadonly">True to ignore read-only cells; False to delete content from read-only cells.</param> public void ClearRangeContent(RangePosition range, CellElementFlag flags, bool checkReadonly = true) { var fixedRange = FixRange(range); bool deleteData = (flags & CellElementFlag.Data) == CellElementFlag.Data; bool deleteFormula = (flags & CellElementFlag.Formula) == CellElementFlag.Formula; bool deleteDataFormat = (flags & CellElementFlag.DataFormat) == CellElementFlag.DataFormat; bool deleteBody = (flags & CellElementFlag.Body) == CellElementFlag.Body; bool deleteStyle = (flags & CellElementFlag.Style) == CellElementFlag.Style; int maxcol = range.Col; #if FORMULA List <Cell> formulaDirtyCells = new List <Cell>(); #endif // FORMULA for (int r = fixedRange.Row; r <= fixedRange.EndRow; r++) { for (int c = fixedRange.Col; c <= fixedRange.EndCol; c++) { var cell = this.cells[r, c]; if (cell != null) { bool dirty = false; if ((!checkReadonly || !cell.IsReadOnly)) { if (deleteData) { cell.InnerData = null; cell.InnerDisplay = string.Empty; if (cell.InnerStyle.HAlign == ReoGridHorAlign.General) { cell.RenderHorAlign = ReoGridRenderHorAlign.Left; } dirty = true; } if (deleteFormula) { cell.InnerFormula = null; #if FORMULA ClearCellReferenceList(cell); #endif // FORMULA } if (deleteDataFormat) { cell.DataFormat = DataFormat.CellDataFormatFlag.General; cell.DataFormatArgs = null; dirty = true; } if (deleteBody) { cell.body = null; dirty = true; } if (dirty) { if (maxcol < c) { maxcol = c; } //cell.RenderTextBounds = RGRectF.Empty; //cell.RenderColor = null; //cell.RenderScaleFactor = this.scaleFactor; // TODO: auto adjust row height #if FORMULA foreach (var referecedRange in this.formulaRanges) { if (referecedRange.Value.Any(rr => rr.Contains(cell.InternalPos)) && !formulaDirtyCells.Contains(referecedRange.Key)) { formulaDirtyCells.Add(referecedRange.Key); } } #endif // FORMULA this.RaiseCellDataChangedEvent(cell); } } } } } if (deleteStyle) { this.RemoveRangeStyles(fixedRange, PlainStyleFlag.All); } #if FORMULA foreach (var dirtyCell in formulaDirtyCells) { RecalcCell(dirtyCell); } #endif // FORMULA // only update the changed columns (up to maxcol) for (int i = fixedRange.Col; i <= maxcol; i++) { var header = this.cols[i]; if (header.Body != null) { header.Body.OnDataChange(fixedRange.Row, fixedRange.EndRow); } } //var fromRange = FixRange(range); //if (flags == CellElementFlag.All) //{ // for (int r = fromRange.Row; r <= fromRange.EndRow; r++) // { // for (int c = fromRange.Col; c <= fromRange.EndCol; c++) // { // this.cells[r, c] = null; // } // } // InvalidateCanvas(); //} //else //{ // if ((flags & CellElementFlag.Data) == CellElementFlag.Data) // { // DeleteRangeData(fromRange, true); // } // if ((flags & CellElementFlag.Body) == CellElementFlag.Body) // { // this.IterateCells(fromRange, (r, c, cell) => // { // cell.body = null; // return true; // }); // InvalidateCanvas(); // } // if ((flags & CellElementFlag.DataFormat) == CellElementFlag.DataFormat) // { // this.DeleteRangeDataFormat(fromRange); // } // if ((flags & CellElementFlag.Style) == CellElementFlag.Style) // { // RemoveRangeStyle(fromRange, PlainStyleFlag.All); // } //} if ((flags & CellElementFlag.Border) == CellElementFlag.Border) { this.RemoveRangeBorders(fixedRange, BorderPositions.All); } RequestInvalidate(); }