//######################################################################################### //★★★★★★★ http://www.cnpopsoft.com [华普软件] ★★★★★★★ //★★★★★★★ 华普软件 - VB & C#.NET 专业论文与源码荟萃! ★★★★★★★ //######################################################################################### /* * Copyright ?2005, Mathew Hall * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. */ using System; using System.Collections; using System.ComponentModel; using System.Drawing; using System.Drawing.Drawing2D; using System.Windows.Forms; using XPTable.Editors; using XPTable.Events; using XPTable.Models; using XPTable.Renderers; using XPTable.Sorting; using XPTable.Themes; using XPTable.Win32; namespace XPTable.Models { /// /// Summary description for Table. /// [DesignTimeVisible(true), ToolboxItem(true), ToolboxBitmap(typeof(Table))] public class Table : Control, ISupportInitialize { #region Event Handlers #region Cells /// /// Occurs when the value of a Cells property changes /// public event CellEventHandler CellPropertyChanged; #region Focus /// /// Occurs when a Cell gains focus /// public event CellFocusEventHandler CellGotFocus; /// /// Occurs when a Cell loses focus /// public event CellFocusEventHandler CellLostFocus; #endregion #region Keys /// /// Occurs when a key is pressed when a Cell has focus /// public event CellKeyEventHandler CellKeyDown; /// /// Occurs when a key is released when a Cell has focus /// public event CellKeyEventHandler CellKeyUp; #endregion #region Mouse /// /// Occurs when the mouse pointer enters a Cell /// public event CellMouseEventHandler CellMouseEnter; /// /// Occurs when the mouse pointer leaves a Cell /// public event CellMouseEventHandler CellMouseLeave; /// /// Occurs when a mouse pointer is over a Cell and a mouse button is pressed /// public event CellMouseEventHandler CellMouseDown; /// /// Occurs when a mouse pointer is over a Cell and a mouse button is released /// public event CellMouseEventHandler CellMouseUp; /// /// Occurs when a mouse pointer is moved over a Cell /// public event CellMouseEventHandler CellMouseMove; /// /// Occurs when the mouse pointer hovers over a Cell /// public event CellMouseEventHandler CellMouseHover; /// /// Occurs when a Cell is clicked /// public event CellMouseEventHandler CellClick; /// /// Occurs when a Cell is double-clicked /// public event CellMouseEventHandler CellDoubleClick; #endregion #region Buttons /// /// Occurs when a Cell's button is clicked /// public event CellButtonEventHandler CellButtonClicked; #endregion #region CheckBox /// /// Occurs when a Cell's Checked value changes /// public event CellCheckBoxEventHandler CellCheckChanged; #endregion #endregion #region Column /// /// Occurs when a Column's property changes /// public event ColumnEventHandler ColumnPropertyChanged; #endregion #region Column Headers /// /// Occurs when the mouse pointer enters a Column Header /// public event HeaderMouseEventHandler HeaderMouseEnter; /// /// Occurs when the mouse pointer leaves a Column Header /// public event HeaderMouseEventHandler HeaderMouseLeave; /// /// Occurs when a mouse pointer is over a Column Header and a mouse button is pressed /// public event HeaderMouseEventHandler HeaderMouseDown; /// /// Occurs when a mouse pointer is over a Column Header and a mouse button is released /// public event HeaderMouseEventHandler HeaderMouseUp; /// /// Occurs when a mouse pointer is moved over a Column Header /// public event HeaderMouseEventHandler HeaderMouseMove; /// /// Occurs when the mouse pointer hovers over a Column Header /// public event HeaderMouseEventHandler HeaderMouseHover; /// /// Occurs when a Column Header is clicked /// public event HeaderMouseEventHandler HeaderClick; /// /// Occurs when a Column Header is double-clicked /// public event HeaderMouseEventHandler HeaderDoubleClick; /// /// Occurs when the height of the Column Headers changes /// public event EventHandler HeaderHeightChanged; #endregion #region ColumnModel /// /// Occurs when the value of the Table's ColumnModel property changes /// public event EventHandler ColumnModelChanged; /// /// Occurs when a Column is added to the ColumnModel /// public event ColumnModelEventHandler ColumnAdded; /// /// Occurs when a Column is removed from the ColumnModel /// public event ColumnModelEventHandler ColumnRemoved; #endregion #region Editing /// /// Occurs when the Table begins editing a Cell /// public event CellEditEventHandler BeginEditing; /// /// Occurs when the Table stops editing a Cell /// public event CellEditEventHandler EditingStopped; /// /// Occurs when the editing of a Cell is cancelled /// public event CellEditEventHandler EditingCancelled; #endregion #region Rows /// /// Occurs when a Cell is added to a Row /// public event RowEventHandler CellAdded; /// /// Occurs when a Cell is removed from a Row /// public event RowEventHandler CellRemoved; /// /// Occurs when the value of a Rows property changes /// public event RowEventHandler RowPropertyChanged; #endregion #region Sorting /// /// Occurs when a Column is about to be sorted /// public event ColumnEventHandler BeginSort; /// /// Occurs after a Column has finished sorting /// public event ColumnEventHandler EndSort; #endregion #region Painting /// /// Occurs before a Cell is painted /// public event PaintCellEventHandler BeforePaintCell; /// /// Occurs after a Cell is painted /// public event PaintCellEventHandler AfterPaintCell; /// /// Occurs before a Column header is painted /// public event PaintHeaderEventHandler BeforePaintHeader; /// /// Occurs after a Column header is painted /// public event PaintHeaderEventHandler AfterPaintHeader; #endregion #region TableModel /// /// Occurs when the value of the Table's TableModel property changes /// public event EventHandler TableModelChanged; /// /// Occurs when a Row is added into the TableModel /// public event TableModelEventHandler RowAdded; /// /// Occurs when a Row is removed from the TableModel /// public event TableModelEventHandler RowRemoved; /// /// Occurs when the value of the TableModel Selection property changes /// public event SelectionEventHandler SelectionChanged; /// /// Occurs when the value of the RowHeight property changes /// public event EventHandler RowHeightChanged; #endregion #endregion #region Class Data /// /// Required designer variable. /// private System.ComponentModel.Container components = null; #region Border /// /// The style of the Table's border /// private BorderStyle borderStyle; #endregion #region Cells /// /// The last known cell position that the mouse was over /// private CellPos lastMouseCell; /// /// The last known cell position that the mouse's left /// button was pressed in /// private CellPos lastMouseDownCell; /// /// The position of the Cell that currently has focus /// private CellPos focusedCell; /// /// The Cell that is currently being edited /// private CellPos editingCell; /// /// The ICellEditor that is currently being used to edit a Cell /// private ICellEditor curentCellEditor; /// /// The action that must be performed on a Cell to start editing /// private EditStartAction editStartAction; /// /// The key that must be pressed for editing to start when /// editStartAction is set to EditStartAction.CustomKey /// private Keys customEditKey; /// /// The amount of time (in milliseconds) that that the /// mouse pointer must hover over a Cell or Column Header before /// a MouseHover event is raised /// private int hoverTime; /// /// A TRACKMOUSEEVENT used to set the hoverTime /// private TRACKMOUSEEVENT trackMouseEvent; #endregion #region Columns /// /// The ColumnModel of the Table /// private ColumnModel columnModel; /// /// Whether the Table supports column resizing /// private bool columnResizing; /// /// The index of the column currently being resized /// private int resizingColumnIndex; /// /// The x coordinate of the currently resizing column /// private int resizingColumnAnchor; /// /// The horizontal distance between the resize starting /// point and the right edge of the resizing column /// private int resizingColumnOffset; /// /// The width that the resizing column will be set to /// once column resizing is finished /// private int resizingColumnWidth; /// /// The index of the current pressed column /// private int pressedColumn; /// /// The index of the current "hot" column /// private int hotColumn; /// /// The index of the last sorted column /// private int lastSortedColumn; /// /// The Color of a sorted Column's background /// private Color sortedColumnBackColor; #endregion #region Grid /// /// Indicates whether grid lines appear between the rows and columns /// containing the rows and cells in the Table /// private GridLines gridLines; /// /// The color of the grid lines /// private Color gridColor; /// /// The line style of the grid lines /// private GridLineStyle gridLineStyle; #endregion #region Header /// /// The styles of the column headers /// private ColumnHeaderStyle headerStyle; /// /// The Renderer used to paint the column headers /// private HeaderRenderer headerRenderer; /// /// The font used to draw the text in the column header /// private Font headerFont; /// /// The ContextMenu for the column headers /// private HeaderContextMenu headerContextMenu; #endregion #region Items /// /// The TableModel of the Table /// private TableModel tableModel; #endregion #region Scrollbars /// /// Indicates whether the Table will allow the user to scroll to any /// columns or rows placed outside of its visible boundaries /// private bool scrollable; /// /// The Table's horizontal ScrollBar /// private HScrollBar hScrollBar; /// /// The Table's vertical ScrollBar /// private VScrollBar vScrollBar; #endregion #region Selection /// /// Specifies whether rows and cells can be selected /// private bool allowSelection; /// /// Specifies whether multiple rows and cells can be selected /// private bool multiSelect; /// /// Specifies whether clicking a row selects all its cells /// private bool fullRowSelect; /// /// Specifies whether the selected rows and cells in the Table remain /// highlighted when the Table loses focus /// private bool hideSelection; /// /// The background color of selected rows and cells /// private Color selectionBackColor; /// /// The foreground color of selected rows and cells /// private Color selectionForeColor; /// /// The background color of selected rows and cells when the Table /// doesn't have focus /// private Color unfocusedSelectionBackColor; /// /// The foreground color of selected rows and cells when the Table /// doesn't have focus /// private Color unfocusedSelectionForeColor; /// /// Determines how selected Cells are hilighted /// private SelectionStyle selectionStyle; #endregion #region Table /// /// The state of the table /// private TableState tableState; /// /// Is the Table currently initialising /// private bool init; /// /// The number of times BeginUpdate has been called /// private int beginUpdateCount; /// /// The ToolTip used by the Table to display cell and column tooltips /// private ToolTip toolTip; /// /// The alternating row background color /// private Color alternatingRowColor; /// /// The text displayed in the Table when it has no data to display /// private string noItemsText; /// /// Specifies whether the Table is being used as a preview Table /// in a ColumnColection editor /// private bool preview; /*/// /// Specifies whether pressing the Tab key while editing moves the /// editor to the next available cell /// private bool tabMovesEditor;*/ #endregion #endregion #region Constructor /// /// Initializes a new instance of the Table class with default settings /// public Table() { // starting setup this.init = true; // This call is required by the Windows.Forms Form Designer. components = new System.ComponentModel.Container(); // this.SetStyle(ControlStyles.UserPaint, true); this.SetStyle(ControlStyles.AllPaintingInWmPaint, true); this.SetStyle(ControlStyles.DoubleBuffer, true); this.SetStyle(ControlStyles.ResizeRedraw, true); this.SetStyle(ControlStyles.Selectable, true); this.TabStop = true; this.Size = new Size(150, 150); this.BackColor = Color.White; // this.columnModel = null; this.tableModel = null; // header this.headerStyle = ColumnHeaderStyle.Clickable; this.headerFont = this.Font; this.headerRenderer = new XPHeaderRenderer(); //this.headerRenderer = new GradientHeaderRenderer(); //this.headerRenderer = new FlatHeaderRenderer(); this.headerRenderer.Font = this.headerFont; this.headerContextMenu = new HeaderContextMenu(); this.columnResizing = true; this.resizingColumnIndex = -1; this.resizingColumnWidth = -1; this.hotColumn = -1; this.pressedColumn = -1; this.lastSortedColumn = -1; this.sortedColumnBackColor = Color.WhiteSmoke; // borders this.borderStyle = BorderStyle.Fixed3D; // scrolling this.scrollable = true; this.hScrollBar = new HScrollBar(); this.hScrollBar.Visible = false; this.hScrollBar.Location = new Point(this.BorderWidth, this.Height - this.BorderWidth - SystemInformation.HorizontalScrollBarHeight); this.hScrollBar.Width = this.Width - (this.BorderWidth * 2) - SystemInformation.VerticalScrollBarWidth; this.hScrollBar.Scroll += new ScrollEventHandler(this.OnHorizontalScroll); this.Controls.Add(this.hScrollBar); this.vScrollBar = new VScrollBar(); this.vScrollBar.Visible = false; this.vScrollBar.Location = new Point(this.Width - this.BorderWidth - SystemInformation.VerticalScrollBarWidth, this.BorderWidth); this.vScrollBar.Height = this.Height - (this.BorderWidth * 2) - SystemInformation.HorizontalScrollBarHeight; this.vScrollBar.Scroll += new ScrollEventHandler(this.OnVerticalScroll); this.Controls.Add(this.vScrollBar); // this.gridLines = GridLines.None;; this.gridColor = SystemColors.Control; this.gridLineStyle = GridLineStyle.Solid; this.allowSelection = true; this.multiSelect = false; this.fullRowSelect = false; this.hideSelection = false; this.selectionBackColor = SystemColors.Highlight; this.selectionForeColor = SystemColors.HighlightText; this.unfocusedSelectionBackColor = SystemColors.Control; this.unfocusedSelectionForeColor = SystemColors.ControlText; this.selectionStyle = SelectionStyle.ListView; this.alternatingRowColor = Color.Transparent; // current table state this.tableState = TableState.Normal; this.lastMouseCell = new CellPos(-1, -1); this.lastMouseDownCell = new CellPos(-1, -1); this.focusedCell = new CellPos(-1, -1); this.hoverTime = 1000; this.trackMouseEvent = null; this.ResetMouseEventArgs(); this.toolTip = new ToolTip(this.components); this.toolTip.Active = false; this.toolTip.InitialDelay = 1000; this.noItemsText = "There are no items in this view"; this.editingCell = new CellPos(-1, -1); this.curentCellEditor = null; this.editStartAction = EditStartAction.DoubleClick; this.customEditKey = Keys.F5; //this.tabMovesEditor = true; // finished setting up this.beginUpdateCount = 0; this.init = false; this.preview = false; } #endregion #region Methods #region Coordinate Translation #region ClientToDisplayRect /// /// Computes the location of the specified client point into coordinates /// relative to the display rectangle /// /// The client x coordinate to convert /// The client y coordinate to convert /// A Point that represents the converted coordinates (x, y), /// relative to the display rectangle public Point ClientToDisplayRect(int x, int y) { int xPos = x - this.BorderWidth; if (this.HScroll) { xPos += this.hScrollBar.Value; } int yPos = y - this.BorderWidth; if (this.VScroll) { yPos += this.TopIndex * this.RowHeight; } return new Point(xPos, yPos); } /// /// Computes the location of the specified client point into coordinates /// relative to the display rectangle /// /// The client coordinate Point to convert /// A Point that represents the converted Point, p, /// relative to the display rectangle public Point ClientToDisplayRect(Point p) { return this.ClientToDisplayRect(p.X, p.Y); } /// /// Converts the location of the specified Rectangle into coordinates /// relative to the display rectangle /// /// The Rectangle to convert whose location is in /// client coordinates /// A Rectangle that represents the converted Rectangle, rect, /// relative to the display rectangle public Rectangle ClientToDisplayRect(Rectangle rect) { return new Rectangle(this.ClientToDisplayRect(rect.Location), rect.Size); } #endregion #region DisplayRectToClient /// /// Computes the location of the specified point relative to the display /// rectangle point into client coordinates /// /// The x coordinate to convert relative to the display rectangle /// The y coordinate to convert relative to the display rectangle /// A Point that represents the converted coordinates (x, y) relative to /// the display rectangle in client coordinates public Point DisplayRectToClient(int x, int y) { int xPos = x + this.BorderWidth; if (this.HScroll) { xPos -= this.hScrollBar.Value; } int yPos = y + this.BorderWidth; if (this.VScroll) { yPos -= this.TopIndex * this.RowHeight; } return new Point(xPos, yPos); } /// /// Computes the location of the specified point relative to the display /// rectangle into client coordinates /// /// The point relative to the display rectangle to convert /// A Point that represents the converted Point relative to /// the display rectangle, p, in client coordinates public Point DisplayRectToClient(Point p) { return this.DisplayRectToClient(p.X, p.Y); } /// /// Converts the location of the specified Rectangle relative to the display /// rectangle into client coordinates /// /// The Rectangle to convert whose location is relative to /// the display rectangle /// A Rectangle that represents the converted Rectangle relative to /// the display rectangle, rect, in client coordinates public Rectangle DisplayRectToClient(Rectangle rect) { return new Rectangle(this.DisplayRectToClient(rect.Location), rect.Size); } #endregion #region Cells /// /// Returns the Cell at the specified client coordinates /// /// The client x coordinate of the Cell /// The client y coordinate of the Cell /// The Cell at the specified client coordinates, or /// null if it does not exist public Cell CellAt(int x, int y) { int row = this.RowIndexAt(x, y); int column = this.ColumnIndexAt(x, y); // return null if the row or column don't exist if (row == -1 || row >= this.TableModel.Rows.Count || column == -1 || column >= this.TableModel.Rows[row].Cells.Count) { return null; } return this.TableModel[row, column]; } /// /// Returns the Cell at the specified client Point /// /// The point of interest /// The Cell at the specified client Point, /// or null if not found public Cell CellAt(Point p) { return this.CellAt(p.X, p.Y); } /// /// Returns a Rectangle that specifies the size and location the cell at /// the specified row and column indexes in client coordinates /// /// The index of the row that contains the cell /// The index of the column that contains the cell /// A Rectangle that specifies the size and location the cell at /// the specified row and column indexes in client coordinates public Rectangle CellRect(int row, int column) { // return null if the row or column don't exist if (row == -1 || row >= this.TableModel.Rows.Count || column == -1 || column >= this.TableModel.Rows[row].Cells.Count) { return Rectangle.Empty; } Rectangle columnRect = this.ColumnRect(column); if (columnRect == Rectangle.Empty) { return columnRect; } Rectangle rowRect = this.RowRect(row); if (rowRect == Rectangle.Empty) { return rowRect; } return new Rectangle(columnRect.X, rowRect.Y, columnRect.Width, rowRect.Height); } /// /// Returns a Rectangle that specifies the size and location the cell at /// the specified cell position in client coordinates /// /// The position of the cell /// A Rectangle that specifies the size and location the cell at /// the specified cell position in client coordinates public Rectangle CellRect(CellPos cellPos) { return this.CellRect(cellPos.Row, cellPos.Column); } /// /// Returns a Rectangle that specifies the size and location of the /// specified cell in client coordinates /// /// The cell whose bounding rectangle is to be retrieved /// A Rectangle that specifies the size and location the specified /// cell in client coordinates public Rectangle CellRect(Cell cell) { if (cell == null || cell.Row == null || cell.InternalIndex == -1) { return Rectangle.Empty; } if (this.TableModel == null || this.ColumnModel == null) { return Rectangle.Empty; } int row = this.TableModel.Rows.IndexOf(cell.Row); int col = cell.InternalIndex; return this.CellRect(row, col); } /// /// Returns whether Cell at the specified row and column indexes /// is not null /// /// The row index of the cell /// The column index of the cell /// True if the cell at the specified row and column indexes /// is not null, otherwise false protected internal bool IsValidCell(int row, int column) { if (this.TableModel != null && this.ColumnModel != null) { if (row >= 0 && row < this.TableModel.Rows.Count) { if (column >= 0 && column < this.ColumnModel.Columns.Count) { return (this.TableModel.Rows[row].Cells[column] != null); } } } return false; } /// /// Returns whether Cell at the specified cell position is not null /// /// The position of the cell /// True if the cell at the specified cell position is not /// null, otherwise false protected internal bool IsValidCell(CellPos cellPos) { return this.IsValidCell(cellPos.Row, cellPos.Column); } /// /// Returns a CellPos that specifies the next Cell that is visible /// and enabled from the specified Cell /// /// A CellPos that specifies the Cell to start /// searching from /// Specifies whether to move to the start of the /// next Row when the end of the current Row is reached /// Specifies whether the search should travel /// in a forward direction (top to bottom, left to right) through the Cells /// Indicates whether the specified starting /// Cell is included in the search /// Specifies whether all Cells in /// the Row should be included in the search /// A CellPos that specifies the next Cell that is visible /// and enabled, or CellPos.Empty if there are no Cells that are visible /// and enabled protected CellPos FindNextVisibleEnabledCell(CellPos start, bool wrap, bool forward, bool includeStart, bool checkOtherCellsInRow) { if (this.ColumnCount == 0 || this.RowCount == 0) { return CellPos.Empty; } int startRow = start.Row != -1 ? start.Row : 0; int startCol = start.Column != -1 ? start.Column : 0; bool first = true; if (forward) { for (int i=startRow; i=0; i--) { int j = (first || !checkOtherCellsInRow ? startCol : this.TableModel.Rows[i].Cells.Count); for (; j>=0; j--) { if (i == startRow && j == startCol) { if (!first) { return CellPos.Empty; } first = false; if (!includeStart) { if (!checkOtherCellsInRow) { break; } continue; } } if (this.IsValidCell(i, j) && this.IsValidColumn(j) && this.TableModel[i, j].Enabled && this.ColumnModel.Columns[j].Enabled && this.ColumnModel.Columns[j].Visible) { return new CellPos(i, j); } if (!checkOtherCellsInRow) { continue; } } if (wrap) { if (i-1 == -1) { i = this.TableModel.Rows.Count; } } else { break; } } } return CellPos.Empty; } /// /// Returns a CellPos that specifies the next Cell that able to be /// edited from the specified Cell /// /// A CellPos that specifies the Cell to start /// searching from /// Specifies whether to move to the start of the /// next Row when the end of the current Row is reached /// Specifies whether the search should travel /// in a forward direction (top to bottom, left to right) through the Cells /// Indicates whether the specified starting /// Cell is included in the search /// A CellPos that specifies the next Cell that is able to /// be edited, or CellPos.Empty if there are no Cells that editable protected CellPos FindNextEditableCell(CellPos start, bool wrap, bool forward, bool includeStart) { if (this.ColumnCount == 0 || this.RowCount == 0) { return CellPos.Empty; } int startRow = start.Row != -1 ? start.Row : 0; int startCol = start.Column != -1 ? start.Column : 0; bool first = true; if (forward) { for (int i=startRow; i=0; i--) { int j = (first ? startCol : this.TableModel.Rows[i].Cells.Count); for (; j>=0; j--) { if (i == startRow && j == startCol) { if (!first) { return CellPos.Empty; } first = false; if (!includeStart) { continue; } } if (this.IsValidCell(i, j) && this.IsValidColumn(j) && this.TableModel[i, j].Editable && this.ColumnModel.Columns[j].Editable) { return new CellPos(i, j); } } if (wrap) { if (i-1 == -1) { i = this.TableModel.Rows.Count; } } else { break; } } } return CellPos.Empty; } #endregion #region Columns /// /// Returns the index of the Column at the specified client coordinates /// /// The client x coordinate of the Column /// The client y coordinate of the Column /// The index of the Column at the specified client coordinates, or /// -1 if it does not exist public int ColumnIndexAt(int x, int y) { if (this.ColumnModel == null) { return -1; } // convert to DisplayRect coordinates before // sending to the ColumnModel return this.ColumnModel.ColumnIndexAtX(this.hScrollBar.Value + x - this.BorderWidth); } /// /// Returns the index of the Column at the specified client point /// /// The point of interest /// The index of the Column at the specified client point, or /// -1 if it does not exist public int ColumnIndexAt(Point p) { return this.ColumnIndexAt(p.X, p.Y); } /// /// Returns the bounding rectangle of the specified /// column's header in client coordinates /// /// The index of the column /// The bounding rectangle of the specified /// column's header public Rectangle ColumnHeaderRect(int column) { if (this.ColumnModel == null) { return Rectangle.Empty; } Rectangle rect = this.ColumnModel.ColumnHeaderRect(column); if (rect == Rectangle.Empty) { return rect; } rect.X -= this.hScrollBar.Value - this.BorderWidth; rect.Y = this.BorderWidth; return rect; } /// /// Returns the bounding rectangle of the specified /// column's header in client coordinates /// /// The column /// The bounding rectangle of the specified /// column's header public Rectangle ColumnHeaderRect(Column column) { if (this.ColumnModel == null) { return Rectangle.Empty; } return this.ColumnHeaderRect(this.ColumnModel.Columns.IndexOf(column)); } /// /// Returns the bounding rectangle of the column at the /// specified index in client coordinates /// /// The column /// The bounding rectangle of the column at the /// specified index public Rectangle ColumnRect(int column) { if (this.ColumnModel == null) { return Rectangle.Empty; } Rectangle rect = this.ColumnHeaderRect(column); if (rect == Rectangle.Empty) { return rect; } rect.Y += this.HeaderHeight; rect.Height = this.TotalRowHeight; return rect; } /// /// Returns the bounding rectangle of the specified column /// in client coordinates /// /// The column /// The bounding rectangle of the specified /// column public Rectangle ColumnRect(Column column) { if (this.ColumnModel == null) { return Rectangle.Empty; } return this.ColumnRect(this.ColumnModel.Columns.IndexOf(column)); } #endregion #region Rows /// /// Returns the index of the Row at the specified client coordinates /// /// The client x coordinate of the Row /// The client y coordinate of the Row /// The index of the Row at the specified client coordinates, or /// -1 if it does not exist public int RowIndexAt(int x, int y) { if (this.TableModel == null) { return -1; } if (this.HeaderStyle != ColumnHeaderStyle.None) { y -= this.HeaderHeight; } y -= this.BorderWidth; if (y < 0) { return -1; } if (this.VScroll) { y += this.TopIndex * this.RowHeight; } return this.TableModel.RowIndexAt(y); } /// /// Returns the index of the Row at the specified client point /// /// The point of interest /// The index of the Row at the specified client point, or /// -1 if it does not exist public int RowIndexAt(Point p) { return this.RowIndexAt(p.X, p.Y); } /// /// Returns the bounding rectangle of the row at the /// specified index in client coordinates /// /// The index of the row /// The bounding rectangle of the row at the /// specified index public Rectangle RowRect(int row) { if (this.TableModel == null || this.ColumnModel == null || row == -1 || row > this.TableModel.Rows.Count) { return Rectangle.Empty; } Rectangle rect = new Rectangle(); rect.X = this.DisplayRectangle.X; rect.Y = this.BorderWidth + ((row - this.TopIndex) * this.RowHeight); rect.Width = this.ColumnModel.VisibleColumnsWidth; rect.Height = this.RowHeight; if (this.HeaderStyle != ColumnHeaderStyle.None) { rect.Y += this.HeaderHeight; } return rect; } /// /// Returns the bounding rectangle of the specified row /// in client coordinates /// /// The row /// The bounding rectangle of the specified /// row public Rectangle RowRect(Row row) { if (this.TableModel == null) { return Rectangle.Empty; } return this.RowRect(this.TableModel.Rows.IndexOf(row)); } #endregion #region Hit Tests /// /// Returns a TableRegions value that represents the table region at /// the specified client coordinates /// /// The client x coordinate /// The client y coordinate /// A TableRegions value that represents the table region at /// the specified client coordinates public TableRegion HitTest(int x, int y) { if (this.HeaderStyle != ColumnHeaderStyle.None && this.HeaderRectangle.Contains(x, y)) { return TableRegion.ColumnHeader; } else if (this.CellDataRect.Contains(x, y)) { return TableRegion.Cells; } else if (!this.Bounds.Contains(x, y)) { return TableRegion.NoWhere; } return TableRegion.NonClientArea; } /// /// Returns a TableRegions value that represents the table region at /// the specified client point /// /// The point of interest /// A TableRegions value that represents the table region at /// the specified client point public TableRegion HitTest(Point p) { return this.HitTest(p.X, p.Y); } #endregion #endregion #region Dispose /// /// Releases the unmanaged resources used by the Control and optionally /// releases the managed resources /// /// true to release both managed and unmanaged /// resources; false to release only unmanaged resources protected override void Dispose(bool disposing) { if (disposing) { if (components != null) { components.Dispose(); } } base.Dispose(disposing); } /// /// Removes the ColumnModel and TableModel from the Table /// public void Clear() { if (this.ColumnModel != null) { this.ColumnModel = null; } if (this.TableModel != null) { this.TableModel = null; } } #endregion #region Editing /// /// Records the Cell that is currently being edited and the /// ICellEditor used to edit the Cell /// /// The Cell that is currently being edited /// The ICellEditor used to edit the Cell private void SetEditingCell(Cell cell, ICellEditor editor) { this.SetEditingCell(new CellPos(cell.Row.InternalIndex, cell.InternalIndex), editor); } /// /// Records the Cell that is currently being edited and the /// ICellEditor used to edit the Cell /// /// The Cell that is currently being edited /// The ICellEditor used to edit the Cell private void SetEditingCell(CellPos cellPos, ICellEditor editor) { this.editingCell = cellPos; this.curentCellEditor = editor; } /// /// Starts editing the Cell at the specified row and column indexes /// /// The row index of the Cell to be edited /// The column index of the Cell to be edited public void EditCell(int row, int column) { this.EditCell(new CellPos(row, column)); } /// /// Starts editing the Cell at the specified CellPos /// /// A CellPos that specifies the Cell to be edited public void EditCell(CellPos cellPos) { // don't bother if the cell doesn't exists or the cell's // column is not visible or the cell is not editable if (!this.IsValidCell(cellPos) || !this.ColumnModel.Columns[cellPos.Column].Visible || !this.IsCellEditable(cellPos)) { return; } // check if we're currently editing a cell if (this.EditingCell != CellPos.Empty) { // don't bother if we're already editing the cell. // if we're editing a different cell stop editing if (this.EditingCell == cellPos) { return; } else { this.EditingCellEditor.StopEditing(); } } Cell cell = this.TableModel[cellPos]; ICellEditor editor = this.ColumnModel.GetCellEditor(cellPos.Column); // make sure we have an editor and that the cell // and the cell's column are editable if (editor == null || !cell.Editable || !this.ColumnModel.Columns[cellPos.Column].Editable) { return; } if (this.EnsureVisible(cellPos)) { this.Refresh(); } Rectangle cellRect = this.CellRect(cellPos); // give anyone subscribed to the table's BeginEditing // event the first chance to cancel editing CellEditEventArgs e = new CellEditEventArgs(cell, editor, this, cellPos.Row, cellPos.Column, cellRect); this.OnBeginEditing(e); // if (!e.Cancel) { // get the editor ready for editing. if PrepareForEditing // returns false, someone who subscribed to the editors // BeginEdit event has cancelled editing if (!editor.PrepareForEditing(cell, this, cellPos, cellRect, e.Handled)) { return; } // keep track of the editing cell and editor // and start editing this.editingCell = cellPos; this.curentCellEditor = editor; editor.StartEditing(); } } /*/// /// Stops editing the current Cell and starts editing the next editable Cell /// /// Specifies whether the editor should traverse /// forward when looking for the next editable Cell protected internal void EditNextCell(bool forwards) { if (this.EditingCell == CellPos.Empty) { return; } CellPos nextCell = this.FindNextEditableCell(this.FocusedCell, true, forwards, false); if (nextCell != CellPos.Empty && nextCell != this.EditingCell) { this.StopEditing(); this.EditCell(nextCell); } }*/ /// /// Stops editing the current Cell and commits any changes /// public void StopEditing() { // don't bother if we're not editing if (this.EditingCell == CellPos.Empty) { return; } this.EditingCellEditor.StopEditing(); this.Invalidate(this.RowRect(this.editingCell.Row)); this.editingCell = CellPos.Empty; this.curentCellEditor = null; } /// /// Cancels editing the current Cell and ignores any changes /// public void CancelEditing() { // don't bother if we're not editing if (this.EditingCell == CellPos.Empty) { return; } this.EditingCellEditor.CancelEditing(); this.editingCell = CellPos.Empty; this.curentCellEditor = null; } /// /// Returns whether the Cell at the specified row and column is able /// to be edited by the user /// /// The row index of the Cell to check /// The column index of the Cell to check /// True if the Cell at the specified row and column is able /// to be edited by the user, false otherwise public bool IsCellEditable(int row, int column) { return this.IsCellEditable(new CellPos(row, column)); } /// /// Returns whether the Cell at the specified CellPos is able /// to be edited by the user /// /// A CellPos that specifies the Cell to check /// True if the Cell at the specified CellPos is able /// to be edited by the user, false otherwise public bool IsCellEditable(CellPos cellpos) { // don't bother if the cell doesn't exists or the cell's // column is not visible if (!this.IsValidCell(cellpos) || !this.ColumnModel.Columns[cellpos.Column].Visible) { return false; } return (this.TableModel[cellpos].Editable && this.ColumnModel.Columns[cellpos.Column].Editable); } /// /// Returns whether the Cell at the specified row and column is able /// to respond to user interaction /// /// The row index of the Cell to check /// The column index of the Cell to check /// True if the Cell at the specified row and column is able /// to respond to user interaction, false otherwise public bool IsCellEnabled(int row, int column) { return this.IsCellEnabled(new CellPos(row, column)); } /// /// Returns whether the Cell at the specified CellPos is able /// to respond to user interaction /// /// A CellPos that specifies the Cell to check /// True if the Cell at the specified CellPos is able /// to respond to user interaction, false otherwise public bool IsCellEnabled(CellPos cellpos) { // don't bother if the cell doesn't exists or the cell's // column is not visible if (!this.IsValidCell(cellpos) || !this.ColumnModel.Columns[cellpos.Column].Visible) { return false; } return (this.TableModel[cellpos].Enabled && this.ColumnModel.Columns[cellpos.Column].Enabled); } #endregion #region Invalidate /// /// Invalidates the specified Cell /// /// The Cell to be invalidated public void InvalidateCell(Cell cell) { this.InvalidateCell(cell.Row.Index, cell.Index); } /// /// Invalidates the Cell located at the specified row and column indicies /// /// The row index of the Cell to be invalidated /// The column index of the Cell to be invalidated public void InvalidateCell(int row, int column) { Rectangle cellRect = this.CellRect(row, column); if (cellRect == Rectangle.Empty) { return; } if (cellRect.IntersectsWith(this.CellDataRect)) { this.Invalidate(Rectangle.Intersect(this.CellDataRect, cellRect), false); } } /// /// Invalidates the Cell located at the specified CellPos /// /// A CellPos that specifies the Cell to be invalidated public void InvalidateCell(CellPos cellPos) { this.InvalidateCell(cellPos.Row, cellPos.Column); } /// /// Invalidates the specified Row /// /// The Row to be invalidated public void InvalidateRow(Row row) { this.InvalidateRow(row.Index); } /// /// Invalidates the Row located at the specified row index /// /// The row index of the Row to be invalidated public void InvalidateRow(int row) { Rectangle rowRect = this.RowRect(row); if (rowRect == Rectangle.Empty) { return; } if (rowRect.IntersectsWith(this.CellDataRect)) { this.Invalidate(Rectangle.Intersect(this.CellDataRect, rowRect), false); } } /// /// Invalidates the Row located at the specified CellPos /// /// A CellPos that specifies the Row to be invalidated public void InvalidateRow(CellPos cellPos) { this.InvalidateRow(cellPos.Row); } #endregion #region Keys /// /// Determines whether the specified key is reserved for use by the Table /// /// One of the Keys values /// true if the specified key is reserved for use by the Table; /// otherwise, false protected internal bool IsReservedKey(Keys key) { if ((key & Keys.Alt) != Keys.Alt) { Keys k = key & Keys.KeyCode; return (k == Keys.Up || k == Keys.Down || k == Keys.Left || k == Keys.Right || k == Keys.PageUp || k == Keys.PageDown || k == Keys.Home || k == Keys.End || k == Keys.Tab); } return false; } /// /// Determines whether the specified key is a regular input key or a special /// key that requires preprocessing /// /// One of the Keys values /// true if the specified key is a regular input key; otherwise, false protected override bool IsInputKey(Keys keyData) { if ((keyData & Keys.Alt) != Keys.Alt) { Keys key = keyData & Keys.KeyCode; switch (key) { case Keys.Up: case Keys.Down: case Keys.Left: case Keys.Right: case Keys.Prior: case Keys.Next: case Keys.End: case Keys.Home: { return true; } } if (base.IsInputKey(keyData)) { return true; } } return false; } #endregion #region Layout /// /// Prevents the Table from drawing until the EndUpdate method is called /// public void BeginUpdate() { if (this.IsHandleCreated) { if (this.beginUpdateCount == 0) { NativeMethods.SendMessage(this.Handle, 11, 0, 0); } this.beginUpdateCount++; } } /// /// Resumes drawing of the Table after drawing is suspended by the /// BeginUpdate method /// public void EndUpdate() { if (this.beginUpdateCount <= 0) { return; } this.beginUpdateCount--; if (this.beginUpdateCount == 0) { NativeMethods.SendMessage(this.Handle, 11, -1, 0); this.PerformLayout(); this.Invalidate(true); } } /// /// Signals the object that initialization is starting /// public void BeginInit() { this.init = true; } /// /// Signals the object that initialization is complete /// public void EndInit() { this.init = false; this.PerformLayout(); } /// /// Gets whether the Table is currently initializing /// [Browsable(false)] public bool Initializing { get { return this.init; } } #endregion #region Mouse /// /// This member supports the .NET Framework infrastructure and is not /// intended to be used directly from your code /// public new void ResetMouseEventArgs() { if (this.trackMouseEvent == null) { this.trackMouseEvent = new TRACKMOUSEEVENT(); this.trackMouseEvent.dwFlags = 3; this.trackMouseEvent.hwndTrack = base.Handle; } this.trackMouseEvent.dwHoverTime = this.HoverTime; NativeMethods.TrackMouseEvent(this.trackMouseEvent); } #endregion #region Scrolling /// /// Updates the scrollbars to reflect any changes made to the Table /// public void UpdateScrollBars() { if (!this.Scrollable || this.ColumnModel == null) { return; } // fix: Add width/height check as otherwise minimize // causes a crash // Portia4ever (kangxj@126.com) // 13/09/2005 // v1.0.1 if (this.Width == 0 || this.Height == 0) { return; } bool hscroll = (this.ColumnModel.VisibleColumnsWidth > this.Width - (this.BorderWidth * 2)); bool vscroll = this.TotalRowAndHeaderHeight > (this.Height - (this.BorderWidth * 2) - (hscroll ? SystemInformation.HorizontalScrollBarHeight : 0)); if (vscroll) { hscroll = (this.ColumnModel.VisibleColumnsWidth > this.Width - (this.BorderWidth * 2) - SystemInformation.VerticalScrollBarWidth); } if (hscroll) { Rectangle hscrollBounds = new Rectangle(this.BorderWidth, this.Height - this.BorderWidth - SystemInformation.HorizontalScrollBarHeight, this.Width - (this.BorderWidth * 2), SystemInformation.HorizontalScrollBarHeight); if (vscroll) { hscrollBounds.Width -= SystemInformation.VerticalScrollBarWidth; } this.hScrollBar.Visible = true; this.hScrollBar.Bounds = hscrollBounds; this.hScrollBar.Minimum = 0; this.hScrollBar.Maximum = this.ColumnModel.VisibleColumnsWidth; this.hScrollBar.SmallChange = Column.MinimumWidth; this.hScrollBar.LargeChange = hscrollBounds.Width - 1; if (this.hScrollBar.Value > this.hScrollBar.Maximum - this.hScrollBar.LargeChange) { this.hScrollBar.Value = this.hScrollBar.Maximum - this.hScrollBar.LargeChange; } } else { this.hScrollBar.Visible = false; this.hScrollBar.Value = 0; } if (vscroll) { Rectangle vscrollBounds = new Rectangle(this.Width - this.BorderWidth - SystemInformation.VerticalScrollBarWidth, this.BorderWidth, SystemInformation.VerticalScrollBarWidth, this.Height - (this.BorderWidth * 2)); if (hscroll) { vscrollBounds.Height -= SystemInformation.HorizontalScrollBarHeight; } this.vScrollBar.Visible = true; this.vScrollBar.Bounds = vscrollBounds; this.vScrollBar.Minimum = 0; this.vScrollBar.Maximum = (this.RowCount > this.VisibleRowCount ? this.RowCount - 1 : this.VisibleRowCount); this.vScrollBar.SmallChange = 1; this.vScrollBar.LargeChange = this.VisibleRowCount - 1; if (this.vScrollBar.Value > this.vScrollBar.Maximum - this.vScrollBar.LargeChange) { this.vScrollBar.Value = this.vScrollBar.Maximum - this.vScrollBar.LargeChange; } } else { this.vScrollBar.Visible = false; this.vScrollBar.Value = 0; } } /// /// Scrolls the contents of the Table horizontally to the specified value /// /// The value to scroll to protected void HorizontalScroll(int value) { int scrollVal = this.hScrollBar.Value - value; if (scrollVal != 0) { RECT scrollRect = RECT.FromRectangle(this.PseudoClientRect); Rectangle invalidateRect = scrollRect.ToRectangle(); NativeMethods.ScrollWindow(this.Handle, scrollVal, 0, ref scrollRect, ref scrollRect); if (scrollVal < 0) { invalidateRect.X = invalidateRect.Right + scrollVal; } invalidateRect.Width = Math.Abs(scrollVal); this.Invalidate(invalidateRect, false); if (this.VScroll) { this.Invalidate(new Rectangle(this.Width - this.BorderWidth - SystemInformation.VerticalScrollBarWidth, this.Height - this.BorderWidth - SystemInformation.HorizontalScrollBarHeight, SystemInformation.VerticalScrollBarWidth, SystemInformation.HorizontalScrollBarHeight), false); } } } /// /// Scrolls the contents of the Table vertically to the specified value /// /// The value to scroll to protected void VerticalScroll(int value) { int scrollVal = this.vScrollBar.Value - value; if (scrollVal != 0) { RECT scrollRect = RECT.FromRectangle(this.CellDataRect); Rectangle invalidateRect = scrollRect.ToRectangle(); scrollVal *= this.RowHeight; NativeMethods.ScrollWindow(this.Handle, 0, scrollVal, ref scrollRect, ref scrollRect); if (scrollVal < 0) { invalidateRect.Y = invalidateRect.Bottom + scrollVal; } invalidateRect.Height = Math.Abs(scrollVal); this.Invalidate(invalidateRect, false); if (this.HScroll) { this.Invalidate(new Rectangle(this.Width - this.BorderWidth - SystemInformation.VerticalScrollBarWidth, this.Height - this.BorderWidth - SystemInformation.HorizontalScrollBarHeight, SystemInformation.VerticalScrollBarWidth, SystemInformation.HorizontalScrollBarHeight), false); } } } /// /// Ensures that the Cell at the specified row and column is visible /// within the Table, scrolling the contents of the Table if necessary /// /// The zero-based index of the row to scroll into view /// The zero-based index of the column to scroll into view /// true if the Table scrolled to the Cell at the specified row /// and column, false otherwise public bool EnsureVisible(int row, int column) { if (!this.Scrollable || (!this.HScroll && !this.VScroll) || row == -1) { return false; } if (column == -1) { if (this.FocusedCell.Column != -1) { column = this.FocusedCell.Column; } else { column = 0; } } int hscrollVal = this.hScrollBar.Value; int vscrollVal = this.vScrollBar.Value; bool moved = false; if (this.HScroll) { if (column < 0) { column = 0; } else if (column >= this.ColumnCount) { column = this.ColumnCount - 1; } if (this.ColumnModel.Columns[column].Visible) { if (this.ColumnModel.Columns[column].Left < this.hScrollBar.Value) { hscrollVal = this.ColumnModel.Columns[column].Left; } else if (this.ColumnModel.Columns[column].Right > this.hScrollBar.Value + this.CellDataRect.Width) { hscrollVal = this.ColumnModel.Columns[column].Right - this.CellDataRect.Width; } if (hscrollVal > this.hScrollBar.Maximum - this.hScrollBar.LargeChange) { hscrollVal = this.hScrollBar.Maximum - this.hScrollBar.LargeChange; } } } if (this.VScroll) { if (row < 0) { vscrollVal = 0; } else if (row >= this.RowCount) { vscrollVal = this.RowCount - 1; } else { if (row < vscrollVal) { vscrollVal = row; } else if (row > vscrollVal + this.vScrollBar.LargeChange) { vscrollVal += row - (vscrollVal + this.vScrollBar.LargeChange); } } if (vscrollVal > this.vScrollBar.Maximum - this.vScrollBar.LargeChange) { vscrollVal = (this.vScrollBar.Maximum - this.vScrollBar.LargeChange) + 1; } } if (this.RowRect(row).Bottom > this.CellDataRect.Bottom) { vscrollVal++; } moved = (this.hScrollBar.Value != hscrollVal || this.vScrollBar.Value != vscrollVal); if (moved) { this.hScrollBar.Value = hscrollVal; this.vScrollBar.Value = vscrollVal; this.Invalidate(this.PseudoClientRect); } return moved; } /// /// Ensures that the Cell at the specified CellPos is visible within /// the Table, scrolling the contents of the Table if necessary /// /// A CellPos that contains the zero-based index /// of the row and column to scroll into view /// public bool EnsureVisible(CellPos cellPos) { return this.EnsureVisible(cellPos.Row, cellPos.Column); } /// /// Gets the index of the first visible Column currently displayed in the Table /// [Browsable(false)] public int FirstVisibleColumn { get { if (this.ColumnModel == null || this.ColumnModel.VisibleColumnCount == 0) { return -1; } return this.ColumnModel.ColumnIndexAtX(this.hScrollBar.Value); } } /// /// Gets the index of the last visible Column currently displayed in the Table /// [Browsable(false)] public int LastVisibleColumn { get { if (this.ColumnModel == null || this.ColumnModel.VisibleColumnCount == 0) { return -1; } int rightEdge = this.hScrollBar.Value + this.PseudoClientRect.Right; if (this.VScroll) { rightEdge -= this.vScrollBar.Width; } int col = this.ColumnModel.ColumnIndexAtX(rightEdge); if (col == -1) { return this.ColumnModel.PreviousVisibleColumn(this.ColumnModel.Columns.Count); } else if (!this.ColumnModel.Columns[col].Visible) { return this.ColumnModel.PreviousVisibleColumn(col); } return col; } } #endregion #region Sorting /// /// Sorts the last sorted column opposite to its current sort order, /// or sorts the currently focused column in ascending order if no /// columns have been sorted /// public void Sort() { this.Sort(true); } /// /// Sorts the last sorted column opposite to its current sort order, /// or sorts the currently focused column in ascending order if no /// columns have been sorted /// /// Specifies whether a stable sorting method /// should be used to sort the column public void Sort(bool stable) { // don't allow sorting if we're being used as a // preview table in a ColumnModel editor if (this.Preview) { return; } // if we don't have a sorted column already, check if // we can use the column of the cell that has focus if (!this.IsValidColumn(this.lastSortedColumn)) { if (this.IsValidColumn(this.focusedCell.Column)) { this.lastSortedColumn = this.focusedCell.Column; } } // make sure the last sorted column exists if (this.IsValidColumn(this.lastSortedColumn)) { // don't bother if the column won't let us sort if (!this.ColumnModel.Columns[this.lastSortedColumn].Sortable) { return; } // work out which direction we should sort SortOrder newOrder = SortOrder.Ascending; Column column = this.ColumnModel.Columns[this.lastSortedColumn]; if (column.SortOrder == SortOrder.Ascending) { newOrder = SortOrder.Descending; } this.Sort(this.lastSortedColumn, column, newOrder, stable); } } /// /// Sorts the specified column opposite to its current sort order, /// or in ascending order if the column is not sorted /// /// The index of the column to sort public void Sort(int column) { this.Sort(column, true); } /// /// Sorts the specified column opposite to its current sort order, /// or in ascending order if the column is not sorted /// /// The index of the column to sort /// Specifies whether a stable sorting method /// should be used to sort the column public void Sort(int column, bool stable) { // don't allow sorting if we're being used as a // preview table in a ColumnModel editor if (this.Preview) { return; } // make sure the column exists if (this.IsValidColumn(column)) { // don't bother if the column won't let us sort if (!this.ColumnModel.Columns[column].Sortable) { return; } // if we already have a different sorted column, set // its sort order to none if (column != this.lastSortedColumn) { if (this.IsValidColumn(this.lastSortedColumn)) { this.ColumnModel.Columns[this.lastSortedColumn].InternalSortOrder = SortOrder.None; } } this.lastSortedColumn = column; // work out which direction we should sort SortOrder newOrder = SortOrder.Ascending; Column col = this.ColumnModel.Columns[column]; if (col.SortOrder == SortOrder.Ascending) { newOrder = SortOrder.Descending; } this.Sort(column, col, newOrder, stable); } } /// /// Sorts the specified column in the specified sort direction /// /// The index of the column to sort /// The direction the column is to be sorted public void Sort(int column, SortOrder sortOrder) { this.Sort(column, sortOrder, true); } /// /// Sorts the specified column in the specified sort direction /// /// The index of the column to sort /// The direction the column is to be sorted /// Specifies whether a stable sorting method /// should be used to sort the column public void Sort(int column, SortOrder sortOrder, bool stable) { // don't allow sorting if we're being used as a // preview table in a ColumnModel editor if (this.Preview) { return; } // make sure the column exists if (this.IsValidColumn(column)) { // don't bother if the column won't let us sort if (!this.ColumnModel.Columns[column].Sortable) { return; } // if we already have a different sorted column, set // its sort order to none if (column != this.lastSortedColumn) { if (this.IsValidColumn(this.lastSortedColumn)) { this.ColumnModel.Columns[this.lastSortedColumn].InternalSortOrder = SortOrder.None; } } this.lastSortedColumn = column; this.Sort(column, this.ColumnModel.Columns[column], sortOrder, stable); } } /// /// Sorts the specified column in the specified sort direction /// /// The index of the column to sort /// The column to sort /// The direction the column is to be sorted /// Specifies whether a stable sorting method /// should be used to sort the column private void Sort(int index, Column column, SortOrder sortOrder, bool stable) { // make sure a null comparer type doesn't sneak past ComparerBase comparer = null; if (column.Comparer != null) { comparer = (ComparerBase) Activator.CreateInstance(column.Comparer, new object[] {this.TableModel, index, sortOrder}); } else if (column.DefaultComparerType != null) { comparer = (ComparerBase) Activator.CreateInstance(column.DefaultComparerType, new object[] {this.TableModel, index, sortOrder}); } else { return; } column.InternalSortOrder = sortOrder; // create the comparer SorterBase sorter = null; // work out which sort method to use. // - InsertionSort/MergeSort are stable sorts, // whereas ShellSort/HeapSort are unstable // - InsertionSort/ShellSort are faster than // MergeSort/HeapSort on small lists and slower // on large lists // so we choose based on the size of the list and // whether the user wants a stable sort if (this.TableModel.Rows.Count < 1000) { if (stable) { sorter = new InsertionSorter(this.TableModel, index, comparer, sortOrder); } else { sorter = new ShellSorter(this.TableModel, index, comparer, sortOrder); } } else { if (stable) { sorter = new MergeSorter(this.TableModel, index, comparer, sortOrder); } else { sorter = new HeapSorter(this.TableModel, index, comparer, sortOrder); } } // don't let the table redraw this.BeginUpdate(); this.OnBeginSort(new ColumnEventArgs(column, index, ColumnEventType.Sorting, null)); sorter.Sort(); this.OnEndSort(new ColumnEventArgs(column, index, ColumnEventType.Sorting, null)); // redraw any changes this.EndUpdate(); } /// /// Returns whether a Column exists at the specified index in the /// Table's ColumnModel /// /// The index of the column to check /// True if a Column exists at the specified index in the /// Table's ColumnModel, false otherwise public bool IsValidColumn(int column) { if (this.ColumnModel == null) { return false; } return (column >= 0 && column < this.ColumnModel.Columns.Count); } #endregion #endregion #region Properties #region Borders /// /// Gets or sets the border style for the Table /// [Category("Appearance"), DefaultValue(BorderStyle.Fixed3D), Description("Indicates the border style for the Table")] public BorderStyle BorderStyle { get { return this.borderStyle; } set { if (!Enum.IsDefined(typeof(BorderStyle), value)) { throw new InvalidEnumArgumentException("value", (int) value, typeof(BorderStyle)); } if (borderStyle != value) { this.borderStyle = value; this.Invalidate(true); } } } /// /// Gets the width of the Tables border /// protected int BorderWidth { get { if (this.BorderStyle == BorderStyle.Fixed3D) { return SystemInformation.Border3DSize.Width; } else if (this.BorderStyle == BorderStyle.FixedSingle) { return 1; } return 0; } } #endregion #region Cells /// /// Gets the last known cell position that the mouse was over /// [Browsable(false)] public CellPos LastMouseCell { get { return this.lastMouseCell; } } /// /// Gets the last known cell position that the mouse's left /// button was pressed in /// [Browsable(false)] public CellPos LastMouseDownCell { get { return this.lastMouseDownCell; } } /// /// Gets or sets the position of the Cell that currently has focus /// [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public CellPos FocusedCell { get { return this.focusedCell; } set { if (!this.IsValidCell(value)) { return; } if (!this.TableModel[value].Enabled) { return; } if (this.focusedCell != value) { if (!this.focusedCell.IsEmpty) { this.RaiseCellLostFocus(this.focusedCell); } this.focusedCell = value; if (!value.IsEmpty) { this.EnsureVisible(value); this.RaiseCellGotFocus(value); } } } } /// /// Gets or sets the amount of time (in milliseconds) that that the /// mouse pointer must hover over a Cell or Column Header before /// a MouseHover event is raised /// [Category("Behavior"), DefaultValue(1000), Description("The amount of time (in milliseconds) that that the mouse pointer must hover over a Cell or Column Header before a MouseHover event is raised")] public int HoverTime { get { return this.hoverTime; } set { if (value < 100) { throw new ArgumentException("HoverTime cannot be less than 100", "value"); } if (this.hoverTime != value) { this.hoverTime = value; this.ResetMouseEventArgs(); } } } #endregion #region ClientRectangle /// /// Gets the rectangle that represents the "client area" of the control. /// (The rectangle excludes the borders and scrollbars) /// [Browsable(false)] public Rectangle PseudoClientRect { get { Rectangle clientRect = this.InternalBorderRect; if (this.HScroll) { clientRect.Height -= SystemInformation.HorizontalScrollBarHeight; } if (this.VScroll) { clientRect.Width -= SystemInformation.VerticalScrollBarWidth; } return clientRect; } } /// /// Gets the rectangle that represents the "cell data area" of the control. /// (The rectangle excludes the borders, column headers and scrollbars) /// [Browsable(false)] public Rectangle CellDataRect { get { Rectangle clientRect = this.PseudoClientRect; if (this.HeaderStyle != ColumnHeaderStyle.None && this.ColumnCount > 0) { clientRect.Y += this.HeaderHeight; clientRect.Height -= this.HeaderHeight; } return clientRect; } } /// /// /// private Rectangle InternalBorderRect { get { return new Rectangle(this.BorderWidth, this.BorderWidth, this.Width - (this.BorderWidth * 2), this.Height - (this.BorderWidth * 2)); } } #endregion #region ColumnModel /// /// Gets or sets the ColumnModel that contains all the Columns /// displayed in the Table /// [Category("Columns"), DefaultValue(null), Description("Specifies the ColumnModel that contains all the Columns displayed in the Table")] public ColumnModel ColumnModel { get { return this.columnModel; } set { if (this.columnModel != value) { if (this.columnModel != null && this.columnModel.Table == this) { this.columnModel.InternalTable = null; } this.columnModel = value; if (value != null) { value.InternalTable = this; } this.OnColumnModelChanged(EventArgs.Empty); } } } /// /// Gets or sets whether the Table allows users to resize Column widths /// [Category("Columns"), DefaultValue(true), Description("Specifies whether the Table allows users to resize Column widths")] public bool ColumnResizing { get { return this.columnResizing; } set { if (this.columnResizing != value) { this.columnResizing = value; } } } /// /// Returns the number of Columns in the Table /// [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int ColumnCount { get { if (this.ColumnModel == null) { return -1; } return this.ColumnModel.Columns.Count; } } /// /// Returns the index of the currently sorted Column /// [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int SortingColumn { get { return this.lastSortedColumn; } } /// /// Gets or sets the background Color for the currently sorted column /// [Category("Columns"), Description("The background Color for a sorted Column")] public Color SortedColumnBackColor { get { return this.sortedColumnBackColor; } set { if (this.sortedColumnBackColor != value) { this.sortedColumnBackColor = value; if (this.IsValidColumn(this.lastSortedColumn)) { Rectangle columnRect = this.ColumnRect(this.lastSortedColumn); if (this.PseudoClientRect.IntersectsWith(columnRect)) { this.Invalidate(Rectangle.Intersect(this.PseudoClientRect, columnRect)); } } } } } /// /// Specifies whether the Table's SortedColumnBackColor property /// should be serialized at design time /// /// True if the SortedColumnBackColor property should be /// serialized, False otherwise private bool ShouldSerializeSortedColumnBackColor() { return this.sortedColumnBackColor != Color.WhiteSmoke; } #endregion #region DisplayRectangle /// /// Gets the rectangle that represents the display area of the Table /// [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public override Rectangle DisplayRectangle { get { Rectangle displayRect = this.CellDataRect; if (!this.init) { displayRect.X -= this.hScrollBar.Value; displayRect.Y -= this.vScrollBar.Value; } if (this.ColumnModel == null) { return displayRect; } if (this.ColumnModel.TotalColumnWidth <= this.CellDataRect.Width) { displayRect.Width = this.CellDataRect.Width; } else { displayRect.Width = this.ColumnModel.VisibleColumnsWidth; } if (this.TotalRowHeight <= this.CellDataRect.Height) { displayRect.Height = this.CellDataRect.Height; } else { displayRect.Height = this.TotalRowHeight; } return displayRect; } } #endregion #region Editing /// /// Gets whether the Table is currently editing a Cell /// [Browsable(false)] public bool IsEditing { get { return !this.EditingCell.IsEmpty; } } /// /// Gets a CellPos that specifies the position of the Cell that /// is currently being edited /// [Browsable(false)] public CellPos EditingCell { get { return this.editingCell; } } /// /// Gets the ICellEditor that is currently being used to edit a Cell /// [Browsable(false)] public ICellEditor EditingCellEditor { get { return this.curentCellEditor; } } /// /// Gets or sets the action that causes editing to be initiated /// [Category("Editing"), DefaultValue(EditStartAction.DoubleClick), Description("The action that causes editing to be initiated")] public EditStartAction EditStartAction { get { return this.editStartAction; } set { if (!Enum.IsDefined(typeof(EditStartAction), value)) { throw new InvalidEnumArgumentException("value", (int) value, typeof(EditStartAction)); } if (this.editStartAction != value) { this.editStartAction = value; } } } /// /// Gets or sets the custom key used to initiate Cell editing /// [Category("Editing"), DefaultValue(Keys.F5), Description("The custom key used to initiate Cell editing")] public Keys CustomEditKey { get { return this.customEditKey; } set { if (this.IsReservedKey(value)) { throw new ArgumentException("CustomEditKey cannot be one of the Table's reserved keys " + "(Up arrow, Down arrow, Left arrow, Right arrow, PageUp, " + "PageDown, Home, End, Tab)", "value"); } if (this.customEditKey != value) { this.customEditKey = value; } } } /*/// /// Gets or sets whether pressing the Tab key during editing moves /// the editor to the next editable Cell /// [Category("Editing"), DefaultValue(true), Description("")] public bool TabMovesEditor { get { return this.tabMovesEditor; } set { this.tabMovesEditor = value; } }*/ #endregion #region Grid /// /// Gets or sets how grid lines are displayed around rows and columns /// [Category("Grid"), DefaultValue(GridLines.None), Description("Determines how grid lines are displayed around rows and columns")] public GridLines GridLines { get { return this.gridLines; } set { if (!Enum.IsDefined(typeof(GridLines), value)) { throw new InvalidEnumArgumentException("value", (int) value, typeof(GridLines)); } if (this.gridLines != value) { this.gridLines = value; this.Invalidate(this.PseudoClientRect, false); } } } /// /// Gets or sets the style of the lines used to draw the grid /// [Category("Grid"), DefaultValue(GridLineStyle.Solid), Description("The style of the lines used to draw the grid")] public GridLineStyle GridLineStyle { get { return this.gridLineStyle; } set { if (!Enum.IsDefined(typeof(GridLineStyle), value)) { throw new InvalidEnumArgumentException("value", (int) value, typeof(GridLineStyle)); } if (this.gridLineStyle != value) { this.gridLineStyle = value; if (this.GridLines != GridLines.None) { this.Invalidate(this.PseudoClientRect, false); } } } } /// /// Gets or sets the Color of the grid lines /// [Category("Grid"), Description("The color of the grid lines")] public Color GridColor { get { return this.gridColor; } set { if (this.gridColor != value) { this.gridColor = value; if (this.GridLines != GridLines.None) { this.Invalidate(this.PseudoClientRect, false); } } } } /// /// Specifies whether the Table's GridColor property /// should be serialized at design time /// /// True if the GridColor property should be /// serialized, False otherwise private bool ShouldSerializeGridColor() { return (this.GridColor != SystemColors.Control); } /// /// /// public override Color BackColor { get { return base.BackColor; } set { base.BackColor = value; } } /// /// Specifies whether the Table's BackColor property /// should be serialized at design time /// /// True if the BackColor property should be /// serialized, False otherwise private bool ShouldSerializeBackColor() { return (this.BackColor != Color.White); } #endregion #region Header /// /// Gets or sets the column header style /// [Category("Columns"), DefaultValue(ColumnHeaderStyle.Clickable), Description("The style of the column headers")] public ColumnHeaderStyle HeaderStyle { get { return this.headerStyle; } set { if (!Enum.IsDefined(typeof(ColumnHeaderStyle), value)) { throw new InvalidEnumArgumentException("value", (int) value, typeof(ColumnHeaderStyle)); } if (this.headerStyle != value) { this.headerStyle = value; this.pressedColumn = -1; this.hotColumn = -1; this.Invalidate(); } } } /// /// Gets the height of the column headers /// [Browsable(false)] public int HeaderHeight { get { if (this.ColumnModel == null || this.HeaderStyle == ColumnHeaderStyle.None) { return 0; } return this.ColumnModel.HeaderHeight; } } /// /// Gets a Rectangle that specifies the size and location of /// the Table's column header area /// [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public Rectangle HeaderRectangle { get { return new Rectangle(this.BorderWidth, this.BorderWidth, this.PseudoClientRect.Width, this.HeaderHeight); } } /// /// Gets or sets the font used to draw the text in the column headers /// [Category("Columns"), Description("The font used to draw the text in the column headers")] public Font HeaderFont { get { return this.headerFont; } set { if (value == null) { throw new ArgumentNullException("HeaderFont cannot be null"); } if (this.headerFont != value) { this.headerFont = value; this.HeaderRenderer.Font = value; this.Invalidate(this.HeaderRectangle, false); } } } /// /// Specifies whether the Table's HeaderFont property /// should be serialized at design time /// /// True if the HeaderFont property should be /// serialized, False otherwise private bool ShouldSerializeHeaderFont() { return this.HeaderFont != this.Font; } /// /// Gets or sets the HeaderRenderer used to draw the Column headers /// [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public HeaderRenderer HeaderRenderer { get { if (this.headerRenderer == null) { this.headerRenderer = new XPHeaderRenderer(); } return this.headerRenderer; } set { if (value == null) { throw new ArgumentNullException("HeaderRenderer cannot be null"); } if (this.headerRenderer != value) { this.headerRenderer = value; this.headerRenderer.Font = this.HeaderFont; this.Invalidate(this.HeaderRectangle, false); } } } /// /// Gets the ContextMenu used for Column Headers /// [Browsable(false)] public HeaderContextMenu HeaderContextMenu { get { return this.headerContextMenu; } } /// /// Gets or sets whether the HeaderContextMenu is able to be /// displayed when the user right clicks on a Column Header /// [Category("Columns"), DefaultValue(true), Description("Indicates whether the HeaderContextMenu is able to be displayed when the user right clicks on a Column Header")] public bool EnableHeaderContextMenu { get { return this.HeaderContextMenu.Enabled; } set { this.HeaderContextMenu.Enabled = value; } } #endregion #region Rows /// /// Gets or sets the height of each row /// [Browsable(false)] public int RowHeight { get { if (this.TableModel == null) { return 0; } return this.TableModel.RowHeight; } } /// /// Gets the combined height of all the rows in the Table /// [Browsable(false)] protected int TotalRowHeight { get { if (this.TableModel == null) { return 0; } return this.TableModel.TotalRowHeight; } } /// /// Gets the combined height of all the rows in the Table /// plus the height of the column headers /// [Browsable(false)] protected int TotalRowAndHeaderHeight { get { return this.TotalRowHeight + this.HeaderHeight; } } /// /// Returns the number of Rows in the Table /// [Browsable(false)] public int RowCount { get { if (this.TableModel == null) { return 0; } return this.TableModel.Rows.Count; } } /// /// Gets the number of rows that are visible in the Table /// [Browsable(false)] public int VisibleRowCount { get { int count = this.CellDataRect.Height / this.RowHeight; if ((this.CellDataRect.Height % this.RowHeight) > 0) { count++; } return count; } } /// /// Gets the index of the first visible row in the Table /// [Browsable(false)] public int TopIndex { get { if (this.TableModel == null || this.TableModel.Rows.Count == 0) { return -1; } if (this.VScroll) { return this.vScrollBar.Value; } return 0; } } /// /// Gets the first visible row in the Table /// [Browsable(false)] public Row TopItem { get { if (this.TableModel == null || this.TableModel.Rows.Count == 0) { return null; } return this.TableModel.Rows[this.TopIndex]; } } /// /// Gets or sets the background color of odd-numbered rows in the Table /// [Category("Appearance"), DefaultValue(typeof(Color), "Transparent"), Description("The background color of odd-numbered rows in the Table")] public Color AlternatingRowColor { get { return this.alternatingRowColor; } set { if (this.alternatingRowColor != value) { this.alternatingRowColor = value; this.Invalidate(this.CellDataRect, false); } } } #endregion #region Scrolling /// /// Gets or sets a value indicating whether the Table will /// allow the user to scroll to any columns or rows placed /// outside of its visible boundaries /// [Category("Behavior"), DefaultValue(true), Description("Indicates whether the Table will display scroll bars if it contains more items than can fit in the client area")] public bool Scrollable { get { return this.scrollable; } set { if (this.scrollable != value) { this.scrollable = value; this.PerformLayout(); } } } /// /// Gets a value indicating whether the horizontal /// scroll bar is visible /// [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool HScroll { get { if (this.hScrollBar == null) { return false; } return this.hScrollBar.Visible; } } /// /// Gets a value indicating whether the vertical /// scroll bar is visible /// [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public bool VScroll { get { if (this.vScrollBar == null) { return false; } return this.vScrollBar.Visible; } } #endregion #region Selection /// /// Gets or sets whether cells are allowed to be selected /// [Category("Selection"), DefaultValue(true), Description("Specifies whether cells are allowed to be selected")] public bool AllowSelection { get { return this.allowSelection; } set { if (this.allowSelection != value) { this.allowSelection = value; if (!value && this.TableModel != null) { this.TableModel.Selections.Clear(); } } } } /// /// Gets or sets how selected Cells are drawn by a Table /// [Category("Selection"), DefaultValue(SelectionStyle.ListView), Description("Determines how selected Cells are drawn by a Table")] public SelectionStyle SelectionStyle { get { return this.selectionStyle; } set { if (!Enum.IsDefined(typeof(SelectionStyle), value)) { throw new InvalidEnumArgumentException("value", (int) value, typeof(SelectionStyle)); } if (this.selectionStyle != value) { this.selectionStyle = value; if (this.TableModel != null) { //this.Invalidate(Rectangle.Intersect(this.CellDataRect, this.TableModel.Selections.SelectionBounds), false); this.Invalidate(this.CellDataRect, false); } } } } /// /// Gets or sets whether multiple cells are allowed to be selected /// [Category("Selection"), DefaultValue(false), Description("Specifies whether multiple cells are allowed to be selected")] public bool MultiSelect { get { return this.multiSelect; } set { if (this.multiSelect != value) { this.multiSelect = value; } } } /// /// Gets or sets whether all other cells in the row are highlighted /// when a cell is selected /// [Category("Selection"), DefaultValue(false), Description("Specifies whether all other cells in the row are highlighted when a cell is selected")] public bool FullRowSelect { get { return this.fullRowSelect; } set { if (this.fullRowSelect != value) { this.fullRowSelect = value; if (this.TableModel != null) { //this.Invalidate(Rectangle.Intersect(this.CellDataRect, this.TableModel.Selections.SelectionBounds), false); this.Invalidate(this.CellDataRect, false); } } } } /// /// Gets or sets whether highlighting is removed from the selected /// cells when the Table loses focus /// [Category("Selection"), DefaultValue(false), Description("Specifies whether highlighting is removed from the selected cells when the Table loses focus")] public bool HideSelection { get { return this.hideSelection; } set { if (this.hideSelection != value) { this.hideSelection = value; if (!this.Focused && this.TableModel != null) { //this.Invalidate(Rectangle.Intersect(this.CellDataRect, this.TableModel.Selections.SelectionBounds), false); this.Invalidate(this.CellDataRect, false); } } } } /// /// Gets or sets the background color of a selected cell /// [Category("Selection"), Description("The background color of a selected cell")] public Color SelectionBackColor { get { return this.selectionBackColor; } set { if (this.selectionBackColor != value) { this.selectionBackColor = value; if (this.TableModel != null) { //this.Invalidate(Rectangle.Intersect(this.CellDataRect, this.TableModel.Selections.SelectionBounds), false); this.Invalidate(this.CellDataRect, false); } } } } /// /// Specifies whether the Table's SelectionBackColor property /// should be serialized at design time /// /// True if the SelectionBackColor property should be /// serialized, False otherwise private bool ShouldSerializeSelectionBackColor() { return (this.selectionBackColor != SystemColors.Highlight); } /// /// Gets or sets the foreground color of a selected cell /// [Category("Selection"), Description("The foreground color of a selected cell")] public Color SelectionForeColor { get { return this.selectionForeColor; } set { if (this.selectionForeColor != value) { this.selectionForeColor = value; if (this.TableModel != null) { //this.Invalidate(Rectangle.Intersect(this.CellDataRect, this.TableModel.Selections.SelectionBounds), false); this.Invalidate(this.CellDataRect, false); } } } } /// /// Specifies whether the Table's SelectionForeColor property /// should be serialized at design time /// /// True if the SelectionForeColor property should be /// serialized, False otherwise private bool ShouldSerializeSelectionForeColor() { return (this.selectionForeColor != SystemColors.HighlightText); } /// /// Gets or sets the background color of a selected cell when the /// Table doesn't have the focus /// [Category("Selection"), Description("The background color of a selected cell when the Table doesn't have the focus")] public Color UnfocusedSelectionBackColor { get { return this.unfocusedSelectionBackColor; } set { if (this.unfocusedSelectionBackColor != value) { this.unfocusedSelectionBackColor = value; if (!this.Focused && !this.HideSelection && this.TableModel != null) { //this.Invalidate(Rectangle.Intersect(this.CellDataRect, this.TableModel.Selections.SelectionBounds), false); this.Invalidate(this.CellDataRect, false); } } } } /// /// Specifies whether the Table's UnfocusedSelectionBackColor property /// should be serialized at design time /// /// True if the UnfocusedSelectionBackColor property should be /// serialized, False otherwise private bool ShouldSerializeUnfocusedSelectionBackColor() { return (this.unfocusedSelectionBackColor != SystemColors.Control); } /// /// Gets or sets the foreground color of a selected cell when the /// Table doesn't have the focus /// [Category("Selection"), Description("The foreground color of a selected cell when the Table doesn't have the focus")] public Color UnfocusedSelectionForeColor { get { return this.unfocusedSelectionForeColor; } set { if (this.unfocusedSelectionForeColor != value) { this.unfocusedSelectionForeColor = value; if (!this.Focused && !this.HideSelection && this.TableModel != null) { //this.Invalidate(Rectangle.Intersect(this.CellDataRect, this.TableModel.Selections.SelectionBounds), false); this.Invalidate(this.CellDataRect, false); } } } } /// /// Specifies whether the Table's UnfocusedSelectionForeColor property /// should be serialized at design time /// /// True if the UnfocusedSelectionForeColor property should be /// serialized, False otherwise private bool ShouldSerializeUnfocusedSelectionForeColor() { return (this.unfocusedSelectionForeColor != SystemColors.ControlText); } /// /// Gets an array that contains the currently selected Rows /// [Browsable(false)] public Row[] SelectedItems { get { if (this.TableModel == null) { return new Row[0]; } return this.TableModel.Selections.SelectedItems; } } /// /// Gets an array that contains the indexes of the currently selected Rows /// [Browsable(false)] public int[] SelectedIndicies { get { if (this.TableModel == null) { return new int[0]; } return this.TableModel.Selections.SelectedIndicies; } } #endregion #region TableModel /// /// Gets or sets the TableModel that contains all the Rows /// and Cells displayed in the Table /// [Category("Items"), DefaultValue(null), Description("Specifies the TableModel that contains all the Rows and Cells displayed in the Table")] public TableModel TableModel { get { return this.tableModel; } set { if (this.tableModel != value) { if (this.tableModel != null && this.tableModel.Table == this) { this.tableModel.InternalTable = null; } this.tableModel = value; if (value != null) { value.InternalTable = this; } this.OnTableModelChanged(EventArgs.Empty); } } } /// /// Gets or sets the text displayed by the Table when it doesn't /// contain any items /// [Category("Appearance"), DefaultValue("There are no items in this view"), Description("Specifies the text displayed by the Table when it doesn't contain any items")] public string NoItemsText { get { return this.noItemsText; } set { if (!this.noItemsText.Equals(value)) { this.noItemsText = value; if (this.ColumnModel == null || this.TableModel == null || this.TableModel.Rows.Count == 0) { this.Invalidate(this.PseudoClientRect); } } } } #endregion #region TableState /// /// Gets or sets the current state of the Table /// protected TableState TableState { get { return this.tableState; } set { this.tableState = value; } } /// /// Calculates the state of the Table at the specified /// client coordinates /// /// The client x coordinate /// The client y coordinate protected void CalcTableState(int x, int y) { TableRegion region = this.HitTest(x, y); // are we in the header if (region == TableRegion.ColumnHeader) { int column = this.ColumnIndexAt(x, y); // get out of here if we aren't in a column if (column == -1) { this.TableState = TableState.Normal; return; } // get the bounding rectangle for the column's header Rectangle columnRect = this.ColumnModel.ColumnHeaderRect(column); x = this.ClientToDisplayRect(x, y).X; // are we in a resizing section on the left if (x < columnRect.Left + Column.ResizePadding) { this.TableState = TableState.ColumnResizing; while (column != 0) { if (this.ColumnModel.Columns[column-1].Visible) { break; } column--; } // if we are in the first visible column or the next column // to the left is disabled, then we should be potentialy // selecting instead of resizing if (column == 0 || !this.ColumnModel.Columns[column-1].Enabled) { this.TableState = TableState.ColumnSelecting; } } // or a resizing section on the right else if (x > columnRect.Right - Column.ResizePadding) { this.TableState = TableState.ColumnResizing; } // looks like we're somewhere in the middle of // the column header else { this.TableState = TableState.ColumnSelecting; } } else if (region == TableRegion.Cells) { this.TableState = TableState.Selecting; } else { this.TableState = TableState.Normal; } if (this.TableState == TableState.ColumnResizing && !this.ColumnResizing) { this.TableState = TableState.ColumnSelecting; } } /// /// Gets whether the Table is able to raise events /// protected internal bool CanRaiseEvents { get { return (this.IsHandleCreated && this.beginUpdateCount == 0); } } /// /// Gets or sets whether the Table is being used as a preview Table in /// a ColumnCollectionEditor /// internal bool Preview { get { return this.preview; } set { this.preview = value; } } #endregion #region ToolTips /// /// Gets the internal tooltip component /// internal ToolTip ToolTip { get { return this.toolTip; } } /// /// Gets or sets whether ToolTips are currently enabled for the Table /// [Category("ToolTips"), DefaultValue(false), Description("Specifies whether ToolTips are enabled for the Table.")] public bool EnableToolTips { get { return this.toolTip.Active; } set { this.toolTip.Active = value; } } /// /// Gets or sets the automatic delay for the Table's ToolTip /// [Category("ToolTips"), DefaultValue(500), Description("Specifies the automatic delay for the Table's ToolTip.")] public int ToolTipAutomaticDelay { get { return this.toolTip.AutomaticDelay; } set { if (value > 0 && this.toolTip.AutomaticDelay != value) { this.toolTip.AutomaticDelay = value; } } } /// /// Gets or sets the period of time the Table's ToolTip remains visible if /// the mouse pointer is stationary within a Cell with a valid ToolTip text /// [Category("ToolTips"), DefaultValue(5000), Description("Specifies the period of time the Table's ToolTip remains visible if the mouse pointer is stationary within a cell with specified ToolTip text.")] public int ToolTipAutoPopDelay { get { return this.toolTip.AutoPopDelay; } set { if (value > 0 && this.toolTip.AutoPopDelay != value) { this.toolTip.AutoPopDelay = value; } } } /// /// Gets or sets the time that passes before the Table's ToolTip appears /// [Category("ToolTips"), DefaultValue(1000), Description("Specifies the time that passes before the Table's ToolTip appears.")] public int ToolTipInitialDelay { get { return this.toolTip.InitialDelay; } set { if (value > 0 && this.toolTip.InitialDelay != value) { this.toolTip.InitialDelay = value; } } } /// /// Gets or sets whether the Table's ToolTip window is /// displayed even when its parent control is not active /// [Category("ToolTips"), DefaultValue(false), Description("Specifies whether the Table's ToolTip window is displayed even when its parent control is not active.")] public bool ToolTipShowAlways { get { return this.toolTip.ShowAlways; } set { if (this.toolTip.ShowAlways != value) { this.toolTip.ShowAlways = value; } } } /// /// /// private void ResetToolTip() { bool tooltipActive = this.ToolTip.Active; if (tooltipActive) { this.ToolTip.Active = false; } this.ResetMouseEventArgs(); this.ToolTip.SetToolTip(this, null); if (tooltipActive) { this.ToolTip.Active = true; } } #endregion #endregion #region Events #region Cells /// /// Raises the CellPropertyChanged event /// /// A CellEventArgs that contains the event data protected internal virtual void OnCellPropertyChanged(CellEventArgs e) { if (this.CanRaiseEvents) { this.InvalidateCell(e.Row, e.Column); if (CellPropertyChanged != null) { CellPropertyChanged(this, e); } if (e.EventType == CellEventType.CheckStateChanged) { this.OnCellCheckChanged(new CellCheckBoxEventArgs(e.Cell, e.Column, e.Row)); } } } /// /// Handler for a Cells PropertyChanged event /// /// The object that raised the event /// A CellEventArgs that contains the event data private void cell_PropertyChanged(object sender, CellEventArgs e) { this.OnCellPropertyChanged(e); } #region Buttons /// /// Raises the CellButtonClicked event /// /// A CellButtonEventArgs that contains the event data protected internal virtual void OnCellButtonClicked(CellButtonEventArgs e) { if (this.CanRaiseEvents) { if (CellButtonClicked != null) { CellButtonClicked(this, e); } } } #endregion #region CheckBox /// /// Raises the CellCheckChanged event /// /// A CellCheckChanged that contains the event data protected internal virtual void OnCellCheckChanged(CellCheckBoxEventArgs e) { if (this.CanRaiseEvents) { if (CellCheckChanged != null) { CellCheckChanged(this, e); } } } #endregion #region Focus /// /// Raises the CellGotFocus event /// /// A CellFocusEventArgs that contains the event data protected virtual void OnCellGotFocus(CellFocusEventArgs e) { if (this.CanRaiseEvents) { ICellRenderer renderer = this.ColumnModel.GetCellRenderer(e.Column); if (renderer != null) { renderer.OnGotFocus(e); } if (CellGotFocus != null) { CellGotFocus(this, e); } } } /// /// Raises the GotFocus event for the Cell at the specified position /// /// The position of the Cell that gained focus protected void RaiseCellGotFocus(CellPos cellPos) { if (!this.IsValidCell(cellPos)) { return; } ICellRenderer renderer = this.ColumnModel.GetCellRenderer(cellPos.Column); if (renderer != null) { Cell cell = null; if (cellPos.Column < this.TableModel.Rows[cellPos.Row].Cells.Count) { cell = this.TableModel.Rows[cellPos.Row].Cells[cellPos.Column]; } CellFocusEventArgs cfea = new CellFocusEventArgs(cell, this, cellPos.Row, cellPos.Column, this.CellRect(cellPos.Row, cellPos.Column)); this.OnCellGotFocus(cfea); } } /// /// Raises the CellLostFocus event /// /// A CellFocusEventArgs that contains the event data protected virtual void OnCellLostFocus(CellFocusEventArgs e) { if (this.CanRaiseEvents) { ICellRenderer renderer = this.ColumnModel.GetCellRenderer(e.Column); if (renderer != null) { renderer.OnLostFocus(e); } if (CellLostFocus != null) { CellLostFocus(this, e); } } } /// /// Raises the LostFocus event for the Cell at the specified position /// /// The position of the Cell that lost focus protected void RaiseCellLostFocus(CellPos cellPos) { if (!this.IsValidCell(cellPos)) { return; } ICellRenderer renderer = this.ColumnModel.GetCellRenderer(cellPos.Column); if (renderer != null) { Cell cell = null; if (cellPos.Column < this.TableModel.Rows[cellPos.Row].Cells.Count) { cell = this.TableModel[cellPos.Row, cellPos.Column]; } CellFocusEventArgs cfea = new CellFocusEventArgs(cell, this, cellPos.Row, cellPos.Column, this.CellRect(cellPos.Row, cellPos.Column)); this.OnCellLostFocus(cfea); } } #endregion #region Keys /// /// Raises the CellKeyDown event /// /// A CellKeyEventArgs that contains the event data protected virtual void OnCellKeyDown(CellKeyEventArgs e) { if (this.CanRaiseEvents) { ICellRenderer renderer = this.ColumnModel.GetCellRenderer(e.Column); if (renderer != null) { renderer.OnKeyDown(e); } if (CellKeyDown != null) { CellKeyDown(e.Cell, e); } } } /// /// Raises a KeyDown event for the Cell at the specified cell position /// /// The position of the Cell /// A KeyEventArgs that contains the event data protected void RaiseCellKeyDown(CellPos cellPos, KeyEventArgs e) { if (!this.IsValidCell(cellPos)) { return; } if (!this.TableModel[cellPos].Enabled) { return; } if (this.ColumnModel.GetCellRenderer(cellPos.Column) != null) { Cell cell = null; if (cellPos.Column < this.TableModel.Rows[cellPos.Row].Cells.Count) { cell = this.TableModel.Rows[cellPos.Row].Cells[cellPos.Column]; } CellKeyEventArgs ckea = new CellKeyEventArgs(cell, this, cellPos, this.CellRect(cellPos.Row, cellPos.Column), e); this.OnCellKeyDown(ckea); } } /// /// Raises the CellKeyUp event /// /// A CellKeyEventArgs that contains the event data protected virtual void OnCellKeyUp(CellKeyEventArgs e) { if (this.CanRaiseEvents) { ICellRenderer renderer = this.ColumnModel.GetCellRenderer(e.Column); if (renderer != null) { renderer.OnKeyUp(e); } if (CellKeyUp != null) { CellKeyUp(e.Cell, e); } } } /// /// Raises a KeyUp event for the Cell at the specified cell position /// /// The position of the Cell /// A KeyEventArgs that contains the event data protected void RaiseCellKeyUp(CellPos cellPos, KeyEventArgs e) { if (!this.IsValidCell(cellPos)) { return; } if (!this.TableModel[cellPos].Enabled) { return; } if (this.ColumnModel.GetCellRenderer(cellPos.Column) != null) { Cell cell = null; if (cellPos.Column < this.TableModel.Rows[cellPos.Row].Cells.Count) { cell = this.TableModel.Rows[cellPos.Row].Cells[cellPos.Column]; } CellKeyEventArgs ckea = new CellKeyEventArgs(cell, this, cellPos, this.CellRect(cellPos.Row, cellPos.Column), e); this.OnCellKeyUp(ckea); } } #endregion #region Mouse #region MouseEnter /// /// Raises the CellMouseEnter event /// /// A CellMouseEventArgs that contains the event data protected virtual void OnCellMouseEnter(CellMouseEventArgs e) { if (this.CanRaiseEvents) { ICellRenderer renderer = this.ColumnModel.GetCellRenderer(e.Column); if (renderer != null) { renderer.OnMouseEnter(e); } if (CellMouseEnter != null) { CellMouseEnter(e.Cell, e); } } } /// /// Raises a MouseEnter event for the Cell at the specified cell position /// /// The position of the Cell protected void RaiseCellMouseEnter(CellPos cellPos) { if (!this.IsValidCell(cellPos)) { return; } if (this.ColumnModel.GetCellRenderer(cellPos.Column) != null) { Cell cell = null; if (cellPos.Column < this.TableModel.Rows[cellPos.Row].Cells.Count) { cell = this.TableModel.Rows[cellPos.Row].Cells[cellPos.Column]; } CellMouseEventArgs mcea = new CellMouseEventArgs(cell, this, cellPos.Row, cellPos.Column, this.CellRect(cellPos.Row, cellPos.Column)); this.OnCellMouseEnter(mcea); } } #endregion #region MouseLeave /// /// Raises the CellMouseLeave event /// /// A CellMouseEventArgs that contains the event data protected virtual void OnCellMouseLeave(CellMouseEventArgs e) { if (this.CanRaiseEvents) { ICellRenderer renderer = this.ColumnModel.GetCellRenderer(e.Column); if (renderer != null) { renderer.OnMouseLeave(e); } if (CellMouseLeave != null) { CellMouseLeave(e.Cell, e); } } } /// /// Raises a MouseLeave event for the Cell at the specified cell position /// /// The position of the Cell protected internal void RaiseCellMouseLeave(CellPos cellPos) { if (!this.IsValidCell(cellPos)) { return; } if (this.ColumnModel.GetCellRenderer(cellPos.Column) != null) { Cell cell = null; if (cellPos.Column < this.TableModel.Rows[cellPos.Row].Cells.Count) { cell = this.TableModel.Rows[cellPos.Row].Cells[cellPos.Column]; } CellMouseEventArgs mcea = new CellMouseEventArgs(cell, this, cellPos.Row, cellPos.Column, this.CellRect(cellPos.Row, cellPos.Column)); this.OnCellMouseLeave(mcea); } } #endregion #region MouseUp /// /// Raises the CellMouseUp event /// /// A CellMouseEventArgs that contains the event data protected virtual void OnCellMouseUp(CellMouseEventArgs e) { if (this.CanRaiseEvents) { ICellRenderer renderer = this.ColumnModel.GetCellRenderer(e.Column); if (renderer != null) { renderer.OnMouseUp(e); } if (CellMouseUp != null) { CellMouseUp(e.Cell, e); } } } /// /// Raises a MouseUp event for the Cell at the specified cell position /// /// The position of the Cell /// A MouseEventArgs that contains the event data protected void RaiseCellMouseUp(CellPos cellPos, MouseEventArgs e) { if (!this.IsValidCell(cellPos)) { return; } if (!this.TableModel[cellPos].Enabled) { return; } if (this.ColumnModel.GetCellRenderer(cellPos.Column) != null) { Cell cell = null; if (cellPos.Column < this.TableModel.Rows[cellPos.Row].Cells.Count) { cell = this.TableModel.Rows[cellPos.Row].Cells[cellPos.Column]; } CellMouseEventArgs mcea = new CellMouseEventArgs(cell, this, cellPos.Row, cellPos.Column, this.CellRect(cellPos.Row, cellPos.Column), e); this.OnCellMouseUp(mcea); } } #endregion #region MouseDown /// /// Raises the CellMouseDown event /// /// A CellMouseEventArgs that contains the event data protected virtual void OnCellMouseDown(CellMouseEventArgs e) { if (this.CanRaiseEvents) { ICellRenderer renderer = this.ColumnModel.GetCellRenderer(e.Column); if (renderer != null) { renderer.OnMouseDown(e); } if (CellMouseDown != null) { CellMouseDown(e.Cell, e); } } } /// /// Raises a MouseDown event for the Cell at the specified cell position /// /// The position of the Cell /// A MouseEventArgs that contains the event data protected void RaiseCellMouseDown(CellPos cellPos, MouseEventArgs e) { if (!this.IsValidCell(cellPos)) { return; } if (!this.TableModel[cellPos].Enabled) { return; } if (this.ColumnModel.GetCellRenderer(cellPos.Column) != null) { Cell cell = null; if (cellPos.Column < this.TableModel.Rows[cellPos.Row].Cells.Count) { cell = this.TableModel.Rows[cellPos.Row].Cells[cellPos.Column]; } CellMouseEventArgs mcea = new CellMouseEventArgs(cell, this, cellPos.Row, cellPos.Column, this.CellRect(cellPos.Row, cellPos.Column), e); this.OnCellMouseDown(mcea); } } #endregion #region MouseMove /// /// Raises the CellMouseMove event /// /// A CellMouseEventArgs that contains the event data protected virtual void OnCellMouseMove(CellMouseEventArgs e) { if (this.CanRaiseEvents) { ICellRenderer renderer = this.ColumnModel.GetCellRenderer(e.Column); if (renderer != null) { renderer.OnMouseMove(e); } if (CellMouseMove != null) { CellMouseMove(e.Cell, e); } } } /// /// Raises a MouseMove event for the Cell at the specified cell position /// /// The position of the Cell /// A MouseEventArgs that contains the event data protected void RaiseCellMouseMove(CellPos cellPos, MouseEventArgs e) { if (!this.IsValidCell(cellPos)) { return; } if (this.ColumnModel.GetCellRenderer(cellPos.Column) != null) { Cell cell = null; if (cellPos.Column < this.TableModel.Rows[cellPos.Row].Cells.Count) { cell = this.TableModel.Rows[cellPos.Row].Cells[cellPos.Column]; } CellMouseEventArgs mcea = new CellMouseEventArgs(cell, this, cellPos.Row, cellPos.Column, this.CellRect(cellPos.Row, cellPos.Column), e); this.OnCellMouseMove(mcea); } } /// /// Resets the last known cell position that the mouse was over to empty /// internal void ResetLastMouseCell() { if (!this.lastMouseCell.IsEmpty) { this.ResetMouseEventArgs(); CellPos oldLastMouseCell = this.lastMouseCell; this.lastMouseCell = CellPos.Empty; this.RaiseCellMouseLeave(oldLastMouseCell); } } #endregion #region MouseHover /// /// Raises the CellHover event /// /// A CellEventArgs that contains the event data protected virtual void OnCellMouseHover(CellMouseEventArgs e) { if (this.CanRaiseEvents) { if (CellMouseHover != null) { CellMouseHover(e.Cell, e); } } } #endregion #region Click /// /// Raises the CellClick event /// /// A CellEventArgs that contains the event data protected virtual void OnCellClick(CellMouseEventArgs e) { if (!this.IsCellEnabled(e.CellPos)) { return; } if (this.CanRaiseEvents) { ICellRenderer renderer = this.ColumnModel.GetCellRenderer(this.LastMouseCell.Column); if (renderer != null) { renderer.OnClick(e); } if (CellClick != null) { CellClick(e.Cell, e); } } } /// /// Raises the CellDoubleClick event /// /// A CellEventArgs that contains the event data protected virtual void OnCellDoubleClick(CellMouseEventArgs e) { if (!this.IsCellEnabled(e.CellPos)) { return; } if (this.CanRaiseEvents) { ICellRenderer renderer = this.ColumnModel.GetCellRenderer(this.LastMouseCell.Column); if (renderer != null) { renderer.OnDoubleClick(e); } if (CellDoubleClick != null) { CellDoubleClick(e.Cell, e); } } } #endregion #endregion #endregion #region Columns /// /// Raises the ColumnPropertyChanged event /// /// A ColumnEventArgs that contains the event data protected internal virtual void OnColumnPropertyChanged(ColumnEventArgs e) { if (this.CanRaiseEvents) { Rectangle columnHeaderRect; if (e.Index != -1) { columnHeaderRect = this.ColumnHeaderRect(e.Index); } else { columnHeaderRect = this.ColumnHeaderRect(e.Column); } switch (e.EventType) { case ColumnEventType.VisibleChanged: case ColumnEventType.WidthChanged: { if (e.EventType == ColumnEventType.VisibleChanged) { if (e.Column.Visible && e.Index != this.lastSortedColumn) { e.Column.InternalSortOrder = SortOrder.None; } if (e.Index == this.FocusedCell.Column && !e.Column.Visible) { int index = this.ColumnModel.NextVisibleColumn(e.Index); if (index == -1) { index = this.ColumnModel.PreviousVisibleColumn(e.Index); } if (index != -1) { this.FocusedCell = new CellPos(this.FocusedCell.Row, index); } else { this.FocusedCell = CellPos.Empty; } } } if (columnHeaderRect.X <= 0) { this.Invalidate(this.PseudoClientRect); } else if (columnHeaderRect.Left <= this.PseudoClientRect.Right) { this.Invalidate(new Rectangle(columnHeaderRect.X, this.PseudoClientRect.Top, this.PseudoClientRect.Right-columnHeaderRect.X, this.PseudoClientRect.Height)); } this.UpdateScrollBars(); break; } case ColumnEventType.TextChanged: case ColumnEventType.StateChanged: case ColumnEventType.ImageChanged: case ColumnEventType.HeaderAlignmentChanged: { if (columnHeaderRect.IntersectsWith(this.HeaderRectangle)) { this.Invalidate(columnHeaderRect); } break; } case ColumnEventType.AlignmentChanged: case ColumnEventType.RendererChanged: case ColumnEventType.EnabledChanged: { if (e.EventType == ColumnEventType.EnabledChanged) { if (e.Index == this.FocusedCell.Column) { this.FocusedCell = CellPos.Empty; } } if (columnHeaderRect.IntersectsWith(this.HeaderRectangle)) { this.Invalidate(new Rectangle(columnHeaderRect.X, this.PseudoClientRect.Top, columnHeaderRect.Width, this.PseudoClientRect.Height)); } break; } } if (ColumnPropertyChanged != null) { ColumnPropertyChanged(e.Column, e); } } } #endregion #region Column Headers #region MouseEnter /// /// Raises the HeaderMouseEnter event /// /// A HeaderMouseEventArgs that contains the event data protected virtual void OnHeaderMouseEnter(HeaderMouseEventArgs e) { if (this.CanRaiseEvents) { if (this.HeaderRenderer != null) { this.HeaderRenderer.OnMouseEnter(e); } if (HeaderMouseEnter != null) { HeaderMouseEnter(e.Column, e); } } } /// /// Raises a MouseEnter event for the Column header at the specified colunm /// index position /// /// The index of the column to recieve the event protected void RaiseHeaderMouseEnter(int index) { if (index < 0 || this.ColumnModel == null || index >= this.ColumnModel.Columns.Count) { return; } if (this.HeaderRenderer != null) { Column column = this.ColumnModel.Columns[index]; HeaderMouseEventArgs mhea = new HeaderMouseEventArgs(column, this, index, this.DisplayRectToClient(this.ColumnModel.ColumnHeaderRect(index))); this.OnHeaderMouseEnter(mhea); } } #endregion #region MouseLeave /// /// Raises the HeaderMouseLeave event /// /// A HeaderMouseEventArgs that contains the event data protected virtual void OnHeaderMouseLeave(HeaderMouseEventArgs e) { if (this.CanRaiseEvents) { if (this.HeaderRenderer != null) { this.HeaderRenderer.OnMouseLeave(e); } if (HeaderMouseLeave != null) { HeaderMouseLeave(e.Column, e); } } } /// /// Raises a MouseLeave event for the Column header at the specified colunm /// index position /// /// The index of the column to recieve the event protected void RaiseHeaderMouseLeave(int index) { if (index < 0 || this.ColumnModel == null || index >= this.ColumnModel.Columns.Count) { return; } if (this.HeaderRenderer != null) { Column column = this.ColumnModel.Columns[index]; HeaderMouseEventArgs mhea = new HeaderMouseEventArgs(column, this, index, this.DisplayRectToClient(this.ColumnModel.ColumnHeaderRect(index))); this.OnHeaderMouseLeave(mhea); } } #endregion #region MouseUp /// /// Raises the HeaderMouseUp event /// /// A HeaderMouseEventArgs that contains the event data protected virtual void OnHeaderMouseUp(HeaderMouseEventArgs e) { if (this.CanRaiseEvents) { if (this.HeaderRenderer != null) { this.HeaderRenderer.OnMouseUp(e); } if (HeaderMouseUp != null) { HeaderMouseUp(e.Column, e); } } } /// /// Raises a MouseUp event for the Column header at the specified colunm /// index position /// /// The index of the column to recieve the event /// A HeaderMouseEventArgs that contains the event data protected void RaiseHeaderMouseUp(int index, MouseEventArgs e) { if (index < 0 || this.ColumnModel == null || index >= this.ColumnModel.Columns.Count) { return; } if (this.HeaderRenderer != null) { Column column = this.ColumnModel.Columns[index]; HeaderMouseEventArgs mhea = new HeaderMouseEventArgs(column, this, index, this.DisplayRectToClient(this.ColumnModel.ColumnHeaderRect(index)), e); this.OnHeaderMouseUp(mhea); } } #endregion #region MouseDown /// /// Raises the HeaderMouseDown event /// /// A HeaderMouseEventArgs that contains the event data protected virtual void OnHeaderMouseDown(HeaderMouseEventArgs e) { if (this.CanRaiseEvents) { if (this.HeaderRenderer != null) { this.HeaderRenderer.OnMouseDown(e); } if (HeaderMouseDown != null) { HeaderMouseDown(e.Column, e); } } } /// /// Raises a MouseDown event for the Column header at the specified colunm /// index position /// /// The index of the column to recieve the event /// A HeaderMouseEventArgs that contains the event data protected void RaiseHeaderMouseDown(int index, MouseEventArgs e) { if (index < 0 || this.ColumnModel == null || index >= this.ColumnModel.Columns.Count) { return; } if (this.HeaderRenderer != null) { Column column = this.ColumnModel.Columns[index]; HeaderMouseEventArgs mhea = new HeaderMouseEventArgs(column, this, index, this.DisplayRectToClient(this.ColumnModel.ColumnHeaderRect(index)), e); this.OnHeaderMouseDown(mhea); } } #endregion #region MouseMove /// /// Raises the HeaderMouseMove event /// /// A HeaderMouseEventArgs that contains the event data protected virtual void OnHeaderMouseMove(HeaderMouseEventArgs e) { if (this.CanRaiseEvents) { if (this.HeaderRenderer != null) { this.HeaderRenderer.OnMouseMove(e); } if (HeaderMouseMove != null) { HeaderMouseMove(e.Column, e); } } } /// /// Raises a MouseMove event for the Column header at the specified colunm /// index position /// /// The index of the column to recieve the event /// A HeaderMouseEventArgs that contains the event data protected void RaiseHeaderMouseMove(int index, MouseEventArgs e) { if (index < 0 || this.ColumnModel == null || index >= this.ColumnModel.Columns.Count) { return; } if (this.HeaderRenderer != null) { Column column = this.ColumnModel.Columns[index]; HeaderMouseEventArgs mhea = new HeaderMouseEventArgs(column, this, index, this.DisplayRectToClient(this.ColumnModel.ColumnHeaderRect(index)), e); this.OnHeaderMouseMove(mhea); } } /// /// Resets the current "hot" column /// internal void ResetHotColumn() { if (this.hotColumn != -1) { this.ResetMouseEventArgs(); int oldHotColumn = this.hotColumn; this.hotColumn = -1; this.RaiseHeaderMouseLeave(oldHotColumn); } } #endregion #region MouseHover /// /// Raises the HeaderMouseHover event /// /// A HeaderMouseEventArgs that contains the event data protected virtual void OnHeaderMouseHover(HeaderMouseEventArgs e) { if (this.CanRaiseEvents) { if (HeaderMouseHover != null) { HeaderMouseHover(e.Column, e); } } } #endregion #region Click /// /// Raises the HeaderClick event /// /// A HeaderMouseEventArgs that contains the event data protected virtual void OnHeaderClick(HeaderMouseEventArgs e) { if (this.CanRaiseEvents) { if (this.HeaderRenderer != null) { this.HeaderRenderer.OnClick(e); } if (HeaderClick != null) { HeaderClick(e.Column, e); } } } /// /// Raises the HeaderDoubleClick event /// /// A HeaderMouseEventArgs that contains the event data protected virtual void OnHeaderDoubleClick(HeaderMouseEventArgs e) { if (this.CanRaiseEvents) { if (this.HeaderRenderer != null) { this.HeaderRenderer.OnDoubleClick(e); } if (HeaderDoubleClick != null) { HeaderDoubleClick(e.Column, e); } } } #endregion #endregion #region ColumnModel /// /// Raises the ColumnModelChanged event /// /// An EventArgs that contains the event data protected virtual void OnColumnModelChanged(EventArgs e) { if (this.CanRaiseEvents) { this.PerformLayout(); this.Invalidate(); if (ColumnModelChanged != null) { ColumnModelChanged(this, e); } } } /// /// Raises the ColumnAdded event /// /// A ColumnModelEventArgs that contains the event data protected internal virtual void OnColumnAdded(ColumnModelEventArgs e) { if (this.CanRaiseEvents) { this.PerformLayout(); this.Invalidate(); if (ColumnAdded != null) { ColumnAdded(this, e); } } } /// /// Raises the ColumnRemoved event /// /// A ColumnModelEventArgs that contains the event data protected internal virtual void OnColumnRemoved(ColumnModelEventArgs e) { if (this.CanRaiseEvents) { this.PerformLayout(); this.Invalidate(); if (ColumnRemoved != null) { ColumnRemoved(this, e); } } } /// /// Raises the HeaderHeightChanged event /// /// An EventArgs that contains the event data protected internal virtual void OnHeaderHeightChanged(EventArgs e) { if (this.CanRaiseEvents) { this.PerformLayout(); this.Invalidate(); if (HeaderHeightChanged != null) { HeaderHeightChanged(this, e); } } } #endregion #region Editing /// /// Raises the BeginEditing event /// /// A CellEditEventArgs that contains the event data protected internal virtual void OnBeginEditing(CellEditEventArgs e) { if (this.CanRaiseEvents) { if (BeginEditing != null) { BeginEditing(e.Cell, e); } } } /// /// Raises the EditingStopped event /// /// A CellEditEventArgs that contains the event data protected internal virtual void OnEditingStopped(CellEditEventArgs e) { if (this.CanRaiseEvents) { if (EditingStopped != null) { EditingStopped(e.Cell, e); } } } /// /// Raises the EditingCancelled event /// /// A CellEditEventArgs that contains the event data protected internal virtual void OnEditingCancelled(CellEditEventArgs e) { if (this.CanRaiseEvents) { if (EditingCancelled != null) { EditingCancelled(e.Cell, e); } } } #endregion #region Focus /// /// Raises the GotFocus event /// /// An EventArgs that contains the event data protected override void OnGotFocus(EventArgs e) { if (this.FocusedCell.IsEmpty) { CellPos p = this.FindNextVisibleEnabledCell(this.FocusedCell, true, true, true, true); if (this.IsValidCell(p)) { this.FocusedCell = p; } } else { this.RaiseCellGotFocus(this.FocusedCell); } if (this.SelectedIndicies.Length > 0) { this.Invalidate(this.CellDataRect); } base.OnGotFocus(e); } /// /// Raises the LostFocus event /// /// An EventArgs that contains the event data protected override void OnLostFocus(EventArgs e) { if (!this.FocusedCell.IsEmpty) { this.RaiseCellLostFocus(this.FocusedCell); } if (this.SelectedIndicies.Length > 0) { this.Invalidate(this.CellDataRect); } base.OnLostFocus(e); } #endregion #region Keys #region KeyDown /// /// Raises the KeyDown event /// /// A KeyEventArgs that contains the event data protected override void OnKeyDown(KeyEventArgs e) { base.OnKeyDown(e); if (this.IsValidCell(this.FocusedCell)) { if (this.IsReservedKey(e.KeyData)) { Keys key = e.KeyData & Keys.KeyCode; if (key == Keys.Up || key == Keys.Down || key == Keys.Left || key == Keys.Right) { CellPos nextCell; if (key == Keys.Up) { nextCell = this.FindNextVisibleEnabledCell(this.FocusedCell, this.FocusedCell.Row > 0, false, false, false); } else if (key == Keys.Down) { nextCell = this.FindNextVisibleEnabledCell(this.FocusedCell, this.FocusedCell.Row < this.RowCount - 1, true, false, false); } else if (key == Keys.Left) { nextCell = this.FindNextVisibleEnabledCell(this.FocusedCell, false, false, false, true); } else { nextCell = this.FindNextVisibleEnabledCell(this.FocusedCell, false, true, false, true); } if (nextCell != CellPos.Empty) { this.FocusedCell = nextCell; if ((e.KeyData & Keys.Modifiers) == Keys.Shift && this.MultiSelect) { this.TableModel.Selections.AddShiftSelectedCell(this.FocusedCell); } else { this.TableModel.Selections.SelectCell(this.FocusedCell); } } } else if (e.KeyData == Keys.PageUp) { if (this.RowCount > 0) { CellPos nextCell; if (!this.VScroll) { nextCell = this.FindNextVisibleEnabledCell(new CellPos(0, this.FocusedCell.Column), true, true, true, false); } else { if (this.FocusedCell.Row > this.vScrollBar.Value && this.TableModel[this.vScrollBar.Value, this.FocusedCell.Column].Enabled) { nextCell = this.FindNextVisibleEnabledCell(new CellPos(this.vScrollBar.Value, this.FocusedCell.Column), true, true, true, false); } else { nextCell = this.FindNextVisibleEnabledCell(new CellPos(Math.Max(-1, this.vScrollBar.Value - (this.vScrollBar.LargeChange - 1)), this.FocusedCell.Column), true, true, true, false); } } if (nextCell != CellPos.Empty) { this.FocusedCell = nextCell; this.TableModel.Selections.SelectCell(this.FocusedCell); } } } else if (e.KeyData == Keys.PageDown) { if (this.RowCount > 0) { CellPos nextCell; if (!this.VScroll) { nextCell = this.FindNextVisibleEnabledCell(new CellPos(this.RowCount - 1, this.FocusedCell.Column), true, false, true, false); } else { if (this.FocusedCell.Row < this.vScrollBar.Value + this.vScrollBar.LargeChange) { if (this.FocusedCell.Row == (this.vScrollBar.Value + this.vScrollBar.LargeChange) - 1 && this.RowRect(this.vScrollBar.Value + this.vScrollBar.LargeChange).Bottom > this.CellDataRect.Bottom) { nextCell = this.FindNextVisibleEnabledCell(new CellPos(Math.Min(this.RowCount - 1, this.FocusedCell.Row - 1 + this.vScrollBar.LargeChange), this.FocusedCell.Column), true, false, true, false); } else { nextCell = this.FindNextVisibleEnabledCell(new CellPos(this.vScrollBar.Value + this.vScrollBar.LargeChange - 1, this.FocusedCell.Column), true, false, true, false); } } else { nextCell = this.FindNextVisibleEnabledCell(new CellPos(Math.Min(this.RowCount - 1, this.FocusedCell.Row + this.vScrollBar.LargeChange), this.FocusedCell.Column), true, false, true, false); } } if (nextCell != CellPos.Empty) { this.FocusedCell = nextCell; this.TableModel.Selections.SelectCell(this.FocusedCell); } } } else if (e.KeyData == Keys.Home || e.KeyData == Keys.End) { if (this.RowCount > 0) { CellPos nextCell; if (e.KeyData == Keys.Home) { nextCell = this.FindNextVisibleEnabledCell(CellPos.Empty, true, true, true, true); } else { nextCell = this.FindNextVisibleEnabledCell(new CellPos(this.RowCount-1, this.TableModel.Rows[this.RowCount-1].Cells.Count), true, false, true, true); } if (nextCell != CellPos.Empty) { this.FocusedCell = nextCell; this.TableModel.Selections.SelectCell(this.FocusedCell); } } } } else { // check if we can start editing with the custom edit key if (e.KeyData == this.CustomEditKey && this.EditStartAction == EditStartAction.CustomKey) { this.EditCell(this.FocusedCell); return; } // send all other key events to the cell's renderer // for further processing this.RaiseCellKeyDown(this.FocusedCell, e); } } else { if (this.FocusedCell == CellPos.Empty) { Keys key = e.KeyData & Keys.KeyCode; if (this.IsReservedKey(e.KeyData)) { if (key == Keys.Down || key == Keys.Right) { CellPos nextCell; if (key == Keys.Down) { nextCell = this.FindNextVisibleEnabledCell(this.FocusedCell, true, true, true, false); } else { nextCell = this.FindNextVisibleEnabledCell(this.FocusedCell, false, true, true, true); } if (nextCell != CellPos.Empty) { this.FocusedCell = nextCell; if ((e.KeyData & Keys.Modifiers) == Keys.Shift && this.MultiSelect) { this.TableModel.Selections.AddShiftSelectedCell(this.FocusedCell); } else { this.TableModel.Selections.SelectCell(this.FocusedCell); } } } } } } } #endregion #region KeyUp /// /// Raises the KeyUp event /// /// A KeyEventArgs that contains the event data protected override void OnKeyUp(KeyEventArgs e) { base.OnKeyUp(e); if (!this.IsReservedKey(e.KeyData)) { // if (e.KeyData == this.CustomEditKey && this.EditStartAction == EditStartAction.CustomKey) { return; } // send all other key events to the cell's renderer // for further processing this.RaiseCellKeyUp(this.FocusedCell, e); } } #endregion #endregion #region Layout /// /// Raises the Layout event /// /// A LayoutEventArgs that contains the event data protected override void OnLayout(LayoutEventArgs levent) { if (!this.IsHandleCreated || this.init) { return; } base.OnLayout(levent); this.UpdateScrollBars(); } #endregion #region Mouse #region MouseUp /// /// Raises the MouseUp event /// /// A MouseEventArgs that contains the event data protected override void OnMouseUp(MouseEventArgs e) { base.OnMouseUp(e); if (!this.CanRaiseEvents) { return; } // work out the current state of play this.CalcTableState(e.X, e.Y); TableRegion region = this.HitTest(e.X, e.Y); if (e.Button == MouseButtons.Left) { // if the left mouse button was down for a cell, // Raise a mouse up for that cell if (!this.LastMouseDownCell.IsEmpty) { if (this.IsValidCell(this.LastMouseDownCell)) { this.RaiseCellMouseUp(this.LastMouseDownCell, e); } // reset the lastMouseDownCell this.lastMouseDownCell = CellPos.Empty; } // if we have just finished resizing, it might // be a good idea to relayout the table if (this.resizingColumnIndex != -1) { if (this.resizingColumnWidth != -1) { this.DrawReversibleLine(this.ColumnRect(this.resizingColumnIndex).Left + this.resizingColumnWidth); } this.ColumnModel.Columns[this.resizingColumnIndex].Width = this.resizingColumnWidth; this.resizingColumnIndex = -1; this.resizingColumnWidth = -1; this.UpdateScrollBars(); this.Invalidate(this.PseudoClientRect, true); } // check if the mouse was released in a column header if (region == TableRegion.ColumnHeader) { int column = this.ColumnIndexAt(e.X, e.Y); // if we are in the header, check if we are in the pressed column if (this.pressedColumn != -1) { if (this.pressedColumn == column) { if (this.hotColumn != -1 && this.hotColumn != column) { this.ColumnModel.Columns[this.hotColumn].InternalColumnState = ColumnState.Normal; } this.ColumnModel.Columns[this.pressedColumn].InternalColumnState = ColumnState.Hot; this.RaiseHeaderMouseUp(column, e); } this.pressedColumn = -1; // only sort the column if we have rows to sort if (this.ColumnModel.Columns[column].Sortable) { if (this.TableModel != null && this.TableModel.Rows.Count > 0) { this.Sort(column); } } this.Invalidate(this.HeaderRectangle, false); } return; } // the mouse wasn't released in a column header, so if we // have a pressed column then we need to make it unpressed if (this.pressedColumn != -1) { this.pressedColumn = -1; this.Invalidate(this.HeaderRectangle, false); } } } #endregion #region MouseDown /// /// Raises the MouseDown event /// /// A MouseEventArgs that contains the event data protected override void OnMouseDown(MouseEventArgs e) { base.OnMouseDown(e); if (!this.CanRaiseEvents) { return; } this.CalcTableState(e.X, e.Y); TableRegion region = this.HitTest(e.X, e.Y); int row = this.RowIndexAt(e.X, e.Y); int column = this.ColumnIndexAt(e.X, e.Y); if (this.IsEditing) { if (this.EditingCell.Row != row || this.EditingCell.Column != column) { this.Focus(); if (region == TableRegion.ColumnHeader && e.Button != MouseButtons.Right) { return; } } } #region ColumnHeader if (region == TableRegion.ColumnHeader) { if (e.Button == MouseButtons.Right && this.HeaderContextMenu.Enabled) { this.HeaderContextMenu.Show(this, new Point(e.X, e.Y)); return; } if (column == -1 || !this.ColumnModel.Columns[column].Enabled) { return; } if (e.Button == MouseButtons.Left) { this.FocusedCell = new CellPos(-1, -1); // don't bother going any further if the user // double clicked if (e.Clicks > 1) { return; } this.RaiseHeaderMouseDown(column, e); if (this.TableState == TableState.ColumnResizing) { Rectangle columnRect = this.ColumnModel.ColumnHeaderRect(column); int x = this.ClientToDisplayRect(e.X, e.Y).X; if (x <= columnRect.Left + Column.ResizePadding) { //column--; column = this.ColumnModel.PreviousVisibleColumn(column); } this.resizingColumnIndex = column; if (this.resizingColumnIndex != -1) { this.resizingColumnAnchor = this.ColumnModel.ColumnHeaderRect(column).Left; this.resizingColumnOffset = x - (this.resizingColumnAnchor + this.ColumnModel.Columns[column].Width); } } else { if (this.HeaderStyle != ColumnHeaderStyle.Clickable || !this.ColumnModel.Columns[column].Sortable) { return; } if (column == -1) { return; } if (this.pressedColumn != -1) { this.ColumnModel.Columns[this.pressedColumn].InternalColumnState = ColumnState.Normal; } this.pressedColumn = column; this.ColumnModel.Columns[column].InternalColumnState = ColumnState.Pressed; } return; } } #endregion #region Cells if (region == TableRegion.Cells) { if (e.Button != MouseButtons.Left && e.Button != MouseButtons.Right) { return; } if (!this.IsValidCell(row, column) || !this.IsCellEnabled(row, column)) { // clear selections this.TableModel.Selections.Clear(); return; } this.FocusedCell = new CellPos(row, column); // don't bother going any further if the user // double clicked or we're not allowed to select if (e.Clicks > 1 || !this.AllowSelection) { return; } this.lastMouseDownCell.Row = row; this.lastMouseDownCell.Column = column; // this.RaiseCellMouseDown(new CellPos(row, column), e); if (!this.ColumnModel.Columns[column].Selectable) { return; } // if ((ModifierKeys & Keys.Shift) == Keys.Shift && this.MultiSelect) { if (e.Button == MouseButtons.Right) { return; } this.TableModel.Selections.AddShiftSelectedCell(row, column); return; } if ((ModifierKeys & Keys.Control) == Keys.Control && this.MultiSelect) { if (e.Button == MouseButtons.Right) { return; } if (this.TableModel.Selections.IsCellSelected(row, column)) { this.TableModel.Selections.RemoveCell(row, column); } else { this.TableModel.Selections.AddCell(row, column); } return; } this.TableModel.Selections.SelectCell(row, column); } #endregion } #endregion #region MouseMove /// /// Raises the MouseMove event /// /// A MouseEventArgs that contains the event data protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); // don't go any further if the table is editing if (this.TableState == TableState.Editing) { return; } // if the left mouse button is down, check if the LastMouseDownCell // references a valid cell. if it does, send the mouse move message // to the cell and then exit (this will stop other cells/headers // from getting the mouse move message even if the mouse is over // them - this seems consistent with the way windows does it for // other controls) if (e.Button == MouseButtons.Left) { if (!this.LastMouseDownCell.IsEmpty) { if (this.IsValidCell(this.LastMouseDownCell)) { this.RaiseCellMouseMove(this.LastMouseDownCell, e); return; } } } // are we resizing a column? if (this.resizingColumnIndex != -1) { if (this.resizingColumnWidth != -1) { this.DrawReversibleLine(this.ColumnRect(this.resizingColumnIndex).Left + this.resizingColumnWidth); } // calculate the new width for the column int width = this.ClientToDisplayRect(e.X, e.Y).X - this.resizingColumnAnchor - this.resizingColumnOffset; // make sure the new width isn't smaller than the minimum allowed // column width, or larger than the maximum allowed column width if (width < Column.MinimumWidth) { width = Column.MinimumWidth; } else if (width > Column.MaximumWidth) { width = Column.MaximumWidth; } this.resizingColumnWidth = width; //this.ColumnModel.Columns[this.resizingColumnIndex].Width = width; this.DrawReversibleLine(this.ColumnRect(this.resizingColumnIndex).Left + this.resizingColumnWidth); return; } // work out the potential state of play this.CalcTableState(e.X, e.Y); TableRegion hitTest = this.HitTest(e.X, e.Y); #region ColumnHeader if (hitTest == TableRegion.ColumnHeader) { // this next bit is pretty complicated. need to work // out which column is displayed as pressed or hot // (so we have the same behaviour as a themed ListView // in Windows XP) int column = this.ColumnIndexAt(e.X, e.Y); // if this isn't the current hot column, reset the // hot columns state to normal and set this column // to be the hot column if (this.hotColumn != column) { if (this.hotColumn != -1) { this.ColumnModel.Columns[this.hotColumn].InternalColumnState = ColumnState.Normal; this.RaiseHeaderMouseLeave(this.hotColumn); } if (this.TableState != TableState.ColumnResizing) { this.hotColumn = column; if (this.hotColumn != -1 && this.ColumnModel.Columns[column].Enabled) { this.ColumnModel.Columns[column].InternalColumnState = ColumnState.Hot; this.RaiseHeaderMouseEnter(column); } } } else { if (column != -1 && this.ColumnModel.Columns[column].Enabled) { this.RaiseHeaderMouseMove(column, e); } } // if this isn't the pressed column, then the pressed columns // state should be set back to normal if (this.pressedColumn != -1 && this.pressedColumn != column) { this.ColumnModel.Columns[this.pressedColumn].InternalColumnState = ColumnState.Normal; } // else if this is the pressed column and its state is not // pressed, then we had better set it else if (column != -1 && this.pressedColumn == column && this.ColumnModel.Columns[this.pressedColumn].ColumnState != ColumnState.Pressed) { this.ColumnModel.Columns[this.pressedColumn].InternalColumnState = ColumnState.Pressed; } // set the cursor to a resizing cursor if necesary if (this.TableState == TableState.ColumnResizing) { Rectangle columnRect = this.ColumnModel.ColumnHeaderRect(column); int x = this.ClientToDisplayRect(e.X, e.Y).X; this.Cursor = Cursors.VSplit; // if the left mouse button is down, we don't want // the resizing cursor so set it back to the default if (e.Button == MouseButtons.Left) { this.Cursor = Cursors.Default; } // if the mouse is in the left side of the column, // the first non-hidden column to the left needs to // become the hot column (so the user knows which // column would be resized if a resize action were // to take place if (x < columnRect.Left + Column.ResizePadding) { int col = column; while (col != 0) { col--; if (this.ColumnModel.Columns[col].Visible) { break; } } if (col != -1) { if (this.ColumnModel.Columns[col].Enabled) { if (this.hotColumn != -1) { this.ColumnModel.Columns[this.hotColumn].InternalColumnState = ColumnState.Normal; } this.hotColumn = col; this.ColumnModel.Columns[this.hotColumn].InternalColumnState = ColumnState.Hot; this.RaiseHeaderMouseEnter(col); } else { this.Cursor = Cursors.Default; } } } else { if (this.ColumnModel.Columns[column].Enabled) { // this mouse is in the right side of the column, // so this column needs to be dsiplayed hot this.hotColumn = column; this.ColumnModel.Columns[this.hotColumn].InternalColumnState = ColumnState.Hot; } else { this.Cursor = Cursors.Default; } } } else { // we're not in a resizing area, so make sure the cursor // is the default cursor (we may have just come from a // resizing area) this.Cursor = Cursors.Default; } // reset the last cell the mouse was over this.ResetLastMouseCell(); return; } #endregion // we're outside of the header, so if there is a hot column, // it need to be reset if (this.hotColumn != -1) { this.ColumnModel.Columns[this.hotColumn].InternalColumnState = ColumnState.Normal; this.ResetHotColumn(); } // if there is a pressed column, its state need to beset to normal if (this.pressedColumn != -1) { this.ColumnModel.Columns[this.pressedColumn].InternalColumnState = ColumnState.Normal; } #region Cells if (hitTest == TableRegion.Cells) { // find the cell the mouse is over CellPos cellPos = new CellPos(this.RowIndexAt(e.X, e.Y), this.ColumnIndexAt(e.X, e.Y)); if (!cellPos.IsEmpty) { if (cellPos != this.lastMouseCell) { // check if the cell exists (ie is not null) if (this.IsValidCell(cellPos)) { CellPos oldLastMouseCell = this.lastMouseCell; if (!oldLastMouseCell.IsEmpty) { this.ResetLastMouseCell(); } this.lastMouseCell = cellPos; this.RaiseCellMouseEnter(cellPos); } else { this.ResetLastMouseCell(); // make sure the cursor is the default cursor // (we may have just come from a resizing area in the header) this.Cursor = Cursors.Default; } } else { this.RaiseCellMouseMove(cellPos, e); } } else { this.ResetLastMouseCell(); if (this.TableModel == null) { this.ResetToolTip(); } } return; } else { this.ResetLastMouseCell(); if (!this.lastMouseDownCell.IsEmpty) { this.RaiseCellMouseLeave(this.lastMouseDownCell); } if (this.TableModel == null) { this.ResetToolTip(); } // make sure the cursor is the default cursor // (we may have just come from a resizing area in the header) this.Cursor = Cursors.Default; } #endregion } #endregion #region MouseLeave /// /// Raises the MouseLeave event /// /// An EventArgs that contains the event data protected override void OnMouseLeave(EventArgs e) { base.OnMouseLeave(e); // we're outside of the header, so if there is a hot column, // it needs to be reset (this shouldn't happen, but better // safe than sorry ;) if (this.hotColumn != -1) { this.ColumnModel.Columns[this.hotColumn].InternalColumnState = ColumnState.Normal; this.ResetHotColumn(); } } #endregion #region MouseWheel /// /// Raises the MouseWheel event /// /// A MouseEventArgs that contains the event data protected override void OnMouseWheel(MouseEventArgs e) { base.OnMouseWheel(e); if (!this.Scrollable || (!this.HScroll && !this.VScroll)) { return; } if (this.VScroll) { int newVal = this.vScrollBar.Value - ((e.Delta / 120) * SystemInformation.MouseWheelScrollLines); if (newVal < 0) { newVal = 0; } else if (newVal > this.vScrollBar.Maximum - this.vScrollBar.LargeChange + 1) { newVal = this.vScrollBar.Maximum - this.vScrollBar.LargeChange + 1; } this.VerticalScroll(newVal); this.vScrollBar.Value = newVal; } else if (this.HScroll) { int newVal = this.hScrollBar.Value - ((e.Delta / 120) * Column.MinimumWidth); if (newVal < 0) { newVal = 0; } else if (newVal > this.hScrollBar.Maximum - this.hScrollBar.LargeChange) { newVal = this.hScrollBar.Maximum - this.hScrollBar.LargeChange; } this.HorizontalScroll(newVal); this.hScrollBar.Value = newVal; } } #endregion #region MouseHover /// /// Raises the MouseHover event /// /// An EventArgs that contains the event data protected override void OnMouseHover(EventArgs e) { base.OnMouseHover(e); if (this.IsValidCell(this.LastMouseCell)) { this.OnCellMouseHover(new CellMouseEventArgs(this.TableModel[this.LastMouseCell], this, this.LastMouseCell, this.CellRect(this.LastMouseCell))); } else if (this.hotColumn != -1) { this.OnHeaderMouseHover(new HeaderMouseEventArgs(this.ColumnModel.Columns[this.hotColumn], this, this.hotColumn, this.DisplayRectToClient(this.ColumnModel.ColumnHeaderRect(this.hotColumn)))); } } #endregion #region Click /// /// Raises the Click event /// /// An EventArgs that contains the event data protected override void OnClick(EventArgs e) { base.OnClick(e); if (this.IsValidCell(this.LastMouseCell)) { this.OnCellClick(new CellMouseEventArgs(this.TableModel[this.LastMouseCell], this, this.LastMouseCell, this.CellRect(this.LastMouseCell))); } else if (this.hotColumn != -1) { this.OnHeaderClick(new HeaderMouseEventArgs(this.ColumnModel.Columns[this.hotColumn], this, this.hotColumn, this.DisplayRectToClient(this.ColumnModel.ColumnHeaderRect(this.hotColumn)))); } } /// /// Raises the DoubleClick event /// /// An EventArgs that contains the event data protected override void OnDoubleClick(EventArgs e) { base.OnDoubleClick(e); if (this.IsValidCell(this.LastMouseCell)) { Rectangle cellRect = this.CellRect(this.LastMouseCell); this.OnCellDoubleClick(new CellMouseEventArgs(this.TableModel[this.LastMouseCell], this, this.LastMouseCell, this.CellRect(this.LastMouseCell))); } else if (this.hotColumn != -1) { this.OnHeaderDoubleClick(new HeaderMouseEventArgs(this.ColumnModel.Columns[this.hotColumn], this, this.hotColumn, this.DisplayRectToClient(this.ColumnModel.ColumnHeaderRect(this.hotColumn)))); } } #endregion #endregion #region Paint /// /// Raises the PaintBackground event /// /// A PaintEventArgs that contains the event data protected override void OnPaintBackground(PaintEventArgs e) { base.OnPaintBackground(e); } /// /// Raises the Paint event /// /// A PaintEventArgs that contains the event data protected override void OnPaint(PaintEventArgs e) { // we'll do our own painting thanks //base.OnPaint(e); // check if we actually need to paint if (this.Width == 0 || this.Height == 0) { return; } if (this.ColumnModel != null) { // keep a record of the current clip region Region clip = e.Graphics.Clip; if (this.TableModel != null && this.TableModel.Rows.Count > 0) { this.OnPaintRows(e); // reset the clipping region e.Graphics.Clip = clip; } if (this.GridLines != GridLines.None) { this.OnPaintGrid(e); } if (this.HeaderStyle != ColumnHeaderStyle.None && this.ColumnModel.Columns.Count > 0) { if (this.HeaderRectangle.IntersectsWith(e.ClipRectangle)) { this.OnPaintHeader(e); } } // reset the clipping region e.Graphics.Clip = clip; } this.OnPaintEmptyTableText(e); this.OnPaintBorder(e); } /// /// Draws a reversible line at the specified screen x-coordinate /// that is the height of the PseudoClientRect /// /// The screen x-coordinate of the reversible line /// to be drawn private void DrawReversibleLine(int x) { Point start = this.PointToScreen(new Point(x, this.PseudoClientRect.Top)); ControlPaint.DrawReversibleLine(start, new Point(start.X, start.Y + this.PseudoClientRect.Height), this.BackColor); } #region Border /// /// Paints the Table's border /// /// A PaintEventArgs that contains the event data protected void OnPaintBorder(PaintEventArgs e) { //e.Graphics.SetClip(e.ClipRectangle); if (this.BorderStyle == BorderStyle.Fixed3D) { if (ThemeManager.VisualStylesEnabled) { TextBoxStates state = TextBoxStates.Normal; if (!this.Enabled) { state = TextBoxStates.Disabled; } // draw the left border Rectangle clipRect = new Rectangle(0, 0, SystemInformation.Border3DSize.Width, this.Height); if (clipRect.IntersectsWith(e.ClipRectangle)) { ThemeManager.DrawTextBox(e.Graphics, this.ClientRectangle, clipRect, state); } // draw the top border clipRect = new Rectangle(0, 0, this.Width, SystemInformation.Border3DSize.Height); if (clipRect.IntersectsWith(e.ClipRectangle)) { ThemeManager.DrawTextBox(e.Graphics, this.ClientRectangle, clipRect, state); } // draw the right border clipRect = new Rectangle(this.Width-SystemInformation.Border3DSize.Width, 0, this.Width, this.Height); if (clipRect.IntersectsWith(e.ClipRectangle)) { ThemeManager.DrawTextBox(e.Graphics, this.ClientRectangle, clipRect, state); } // draw the bottom border clipRect = new Rectangle(0, this.Height-SystemInformation.Border3DSize.Height, this.Width, SystemInformation.Border3DSize.Height); if (clipRect.IntersectsWith(e.ClipRectangle)) { ThemeManager.DrawTextBox(e.Graphics, this.ClientRectangle, clipRect, state); } } else { ControlPaint.DrawBorder3D(e.Graphics, 0, 0, this.Width, this.Height, Border3DStyle.Sunken); } } else if (this.BorderStyle == BorderStyle.FixedSingle) { e.Graphics.DrawRectangle(Pens.Black, 0, 0, this.Width-1, this.Height-1); } if (this.HScroll && this.VScroll) { Rectangle rect = new Rectangle(this.Width - this.BorderWidth - SystemInformation.VerticalScrollBarWidth, this.Height - this.BorderWidth - SystemInformation.HorizontalScrollBarHeight, SystemInformation.VerticalScrollBarWidth, SystemInformation.HorizontalScrollBarHeight); if (rect.IntersectsWith(e.ClipRectangle)) { e.Graphics.FillRectangle(SystemBrushes.Control, rect); } } } #endregion #region Cells /// /// Paints the Cell at the specified row and column indexes /// /// A PaintEventArgs that contains the event data /// The index of the row that contains the cell to be painted /// The index of the column that contains the cell to be painted /// The bounding Rectangle of the Cell protected void OnPaintCell(PaintEventArgs e, int row, int column, Rectangle cellRect) { if (row == 0 && column == 1) { column = 1; } // get the renderer for the cells column ICellRenderer renderer = this.ColumnModel.Columns[column].Renderer; if (renderer == null) { // get the default renderer for the column renderer = this.ColumnModel.GetCellRenderer(this.ColumnModel.Columns[column].GetDefaultRendererName()); } // if the renderer is still null (which it shouldn't) // the get out of here if (renderer == null) { return; } PaintCellEventArgs pcea = new PaintCellEventArgs(e.Graphics, cellRect); pcea.Graphics.SetClip(Rectangle.Intersect(e.ClipRectangle, cellRect)); if (column < this.TableModel.Rows[row].Cells.Count) { // is the cell selected bool selected = false; if (this.FullRowSelect) { selected = this.TableModel.Selections.IsRowSelected(row); } else { if (this.SelectionStyle == SelectionStyle.ListView) { if (this.TableModel.Selections.IsRowSelected(row) && this.ColumnModel.PreviousVisibleColumn(column) == -1) { selected = true; } } else if (this.SelectionStyle == SelectionStyle.Grid) { if (this.TableModel.Selections.IsCellSelected(row, column)) { selected = true; } } } // bool editable = this.TableModel[row, column].Editable && this.TableModel.Rows[row].Editable && this.ColumnModel.Columns[column].Editable; bool enabled = this.TableModel[row, column].Enabled && this.TableModel.Rows[row].Enabled && this.ColumnModel.Columns[column].Enabled; // draw the cell pcea.SetCell(this.TableModel[row, column]); pcea.SetRow(row); pcea.SetColumn(column); pcea.SetTable(this); pcea.SetSelected(selected); pcea.SetFocused(this.Focused && this.FocusedCell.Row == row && this.FocusedCell.Column == column); pcea.SetSorted(column == this.lastSortedColumn); pcea.SetEditable(editable); pcea.SetEnabled(enabled); pcea.SetCellRect(cellRect); } else { // there isn't a cell for this column, so send a // null value for the cell and the renderer will // take care of the rest (it should draw an empty cell) pcea.SetCell(null); pcea.SetRow(row); pcea.SetColumn(column); pcea.SetTable(this); pcea.SetSelected(false); pcea.SetFocused(false); pcea.SetSorted(false); pcea.SetEditable(false); pcea.SetEnabled(false); pcea.SetCellRect(cellRect); } // let the user get the first crack at painting the cell this.OnBeforePaintCell(pcea); // only send to the renderer if the user hasn't // set the handled property if (!pcea.Handled) { renderer.OnPaintCell(pcea); } // let the user have another go this.OnAfterPaintCell(pcea); } /// /// Raises the BeforePaintCell event /// /// A PaintCellEventArgs that contains the event data protected virtual void OnBeforePaintCell(PaintCellEventArgs e) { if (BeforePaintCell != null) { BeforePaintCell(this, e); } } /// /// Raises the AfterPaintCell event /// /// A PaintCellEventArgs that contains the event data protected virtual void OnAfterPaintCell(PaintCellEventArgs e) { if (AfterPaintCell != null) { AfterPaintCell(this, e); } } #endregion #region Grid /// /// Paints the Table's grid /// /// A PaintEventArgs that contains the event data protected void OnPaintGrid(PaintEventArgs e) { if (this.GridLines == GridLines.None) { return; } // //e.Graphics.SetClip(e.ClipRectangle); if (this.ColumnModel == null || this.ColumnModel.Columns.Count == 0) { return; } //e.Graphics.SetClip(e.ClipRectangle); if (this.ColumnModel != null) { using (Pen gridPen = new Pen(this.GridColor)) { // gridPen.DashStyle = (DashStyle) this.GridLineStyle; // check if we can draw column lines if ((this.GridLines & GridLines.Columns) == GridLines.Columns) { int right = this.DisplayRectangle.X; for (int i=0; i= e.ClipRectangle.Left && right <= e.ClipRectangle.Right) { e.Graphics.DrawLine(gridPen, right-1, e.ClipRectangle.Top, right-1, e.ClipRectangle.Bottom); } } } } if (this.TableModel != null) { // check if we can draw row lines if ((this.GridLines & GridLines.Rows) == GridLines.Rows) { int y = this.CellDataRect.Y + this.RowHeight - 1; for (int i=y; i<=e.ClipRectangle.Bottom; i+=this.RowHeight) { if (i >= this.CellDataRect.Top) { e.Graphics.DrawLine(gridPen, e.ClipRectangle.Left, i, e.ClipRectangle.Right, i); } } } } } } } #endregion #region Header /// /// Paints the Table's Column headers /// /// A PaintEventArgs that contains the event data protected void OnPaintHeader(PaintEventArgs e) { // only bother if we actually get to paint something if (!this.HeaderRectangle.IntersectsWith(e.ClipRectangle)) { return; } int xPos = this.DisplayRectangle.Left; bool needDummyHeader = true; // PaintHeaderEventArgs phea = new PaintHeaderEventArgs(e.Graphics, e.ClipRectangle); for (int i=0; i