/// <summary> /// Create a Spreadsheet Cell. /// </summary> /// <param name="spreadsheetRow">The spreadsheet row associated with this cell.</param> /// <param name="spreadsheetColumn">The spreadsheet column associated with this cell.</param> public SpreadsheetCell(SpreadsheetRow spreadsheetRow, SpreadsheetColumn spreadsheetColumn) { // Initialize the Object. this.spreadsheetRow = spreadsheetRow; this.spreadsheetColumn = spreadsheetColumn; this.style = spreadsheetColumn.Style; this.IsModified = true; }
public AnimatedCell(SpreadsheetColumn spreadsheetColumn, SpreadsheetRow spreadsheetRow, Style[] animationArray) { // Initialize the object. Note that the first color in the sequence was set when the change in value was discovered, so // the sequencing actually begins with the second style in the sequence of colors. this.AnimationIndex = 1; this.AnimationArray = animationArray; this.SpreadsheetColumn = spreadsheetColumn; this.SpreadsheetRow = spreadsheetRow; }
/// <summary> /// Compares an AnimatedCell address to another object. /// </summary> /// <param name="obj"></param> /// <returns></returns> public int CompareTo(object obj) { // Compare this against another AnimatedCell. if (obj is AnimatedCell) { AnimatedCell operand = (AnimatedCell)obj; // The rows are compared by testing the primary key elements of each row. foreach (DataColumn dataColumn in this.SpreadsheetRow.Table.PrimaryKey) { int comparison = this.SpreadsheetRow[dataColumn].CompareTo(operand.SpreadsheetRow[dataColumn]); if (comparison != 0) { return(comparison); } } // If the two rows are the same, compare the column. return(this.SpreadsheetColumn.Ordinal.CompareTo(operand.SpreadsheetColumn.Ordinal)); } // Compare this object against the column and row addresses. if (obj is object[]) { // Extract the row and column from the arguments. SpreadsheetColumn spreadsheetColumn = (SpreadsheetColumn)((object[])obj)[0]; SpreadsheetRow spreadsheetRow = (SpreadsheetRow)((object[])obj)[1]; // The rows are compared by testing the primary key elements of each row. foreach (DataColumn dataColumn in this.SpreadsheetRow.Table.PrimaryKey) { int comparison = this.SpreadsheetRow[dataColumn].CompareTo(spreadsheetRow[dataColumn]); if (comparison != 0) { return(comparison); } } // If the two rows are the same, compare the column. return(this.SpreadsheetColumn.Ordinal.CompareTo(spreadsheetColumn.Ordinal)); } // Any other comparison isn't supported. throw new Exception(string.Format("Can't compare {0} to type {1}", obj.GetType().ToString(), this.GetType().ToString())); }
/// <summary> /// Measures the document, row and cell dimensions. /// </summary> /// <returns>A region of the document that has changed.</returns> internal Region MeasureData() { // This area will collect all the rectangles that need to be updated in the viewer. Region region = new Region(); region.MakeEmpty(); // This is used to collect smaller row and cell rectangles into the largest possible area. The idea is to minimzed the // number of rectangles that the GDI has to handle when the document is redrawn. Of course, this could be done with an // infinite region, but the idea is also to minimize the amount of drawing that needs to be done. Rectangle aggregateRectangle = Rectangle.Empty; // This is used as a relatively static area where the dimensions of a row in the current view are calcualted. Rectangle rowRectangle = Rectangle.Empty; // These provide cursors for keeping track of the location as the document is measured. Point displayCursor = Point.Empty; Point printerCursor = Point.Empty; // Initialize the total area of the screen and printed document. The width of the documents has been calculated by // this point (it is calculated in 'MeasureHeader', but the length has not yet been calculated. this.displayRectangle = new Rectangle(0, 0, this.headerRectangle.Width, 0); // The default view defines which rows are visible and in what order the appear. It defines the 'visible' document. // This code will run through the visible document and assign coordinates to the rows and cells according to their // place in the view. int rowIndex = 0; foreach (DataRowView dataRowView in this.spreadsheetRowView) { // The underlying row will be updated with coordinates that represent its location in the view. SpreadsheetRow spreadsheetRow = (SpreadsheetRow)dataRowView.Row; // This index gives the row position within the sorted and filtered view. spreadsheetRow.RowViewIndex = rowIndex++; // This define the coordinates of this row in the view. rowRectangle.X = displayCursor.X; rowRectangle.Y = displayCursor.Y; rowRectangle.Width = this.displayRectangle.Width; rowRectangle.Height = spreadsheetRow.rectangle.Height; // The code below will check the coordinates of the rows and cells with the goal of preserving the data that hasn't // changed. However, if the row is invalid, there's no need to add the cells to the update region. If every cell // of every row were entered into the region, it would choke the GDI. If the row is out of place, this flag will // skip the cell checking logic. bool isRowValid = true; // If the position and height of the row don't agree with where it should be in the DataView, then the coordinates // will have to be updated and the row will be added to the list of things that need redrawing. if (spreadsheetRow.rectangle.Location != rowRectangle.Location || spreadsheetRow.rectangle.Height != rowRectangle.Height || !spreadsheetRow.IsVisible) { // The entire row will be added to the update region, there's no need to add the cell rectangles as well. // This is done as an optimization; if ever cell in a large document were modified at once, it would blow out // the storage are for a region. isRowValid = false; // Update the coordinates of the row with the calculated rectangle from the view. spreadsheetRow.rectangle = rowRectangle; // This will combine the rectangles when the top and bottom borders coincide. The aggregate rectangle collects // individual rows into one large rectangle and places the largest contiguous rectangle possible into the // region. This simplifies the work the GDI has to do to process the invalid areas of the document. if (aggregateRectangle.Left == rowRectangle.Left && aggregateRectangle.Width == rowRectangle.Width && aggregateRectangle.Bottom == rowRectangle.Top) { aggregateRectangle = Rectangle.Union(aggregateRectangle, rowRectangle); } else { if (!aggregateRectangle.IsEmpty) { region.Union(aggregateRectangle); } aggregateRectangle = rowRectangle; } } // This will calculate the location of each of the cells in document coordinates. If the coordinates are // different from the current coordinates that cell will be added to the update region, assuming the row hasn't // already been added. foreach (SpreadsheetColumn spreadsheetColumn in this.ViewColumns) { SpreadsheetCell spreadsheetCell = spreadsheetRow[spreadsheetColumn]; // This is the rectangle that this cell should occupy. Rectangle cellRectangle = new Rectangle(displayCursor, new Size(spreadsheetColumn.Rectangle.Width, spreadsheetRow.rectangle.Height)); // This is the test to see if this cell needs to be redrawn. If the row isn't valid, then we don't need // to redraw the cell because it will be redrawn when the row is painted. If the dimensions of the cell // have changed, then it needs to be repainted. Finally, if there was an original version of this cell and // it isn't the same as the suggested cell, it needs to be repainted. if (isRowValid && (spreadsheetCell.DisplayRectangle != cellRectangle || spreadsheetCell.IsModified)) { // This will aggregate the cells into larger rectangles if their edges cooincide. The large contiguous // rectangles are added to the area that needs to be redrawn. if (aggregateRectangle.Top == cellRectangle.Top && aggregateRectangle.Height == cellRectangle.Height && aggregateRectangle.Right == cellRectangle.Left) { aggregateRectangle = Rectangle.Union(aggregateRectangle, cellRectangle); } else { if (!aggregateRectangle.IsEmpty) { region.Union(aggregateRectangle); } aggregateRectangle = cellRectangle; } } spreadsheetCell.DisplayRectangle = cellRectangle; // This flag is set after the cell is measured. The cell won't be updated again until this flag is set // again. spreadsheetCell.IsModified = false; // Move the cursor up to the next column and test the next cell. displayCursor.X += spreadsheetColumn.Rectangle.Width; } // This will reset the cursor for the next row. displayCursor.X = 0; displayCursor.Y += spreadsheetRow.rectangle.Height; } // If all the rows and all the cells have been measured and checked for changes, then check one last time to see if // there is anything in the aggregate rectangel that needs to be redrawn. if (!aggregateRectangle.IsEmpty) { region.Union(aggregateRectangle); } // This is the height of the displayed document. this.displayRectangle.Height = displayCursor.Y; // This contains all the areas that need to be redrawn. return(region); }