/*
* 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>
/// Summary description for Table.
/// </summary>
[DesignTimeVisible(true),
ToolboxItem(true),
ToolboxBitmap(typeof(Table))]
public class Table : Control, ISupportInitialize
{
#region Event Handlers
#region Cells
/// <summary>
/// Occurs when the value of a Cells property changes
/// </summary>
public event CellEventHandler CellPropertyChanged;
#region Focus
/// <summary>
/// Occurs when a Cell gains focus
/// </summary>
public event CellFocusEventHandler CellGotFocus;
/// <summary>
/// Occurs when a Cell loses focus
/// </summary>
public event CellFocusEventHandler CellLostFocus;
#endregion
#region Keys
/// <summary>
/// Occurs when a key is pressed when a Cell has focus
/// </summary>
public event CellKeyEventHandler CellKeyDown;
/// <summary>
/// Occurs when a key is released when a Cell has focus
/// </summary>
public event CellKeyEventHandler CellKeyUp;
#endregion
#region Mouse
/// <summary>
/// Occurs when the mouse pointer enters a Cell
/// </summary>
public event CellMouseEventHandler CellMouseEnter;
/// <summary>
/// Occurs when the mouse pointer leaves a Cell
/// </summary>
public event CellMouseEventHandler CellMouseLeave;
/// <summary>
/// Occurs when a mouse pointer is over a Cell and a mouse button is pressed
/// </summary>
public event CellMouseEventHandler CellMouseDown;
/// <summary>
/// Occurs when a mouse pointer is over a Cell and a mouse button is released
/// </summary>
public event CellMouseEventHandler CellMouseUp;
/// <summary>
/// Occurs when a mouse pointer is moved over a Cell
/// </summary>
public event CellMouseEventHandler CellMouseMove;
/// <summary>
/// Occurs when the mouse pointer hovers over a Cell
/// </summary>
public event CellMouseEventHandler CellMouseHover;
/// <summary>
/// Occurs when a Cell is clicked
/// </summary>
public event CellMouseEventHandler CellClick;
/// <summary>
/// Occurs when a Cell is double-clicked
/// </summary>
public event CellMouseEventHandler CellDoubleClick;
#endregion
#region Buttons
/// <summary>
/// Occurs when a Cell's button is clicked
/// </summary>
public event CellButtonEventHandler CellButtonClicked;
#endregion
#region CheckBox
/// <summary>
/// Occurs when a Cell's Checked value changes
/// </summary>
public event CellCheckBoxEventHandler CellCheckChanged;
#endregion
#endregion
#region Column
/// <summary>
/// Occurs when a Column's property changes
/// </summary>
public event ColumnEventHandler ColumnPropertyChanged;
#endregion
#region Column Headers
/// <summary>
/// Occurs when the mouse pointer enters a Column Header
/// </summary>
public event HeaderMouseEventHandler HeaderMouseEnter;
/// <summary>
/// Occurs when the mouse pointer leaves a Column Header
/// </summary>
public event HeaderMouseEventHandler HeaderMouseLeave;
/// <summary>
/// Occurs when a mouse pointer is over a Column Header and a mouse button is pressed
/// </summary>
public event HeaderMouseEventHandler HeaderMouseDown;
/// <summary>
/// Occurs when a mouse pointer is over a Column Header and a mouse button is released
/// </summary>
public event HeaderMouseEventHandler HeaderMouseUp;
/// <summary>
/// Occurs when a mouse pointer is moved over a Column Header
/// </summary>
public event HeaderMouseEventHandler HeaderMouseMove;
/// <summary>
/// Occurs when the mouse pointer hovers over a Column Header
/// </summary>
public event HeaderMouseEventHandler HeaderMouseHover;
/// <summary>
/// Occurs when a Column Header is clicked
/// </summary>
public event HeaderMouseEventHandler HeaderClick;
/// <summary>
/// Occurs when a Column Header is double-clicked
/// </summary>
public event HeaderMouseEventHandler HeaderDoubleClick;
/// <summary>
/// Occurs when the height of the Column Headers changes
/// </summary>
public event EventHandler HeaderHeightChanged;
#endregion
#region ColumnModel
/// <summary>
/// Occurs when the value of the Table's ColumnModel property changes
/// </summary>
public event EventHandler ColumnModelChanged;
/// <summary>
/// Occurs when a Column is added to the ColumnModel
/// </summary>
public event ColumnModelEventHandler ColumnAdded;
/// <summary>
/// Occurs when a Column is removed from the ColumnModel
/// </summary>
public event ColumnModelEventHandler ColumnRemoved;
#endregion
#region Editing
/// <summary>
/// Occurs when the Table begins editing a Cell
/// </summary>
public event CellEditEventHandler BeginEditing;
/// <summary>
/// Occurs when the Table stops editing a Cell
/// </summary>
public event CellEditEventHandler EditingStopped;
/// <summary>
/// Occurs when the editing of a Cell is cancelled
/// </summary>
public event CellEditEventHandler EditingCancelled;
#endregion
#region Rows
/// <summary>
/// Occurs when a Cell is added to a Row
/// </summary>
public event RowEventHandler CellAdded;
/// <summary>
/// Occurs when a Cell is removed from a Row
/// </summary>
public event RowEventHandler CellRemoved;
/// <summary>
/// Occurs when the value of a Rows property changes
/// </summary>
public event RowEventHandler RowPropertyChanged;
#endregion
#region Sorting
/// <summary>
/// Occurs when a Column is about to be sorted
/// </summary>
public event ColumnEventHandler BeginSort;
/// <summary>
/// Occurs after a Column has finished sorting
/// </summary>
public event ColumnEventHandler EndSort;
#endregion
#region Painting
/// <summary>
/// Occurs before a Cell is painted
/// </summary>
public event PaintCellEventHandler BeforePaintCell;
/// <summary>
/// Occurs after a Cell is painted
/// </summary>
public event PaintCellEventHandler AfterPaintCell;
/// <summary>
/// Occurs before a Column header is painted
/// </summary>
public event PaintHeaderEventHandler BeforePaintHeader;
/// <summary>
/// Occurs after a Column header is painted
/// </summary>
public event PaintHeaderEventHandler AfterPaintHeader;
#endregion
#region TableModel
/// <summary>
/// Occurs when the value of the Table's TableModel property changes
/// </summary>
public event EventHandler TableModelChanged;
/// <summary>
/// Occurs when a Row is added into the TableModel
/// </summary>
public event TableModelEventHandler RowAdded;
/// <summary>
/// Occurs when a Row is removed from the TableModel
/// </summary>
public event TableModelEventHandler RowRemoved;
/// <summary>
/// Occurs when the value of the TableModel Selection property changes
/// </summary>
public event SelectionEventHandler SelectionChanged;
/// <summary>
/// Occurs when the value of the RowHeight property changes
/// </summary>
public event EventHandler RowHeightChanged;
#endregion
#endregion
#region Class Data
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;
#region Border
/// <summary>
/// The style of the Table's border
/// </summary>
private BorderStyle borderStyle;
#endregion
#region Cells
/// <summary>
/// The last known cell position that the mouse was over
/// </summary>
private CellPos lastMouseCell;
/// <summary>
/// The last known cell position that the mouse's left
/// button was pressed in
/// </summary>
private CellPos lastMouseDownCell;
/// <summary>
/// The position of the Cell that currently has focus
/// </summary>
private CellPos focusedCell;
/// <summary>
/// The Cell that is currently being edited
/// </summary>
private CellPos editingCell;
/// <summary>
/// The ICellEditor that is currently being used to edit a Cell
/// </summary>
private ICellEditor curentCellEditor;
/// <summary>
/// The action that must be performed on a Cell to start editing
/// </summary>
private EditStartAction editStartAction;
/// <summary>
/// The key that must be pressed for editing to start when
/// editStartAction is set to EditStartAction.CustomKey
/// </summary>
private Keys customEditKey;
/// <summary>
/// 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
/// </summary>
private int hoverTime;
/// <summary>
/// A TRACKMOUSEEVENT used to set the hoverTime
/// </summary>
private TRACKMOUSEEVENT trackMouseEvent;
#endregion
#region Columns
/// <summary>
/// The ColumnModel of the Table
/// </summary>
private ColumnModel columnModel;
/// <summary>
/// Whether the Table supports column resizing
/// </summary>
private bool columnResizing;
/// <summary>
/// The index of the column currently being resized
/// </summary>
private int resizingColumnIndex;
/// <summary>
/// The x coordinate of the currently resizing column
/// </summary>
private int resizingColumnAnchor;
/// <summary>
/// The horizontal distance between the resize starting
/// point and the right edge of the resizing column
/// </summary>
private int resizingColumnOffset;
/// <summary>
/// The width that the resizing column will be set to
/// once column resizing is finished
/// </summary>
private int resizingColumnWidth;
/// <summary>
/// The index of the current pressed column
/// </summary>
private int pressedColumn;
/// <summary>
/// The index of the current "hot" column
/// </summary>
private int hotColumn;
/// <summary>
/// The index of the last sorted column
/// </summary>
private int lastSortedColumn;
/// <summary>
/// The Color of a sorted Column's background
/// </summary>
private Color sortedColumnBackColor;
#endregion
#region Grid
/// <summary>
/// Indicates whether grid lines appear between the rows and columns
/// containing the rows and cells in the Table
/// </summary>
private GridLines gridLines;
/// <summary>
/// The color of the grid lines
/// </summary>
private Color gridColor;
/// <summary>
/// The line style of the grid lines
/// </summary>
private GridLineStyle gridLineStyle;
#endregion
#region Header
/// <summary>
/// The styles of the column headers
/// </summary>
private ColumnHeaderStyle headerStyle;
/// <summary>
/// The Renderer used to paint the column headers
/// </summary>
private HeaderRenderer headerRenderer;
/// <summary>
/// The font used to draw the text in the column header
/// </summary>
private Font headerFont;
/// <summary>
/// The ContextMenu for the column headers
/// </summary>
private HeaderContextMenu headerContextMenu;
#endregion
#region Items
/// <summary>
/// The TableModel of the Table
/// </summary>
private TableModel tableModel;
#endregion
#region Scrollbars
/// <summary>
/// Indicates whether the Table will allow the user to scroll to any
/// columns or rows placed outside of its visible boundaries
/// </summary>
private bool scrollable;
/// <summary>
/// The Table's horizontal ScrollBar
/// </summary>
private HScrollBar hScrollBar;
/// <summary>
/// The Table's vertical ScrollBar
/// </summary>
private VScrollBar vScrollBar;
#endregion
#region Selection
/// <summary>
/// Specifies whether rows and cells can be selected
/// </summary>
private bool allowSelection;
/// <summary>
/// Specifies whether multiple rows and cells can be selected
/// </summary>
private bool multiSelect;
/// <summary>
/// Specifies whether clicking a row selects all its cells
/// </summary>
private bool fullRowSelect;
/// <summary>
/// Specifies whether the selected rows and cells in the Table remain
/// highlighted when the Table loses focus
/// </summary>
private bool hideSelection;
/// <summary>
/// The background color of selected rows and cells
/// </summary>
private Color selectionBackColor;
/// <summary>
/// The foreground color of selected rows and cells
/// </summary>
private Color selectionForeColor;
/// <summary>
/// The background color of selected rows and cells when the Table
/// doesn't have focus
/// </summary>
private Color unfocusedSelectionBackColor;
/// <summary>
/// The foreground color of selected rows and cells when the Table
/// doesn't have focus
/// </summary>
private Color unfocusedSelectionForeColor;
/// <summary>
/// Determines how selected Cells are hilighted
/// </summary>
private SelectionStyle selectionStyle;
#endregion
#region Table
/// <summary>
/// The state of the table
/// </summary>
private TableState tableState;
/// <summary>
/// Is the Table currently initialising
/// </summary>
private bool init;
/// <summary>
/// The number of times BeginUpdate has been called
/// </summary>
private int beginUpdateCount;
/// <summary>
/// The ToolTip used by the Table to display cell and column tooltips
/// </summary>
private ToolTip toolTip;
/// <summary>
/// The alternating row background color
/// </summary>
private Color alternatingRowColor;
/// <summary>
/// The text displayed in the Table when it has no data to display
/// </summary>
private string noItemsText;
/// <summary>
/// Specifies whether the Table is being used as a preview Table
/// in a ColumnColection editor
/// </summary>
private bool preview;
/*/// <summary>
/// Specifies whether pressing the Tab key while editing moves the
/// editor to the next available cell
/// </summary>
private bool tabMovesEditor;*/
#endregion
#endregion
#region Constructor
/// <summary>
/// Initializes a new instance of the Table class with default settings
/// </summary>
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
/// <summary>
/// Computes the location of the specified client point into coordinates
/// relative to the display rectangle
/// </summary>
/// <param name="x">The client x coordinate to convert</param>
/// <param name="y">The client y coordinate to convert</param>
/// <returns>A Point that represents the converted coordinates (x, y),
/// relative to the display rectangle</returns>
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);
}
/// <summary>
/// Computes the location of the specified client point into coordinates
/// relative to the display rectangle
/// </summary>
/// <param name="p">The client coordinate Point to convert</param>
/// <returns>A Point that represents the converted Point, p,
/// relative to the display rectangle</returns>
public Point ClientToDisplayRect(Point p)
{
return this.ClientToDisplayRect(p.X, p.Y);
}
/// <summary>
/// Converts the location of the specified Rectangle into coordinates
/// relative to the display rectangle
/// </summary>
/// <param name="rect">The Rectangle to convert whose location is in
/// client coordinates</param>
/// <returns>A Rectangle that represents the converted Rectangle, rect,
/// relative to the display rectangle</returns>
public Rectangle ClientToDisplayRect(Rectangle rect)
{
return new Rectangle(this.ClientToDisplayRect(rect.Location), rect.Size);
}
#endregion
#region DisplayRectToClient
/// <summary>
/// Computes the location of the specified point relative to the display
/// rectangle point into client coordinates
/// </summary>
/// <param name="x">The x coordinate to convert relative to the display rectangle</param>
/// <param name="y">The y coordinate to convert relative to the display rectangle</param>
/// <returns>A Point that represents the converted coordinates (x, y) relative to
/// the display rectangle in client coordinates</returns>
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);
}
/// <summary>
/// Computes the location of the specified point relative to the display
/// rectangle into client coordinates
/// </summary>
/// <param name="p">The point relative to the display rectangle to convert</param>
/// <returns>A Point that represents the converted Point relative to
/// the display rectangle, p, in client coordinates</returns>
public Point DisplayRectToClient(Point p)
{
return this.DisplayRectToClient(p.X, p.Y);
}
/// <summary>
/// Converts the location of the specified Rectangle relative to the display
/// rectangle into client coordinates
/// </summary>
/// <param name="rect">The Rectangle to convert whose location is relative to
/// the display rectangle</param>
/// <returns>A Rectangle that represents the converted Rectangle relative to
/// the display rectangle, rect, in client coordinates</returns>
public Rectangle DisplayRectToClient(Rectangle rect)
{
return new Rectangle(this.DisplayRectToClient(rect.Location), rect.Size);
}
#endregion
#region Cells
/// <summary>
/// Returns the Cell at the specified client coordinates
/// </summary>
/// <param name="x">The client x coordinate of the Cell</param>
/// <param name="y">The client y coordinate of the Cell</param>
/// <returns>The Cell at the specified client coordinates, or
/// null if it does not exist</returns>
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];
}
/// <summary>
/// Returns the Cell at the specified client Point
/// </summary>
/// <param name="p">The point of interest</param>
/// <returns>The Cell at the specified client Point,
/// or null if not found</returns>
public Cell CellAt(Point p)
{
return this.CellAt(p.X, p.Y);
}
/// <summary>
/// Returns a Rectangle that specifies the size and location the cell at
/// the specified row and column indexes in client coordinates
/// </summary>
/// <param name="row">The index of the row that contains the cell</param>
/// <param name="column">The index of the column that contains the cell</param>
/// <returns>A Rectangle that specifies the size and location the cell at
/// the specified row and column indexes in client coordinates</returns>
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);
}
/// <summary>
/// Returns a Rectangle that specifies the size and location the cell at
/// the specified cell position in client coordinates
/// </summary>
/// <param name="cellPos">The position of the cell</param>
/// <returns>A Rectangle that specifies the size and location the cell at
/// the specified cell position in client coordinates</returns>
public Rectangle CellRect(CellPos cellPos)
{
return this.CellRect(cellPos.Row, cellPos.Column);
}
/// <summary>
/// Returns a Rectangle that specifies the size and location of the
/// specified cell in client coordinates
/// </summary>
/// <param name="cell">The cell whose bounding rectangle is to be retrieved</param>
/// <returns>A Rectangle that specifies the size and location the specified
/// cell in client coordinates</returns>
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);
}
/// <summary>
/// Returns whether Cell at the specified row and column indexes
/// is not null
/// </summary>
/// <param name="row">The row index of the cell</param>
/// <param name="column">The column index of the cell</param>
/// <returns>True if the cell at the specified row and column indexes
/// is not null, otherwise false</returns>
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;
}
/// <summary>
/// Returns whether Cell at the specified cell position is not null
/// </summary>
/// <param name="cellPos">The position of the cell</param>
/// <returns>True if the cell at the specified cell position is not
/// null, otherwise false</returns>
protected internal bool IsValidCell(CellPos cellPos)
{
return this.IsValidCell(cellPos.Row, cellPos.Column);
}
/// <summary>
/// Returns a CellPos that specifies the next Cell that is visible
/// and enabled from the specified Cell
/// </summary>
/// <param name="start">A CellPos that specifies the Cell to start
/// searching from</param>
/// <param name="wrap">Specifies whether to move to the start of the
/// next Row when the end of the current Row is reached</param>
/// <param name="forward">Specifies whether the search should travel
/// in a forward direction (top to bottom, left to right) through the Cells</param>
/// <param name="includeStart">Indicates whether the specified starting
/// Cell is included in the search</param>
/// <param name="checkOtherCellsInRow">Specifies whether all Cells in
/// the Row should be included in the search</param>
/// <returns>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</returns>
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<this.RowCount; i++)
{
int j = (first || !checkOtherCellsInRow ? startCol : 0);
for (; j<this.TableModel.Rows[i].Cells.Count; 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 == this.TableModel.Rows.Count)
{
i = -1;
}
}
else
{
break;
}
}
}
else
{
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;
}
/// <summary>
/// Returns a CellPos that specifies the next Cell that able to be
/// edited from the specified Cell
/// </summary>
/// <param name="start">A CellPos that specifies the Cell to start
/// searching from</param>
/// <param name="wrap">Specifies whether to move to the start of the
/// next Row when the end of the current Row is reached</param>
/// <param name="forward">Specifies whether the search should travel
/// in a forward direction (top to bottom, left to right) through the Cells</param>
/// <param name="includeStart">Indicates whether the specified starting
/// Cell is included in the search</param>
/// <returns>A CellPos that specifies the next Cell that is able to
/// be edited, or CellPos.Empty if there are no Cells that editable</returns>
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<this.RowCount; i++)
{
int j = (first ? startCol : 0);
for (; j<this.TableModel.Rows[i].Cells.Count; 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 == this.TableModel.Rows.Count)
{
i = -1;
}
}
else
{
break;
}
}
}
else
{
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
/// <summary>
/// Returns the index of the Column at the specified client coordinates
/// </summary>
/// <param name="x">The client x coordinate of the Column</param>
/// <param name="y">The client y coordinate of the Column</param>
/// <returns>The index of the Column at the specified client coordinates, or
/// -1 if it does not exist</returns>
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);
}
/// <summary>
/// Returns the index of the Column at the specified client point
/// </summary>
/// <param name="p">The point of interest</param>
/// <returns>The index of the Column at the specified client point, or
/// -1 if it does not exist</returns>
public int ColumnIndexAt(Point p)
{
return this.ColumnIndexAt(p.X, p.Y);
}
/// <summary>
/// Returns the bounding rectangle of the specified
/// column's header in client coordinates
/// </summary>
/// <param name="column">The index of the column</param>
/// <returns>The bounding rectangle of the specified
/// column's header</returns>
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;
}
/// <summary>
/// Returns the bounding rectangle of the specified
/// column's header in client coordinates
/// </summary>
/// <param name="column">The column</param>
/// <returns>The bounding rectangle of the specified
/// column's header</returns>
public Rectangle ColumnHeaderRect(Column column)
{
if (this.ColumnModel == null)
{
return Rectangle.Empty;
}
return this.ColumnHeaderRect(this.ColumnModel.Columns.IndexOf(column));
}
/// <summary>
/// Returns the bounding rectangle of the column at the
/// specified index in client coordinates
/// </summary>
/// <param name="column">The column</param>
/// <returns>The bounding rectangle of the column at the
/// specified index</returns>
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;
}
/// <summary>
/// Returns the bounding rectangle of the specified column
/// in client coordinates
/// </summary>
/// <param name="column">The column</param>
/// <returns>The bounding rectangle of the specified
/// column</returns>
public Rectangle ColumnRect(Column column)
{
if (this.ColumnModel == null)
{
return Rectangle.Empty;
}
return this.ColumnRect(this.ColumnModel.Columns.IndexOf(column));
}
#endregion
#region Rows
/// <summary>
/// Returns the index of the Row at the specified client coordinates
/// </summary>
/// <param name="x">The client x coordinate of the Row</param>
/// <param name="y">The client y coordinate of the Row</param>
/// <returns>The index of the Row at the specified client coordinates, or
/// -1 if it does not exist</returns>
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);
}
/// <summary>
/// Returns the index of the Row at the specified client point
/// </summary>
/// <param name="p">The point of interest</param>
/// <returns>The index of the Row at the specified client point, or
/// -1 if it does not exist</returns>
public int RowIndexAt(Point p)
{
return this.RowIndexAt(p.X, p.Y);
}
/// <summary>
/// Returns the bounding rectangle of the row at the
/// specified index in client coordinates
/// </summary>
/// <param name="row">The index of the row</param>
/// <returns>The bounding rectangle of the row at the
/// specified index</returns>
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;
}
/// <summary>
/// Returns the bounding rectangle of the specified row
/// in client coordinates
/// </summary>
/// <param name="row">The row</param>
/// <returns>The bounding rectangle of the specified
/// row</returns>
public Rectangle RowRect(Row row)
{
if (this.TableModel == null)
{
return Rectangle.Empty;
}
return this.RowRect(this.TableModel.Rows.IndexOf(row));
}
#endregion
#region Hit Tests
/// <summary>
/// Returns a TableRegions value that represents the table region at
/// the specified client coordinates
/// </summary>
/// <param name="x">The client x coordinate</param>
/// <param name="y">The client y coordinate</param>
/// <returns>A TableRegions value that represents the table region at
/// the specified client coordinates</returns>
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;
}
/// <summary>
/// Returns a TableRegions value that represents the table region at
/// the specified client point
/// </summary>
/// <param name="p">The point of interest</param>
/// <returns>A TableRegions value that represents the table region at
/// the specified client point</returns>
public TableRegion HitTest(Point p)
{
return this.HitTest(p.X, p.Y);
}
#endregion
#endregion
#region Dispose
/// <summary>
/// Releases the unmanaged resources used by the Control and optionally
/// releases the managed resources
/// </summary>
/// <param name="disposing">true to release both managed and unmanaged
/// resources; false to release only unmanaged resources</param>
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose(disposing);
}
/// <summary>
/// Removes the ColumnModel and TableModel from the Table
/// </summary>
public void Clear()
{
if (this.ColumnModel != null)
{
this.ColumnModel = null;
}
if (this.TableModel != null)
{
this.TableModel = null;
}
}
#endregion
#region Editing
/// <summary>
/// Records the Cell that is currently being edited and the
/// ICellEditor used to edit the Cell
/// </summary>
/// <param name="cell">The Cell that is currently being edited</param>
/// <param name="editor">The ICellEditor used to edit the Cell</param>
private void SetEditingCell(Cell cell, ICellEditor editor)
{
this.SetEditingCell(new CellPos(cell.Row.InternalIndex, cell.InternalIndex), editor);
}
/// <summary>
/// Records the Cell that is currently being edited and the
/// ICellEditor used to edit the Cell
/// </summary>
/// <param name="cellPos">The Cell that is currently being edited</param>
/// <param name="editor">The ICellEditor used to edit the Cell</param>
private void SetEditingCell(CellPos cellPos, ICellEditor editor)
{
this.editingCell = cellPos;
this.curentCellEditor = editor;
}
/// <summary>
/// Starts editing the Cell at the specified row and column indexes
/// </summary>
/// <param name="row">The row index of the Cell to be edited</param>
/// <param name="column">The column index of the Cell to be edited</param>
public void EditCell(int row, int column)
{
this.EditCell(new CellPos(row, column));
}
/// <summary>
/// Starts editing the Cell at the specified CellPos
/// </summary>
/// <param name="cellPos">A CellPos that specifies the Cell to be edited</param>
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();
}
}
/*/// <summary>
/// Stops editing the current Cell and starts editing the next editable Cell
/// </summary>
/// <param name="forwards">Specifies whether the editor should traverse
/// forward when looking for the next editable Cell</param>
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);
}
}*/
/// <summary>
/// Stops editing the current Cell and commits any changes
/// </summary>
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;
}
/// <summary>
/// Cancels editing the current Cell and ignores any changes
/// </summary>
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;
}
/// <summary>
/// Returns whether the Cell at the specified row and column is able
/// to be edited by the user
/// </summary>
/// <param name="row">The row index of the Cell to check</param>
/// <param name="column">The column index of the Cell to check</param>
/// <returns>True if the Cell at the specified row and column is able
/// to be edited by the user, false otherwise</returns>
public bool IsCellEditable(int row, int column)
{
return this.IsCellEditable(new CellPos(row, column));
}
/// <summary>
/// Returns whether the Cell at the specified CellPos is able
/// to be edited by the user
/// </summary>
/// <param name="cellpos">A CellPos that specifies the Cell to check</param>
/// <returns>True if the Cell at the specified CellPos is able
/// to be edited by the user, false otherwise</returns>
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);
}
/// <summary>
/// Returns whether the Cell at the specified row and column is able
/// to respond to user interaction
/// </summary>
/// <param name="row">The row index of the Cell to check</param>
/// <param name="column">The column index of the Cell to check</param>
/// <returns>True if the Cell at the specified row and column is able
/// to respond to user interaction, false otherwise</returns>
public bool IsCellEnabled(int row, int column)
{
return this.IsCellEnabled(new CellPos(row, column));
}
/// <summary>
/// Returns whether the Cell at the specified CellPos is able
/// to respond to user interaction
/// </summary>
/// <param name="cellpos">A CellPos that specifies the Cell to check</param>
/// <returns>True if the Cell at the specified CellPos is able
/// to respond to user interaction, false otherwise</returns>
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
/// <summary>
/// Invalidates the specified Cell
/// </summary>
/// <param name="cell">The Cell to be invalidated</param>
public void InvalidateCell(Cell cell)
{
this.InvalidateCell(cell.Row.Index, cell.Index);
}
/// <summary>
/// Invalidates the Cell located at the specified row and column indicies
/// </summary>
/// <param name="row">The row index of the Cell to be invalidated</param>
/// <param name="column">The column index of the Cell to be invalidated</param>
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);
}
}
/// <summary>
/// Invalidates the Cell located at the specified CellPos
/// </summary>
/// <param name="cellPos">A CellPos that specifies the Cell to be invalidated</param>
public void InvalidateCell(CellPos cellPos)
{
this.InvalidateCell(cellPos.Row, cellPos.Column);
}
/// <summary>
/// Invalidates the specified Row
/// </summary>
/// <param name="row">The Row to be invalidated</param>
public void InvalidateRow(Row row)
{
this.InvalidateRow(row.Index);
}
/// <summary>
/// Invalidates the Row located at the specified row index
/// </summary>
/// <param name="row">The row index of the Row to be invalidated</param>
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);
}
}
/// <summary>
/// Invalidates the Row located at the specified CellPos
/// </summary>
/// <param name="cellPos">A CellPos that specifies the Row to be invalidated</param>
public void InvalidateRow(CellPos cellPos)
{
this.InvalidateRow(cellPos.Row);
}
#endregion
#region Keys
/// <summary>
/// Determines whether the specified key is reserved for use by the Table
/// </summary>
/// <param name="key">One of the Keys values</param>
/// <returns>true if the specified key is reserved for use by the Table;
/// otherwise, false</returns>
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;
}
/// <summary>
/// Determines whether the specified key is a regular input key or a special
/// key that requires preprocessing
/// </summary>
/// <param name="keyData">One of the Keys values</param>
/// <returns>true if the specified key is a regular input key; otherwise, false</returns>
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
/// <summary>
/// Prevents the Table from drawing until the EndUpdate method is called
/// </summary>
public void BeginUpdate()
{
if (this.IsHandleCreated)
{
if (this.beginUpdateCount == 0)
{
NativeMethods.SendMessage(this.Handle, 11, 0, 0);
}
this.beginUpdateCount++;
}
}
/// <summary>
/// Resumes drawing of the Table after drawing is suspended by the
/// BeginUpdate method
/// </summary>
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);
}
}
/// <summary>
/// Signals the object that initialization is starting
/// </summary>
public void BeginInit()
{
this.init = true;
}
/// <summary>
/// Signals the object that initialization is complete
/// </summary>
public void EndInit()
{
this.init = false;
this.PerformLayout();
}
/// <summary>
/// Gets whether the Table is currently initializing
/// </summary>
[Browsable(false)]
public bool Initializing
{
get
{
return this.init;
}
}
#endregion
#region Mouse
/// <summary>
/// This member supports the .NET Framework infrastructure and is not
/// intended to be used directly from your code
/// </summary>
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
/// <summary>
/// Updates the scrollbars to reflect any changes made to the Table
/// </summary>
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;
}
}
/// <summary>
/// Scrolls the contents of the Table horizontally to the specified value
/// </summary>
/// <param name="value">The value to scroll to</param>
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);
}
}
}
/// <summary>
/// Scrolls the contents of the Table vertically to the specified value
/// </summary>
/// <param name="value">The value to scroll to</param>
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);
}
}
}
/// <summary>
/// Ensures that the Cell at the specified row and column is visible
/// within the Table, scrolling the contents of the Table if necessary
/// </summary>
/// <param name="row">The zero-based index of the row to scroll into view</param>
/// <param name="column">The zero-based index of the column to scroll into view</param>
/// <returns>true if the Table scrolled to the Cell at the specified row
/// and column, false otherwise</returns>
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;
}
/// <summary>
/// Ensures that the Cell at the specified CellPos is visible within
/// the Table, scrolling the contents of the Table if necessary
/// </summary>
/// <param name="cellPos">A CellPos that contains the zero-based index
/// of the row and column to scroll into view</param>
/// <returns></returns>
public bool EnsureVisible(CellPos cellPos)
{
return this.EnsureVisible(cellPos.Row, cellPos.Column);
}
/// <summary>
/// Gets the index of the first visible Column currently displayed in the Table
/// </summary>
[Browsable(false)]
public int FirstVisibleColumn
{
get
{
if (this.ColumnModel == null || this.ColumnModel.VisibleColumnCount == 0)
{
return -1;
}
return this.ColumnModel.ColumnIndexAtX(this.hScrollBar.Value);
}
}
/// <summary>
/// Gets the index of the last visible Column currently displayed in the Table
/// </summary>
[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
/// <summary>
/// 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
/// </summary>
public void Sort()
{
this.Sort(true);
}
/// <summary>
/// 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
/// </summary>
/// <param name="stable">Specifies whether a stable sorting method
/// should be used to sort the column</param>
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);
}
}
/// <summary>
/// Sorts the specified column opposite to its current sort order,
/// or in ascending order if the column is not sorted
/// </summary>
/// <param name="column">The index of the column to sort</param>
public void Sort(int column)
{
this.Sort(column, true);
}
/// <summary>
/// Sorts the specified column opposite to its current sort order,
/// or in ascending order if the column is not sorted
/// </summary>
/// <param name="column">The index of the column to sort</param>
/// <param name="stable">Specifies whether a stable sorting method
/// should be used to sort the column</param>
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);
}
}
/// <summary>
/// Sorts the specified column in the specified sort direction
/// </summary>
/// <param name="column">The index of the column to sort</param>
/// <param name="sortOrder">The direction the column is to be sorted</param>
public void Sort(int column, SortOrder sortOrder)
{
this.Sort(column, sortOrder, true);
}
/// <summary>
/// Sorts the specified column in the specified sort direction
/// </summary>
/// <param name="column">The index of the column to sort</param>
/// <param name="sortOrder">The direction the column is to be sorted</param>
/// <param name="stable">Specifies whether a stable sorting method
/// should be used to sort the column</param>
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);
}
}
/// <summary>
/// Sorts the specified column in the specified sort direction
/// </summary>
/// <param name="index">The index of the column to sort</param>
/// <param name="column">The column to sort</param>
/// <param name="sortOrder">The direction the column is to be sorted</param>
/// <param name="stable">Specifies whether a stable sorting method
/// should be used to sort the column</param>
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();
}
/// <summary>
/// Returns whether a Column exists at the specified index in the
/// Table's ColumnModel
/// </summary>
/// <param name="column">The index of the column to check</param>
/// <returns>True if a Column exists at the specified index in the
/// Table's ColumnModel, false otherwise</returns>
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
/// <summary>
/// Gets or sets the border style for the Table
/// </summary>
[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);
}
}
}
/// <summary>
/// Gets the width of the Tables border
/// </summary>
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
/// <summary>
/// Gets the last known cell position that the mouse was over
/// </summary>
[Browsable(false)]
public CellPos LastMouseCell
{
get
{
return this.lastMouseCell;
}
}
/// <summary>
/// Gets the last known cell position that the mouse's left
/// button was pressed in
/// </summary>
[Browsable(false)]
public CellPos LastMouseDownCell
{
get
{
return this.lastMouseDownCell;
}
}
/// <summary>
/// Gets or sets the position of the Cell that currently has focus
/// </summary>
[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);
}
}
}
}
/// <summary>
/// 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
/// </summary>
[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
/// <summary>
/// Gets the rectangle that represents the "client area" of the control.
/// (The rectangle excludes the borders and scrollbars)
/// </summary>
[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;
}
}
/// <summary>
/// Gets the rectangle that represents the "cell data area" of the control.
/// (The rectangle excludes the borders, column headers and scrollbars)
/// </summary>
[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;
}
}
/// <summary>
///
/// </summary>
private Rectangle InternalBorderRect
{
get
{
return new Rectangle(this.BorderWidth,
this.BorderWidth,
this.Width - (this.BorderWidth * 2),
this.Height - (this.BorderWidth * 2));
}
}
#endregion
#region ColumnModel
/// <summary>
/// Gets or sets the ColumnModel that contains all the Columns
/// displayed in the Table
/// </summary>
[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);
}
}
}
/// <summary>
/// Gets or sets whether the Table allows users to resize Column widths
/// </summary>
[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;
}
}
}
/// <summary>
/// Returns the number of Columns in the Table
/// </summary>
[Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public int ColumnCount
{
get
{
if (this.ColumnModel == null)
{
return -1;
}
return this.ColumnModel.Columns.Count;
}
}
/// <summary>
/// Returns the index of the currently sorted Column
/// </summary>
[Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public int SortingColumn
{
get
{
return this.lastSortedColumn;
}
}
/// <summary>
/// Gets or sets the background Color for the currently sorted column
/// </summary>
[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));
}
}
}
}
}
/// <summary>
/// Specifies whether the Table's SortedColumnBackColor property
/// should be serialized at design time
/// </summary>
/// <returns>True if the SortedColumnBackColor property should be
/// serialized, False otherwise</returns>
private bool ShouldSerializeSortedColumnBackColor()
{
return this.sortedColumnBackColor != Color.WhiteSmoke;
}
#endregion
#region DisplayRectangle
/// <summary>
/// Gets the rectangle that represents the display area of the Table
/// </summary>
[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
/// <summary>
/// Gets whether the Table is currently editing a Cell
/// </summary>
[Browsable(false)]
public bool IsEditing
{
get
{
return !this.EditingCell.IsEmpty;
}
}
/// <summary>
/// Gets a CellPos that specifies the position of the Cell that
/// is currently being edited
/// </summary>
[Browsable(false)]
public CellPos EditingCell
{
get
{
return this.editingCell;
}
}
/// <summary>
/// Gets the ICellEditor that is currently being used to edit a Cell
/// </summary>
[Browsable(false)]
public ICellEditor EditingCellEditor
{
get
{
return this.curentCellEditor;
}
}
/// <summary>
/// Gets or sets the action that causes editing to be initiated
/// </summary>
[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;
}
}
}
/// <summary>
/// Gets or sets the custom key used to initiate Cell editing
/// </summary>
[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;
}
}
}
/*/// <summary>
/// Gets or sets whether pressing the Tab key during editing moves
/// the editor to the next editable Cell
/// </summary>
[Category("Editing"),
DefaultValue(true),
Description("")]
public bool TabMovesEditor
{
get
{
return this.tabMovesEditor;
}
set
{
this.tabMovesEditor = value;
}
}*/
#endregion
#region Grid
/// <summary>
/// Gets or sets how grid lines are displayed around rows and columns
/// </summary>
[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);
}
}
}
/// <summary>
/// Gets or sets the style of the lines used to draw the grid
/// </summary>
[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);
}
}
}
}
/// <summary>
/// Gets or sets the Color of the grid lines
/// </summary>
[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);
}
}
}
}
/// <summary>
/// Specifies whether the Table's GridColor property
/// should be serialized at design time
/// </summary>
/// <returns>True if the GridColor property should be
/// serialized, False otherwise</returns>
private bool ShouldSerializeGridColor()
{
return (this.GridColor != SystemColors.Control);
}
/// <summary>
///
/// </summary>
public override Color BackColor
{
get
{
return base.BackColor;
}
set
{
base.BackColor = value;
}
}
/// <summary>
/// Specifies whether the Table's BackColor property
/// should be serialized at design time
/// </summary>
/// <returns>True if the BackColor property should be
/// serialized, False otherwise</returns>
private bool ShouldSerializeBackColor()
{
return (this.BackColor != Color.White);
}
#endregion
#region Header
/// <summary>
/// Gets or sets the column header style
/// </summary>
[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();
}
}
}
/// <summary>
/// Gets the height of the column headers
/// </summary>
[Browsable(false)]
public int HeaderHeight
{
get
{
if (this.ColumnModel == null || this.HeaderStyle == ColumnHeaderStyle.None)
{
return 0;
}
return this.ColumnModel.HeaderHeight;
}
}
/// <summary>
/// Gets a Rectangle that specifies the size and location of
/// the Table's column header area
/// </summary>
[Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Rectangle HeaderRectangle
{
get
{
return new Rectangle(this.BorderWidth, this.BorderWidth, this.PseudoClientRect.Width, this.HeaderHeight);
}
}
/// <summary>
/// Gets or sets the font used to draw the text in the column headers
/// </summary>
[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);
}
}
}
/// <summary>
/// Specifies whether the Table's HeaderFont property
/// should be serialized at design time
/// </summary>
/// <returns>True if the HeaderFont property should be
/// serialized, False otherwise</returns>
private bool ShouldSerializeHeaderFont()
{
return this.HeaderFont != this.Font;
}
/// <summary>
/// Gets or sets the HeaderRenderer used to draw the Column headers
/// </summary>
[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);
}
}
}
/// <summary>
/// Gets the ContextMenu used for Column Headers
/// </summary>
[Browsable(false)]
public HeaderContextMenu HeaderContextMenu
{
get
{
return this.headerContextMenu;
}
}
/// <summary>
/// Gets or sets whether the HeaderContextMenu is able to be
/// displayed when the user right clicks on a Column Header
/// </summary>
[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
/// <summary>
/// Gets or sets the height of each row
/// </summary>
[Browsable(false)]
public int RowHeight
{
get
{
if (this.TableModel == null)
{
return 0;
}
return this.TableModel.RowHeight;
}
}
/// <summary>
/// Gets the combined height of all the rows in the Table
/// </summary>
[Browsable(false)]
protected int TotalRowHeight
{
get
{
if (this.TableModel == null)
{
return 0;
}
return this.TableModel.TotalRowHeight;
}
}
/// <summary>
/// Gets the combined height of all the rows in the Table
/// plus the height of the column headers
/// </summary>
[Browsable(false)]
protected int TotalRowAndHeaderHeight
{
get
{
return this.TotalRowHeight + this.HeaderHeight;
}
}
/// <summary>
/// Returns the number of Rows in the Table
/// </summary>
[Browsable(false)]
public int RowCount
{
get
{
if (this.TableModel == null)
{
return 0;
}
return this.TableModel.Rows.Count;
}
}
/// <summary>
/// Gets the number of rows that are visible in the Table
/// </summary>
[Browsable(false)]
public int VisibleRowCount
{
get
{
int count = this.CellDataRect.Height / this.RowHeight;
if ((this.CellDataRect.Height % this.RowHeight) > 0)
{
count++;
}
return count;
}
}
/// <summary>
/// Gets the index of the first visible row in the Table
/// </summary>
[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;
}
}
/// <summary>
/// Gets the first visible row in the Table
/// </summary>
[Browsable(false)]
public Row TopItem
{
get
{
if (this.TableModel == null || this.TableModel.Rows.Count == 0)
{
return null;
}
return this.TableModel.Rows[this.TopIndex];
}
}
/// <summary>
/// Gets or sets the background color of odd-numbered rows in the Table
/// </summary>
[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
/// <summary>
/// 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
/// </summary>
[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();
}
}
}
/// <summary>
/// Gets a value indicating whether the horizontal
/// scroll bar is visible
/// </summary>
[Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool HScroll
{
get
{
if (this.hScrollBar == null)
{
return false;
}
return this.hScrollBar.Visible;
}
}
/// <summary>
/// Gets a value indicating whether the vertical
/// scroll bar is visible
/// </summary>
[Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool VScroll
{
get
{
if (this.vScrollBar == null)
{
return false;
}
return this.vScrollBar.Visible;
}
}
#endregion
#region Selection
/// <summary>
/// Gets or sets whether cells are allowed to be selected
/// </summary>
[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();
}
}
}
}
/// <summary>
/// Gets or sets how selected Cells are drawn by a Table
/// </summary>
[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);
}
}
}
}
/// <summary>
/// Gets or sets whether multiple cells are allowed to be selected
/// </summary>
[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;
}
}
}
/// <summary>
/// Gets or sets whether all other cells in the row are highlighted
/// when a cell is selected
/// </summary>
[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);
}
}
}
}
/// <summary>
/// Gets or sets whether highlighting is removed from the selected
/// cells when the Table loses focus
/// </summary>
[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);
}
}
}
}
/// <summary>
/// Gets or sets the background color of a selected cell
/// </summary>
[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);
}
}
}
}
/// <summary>
/// Specifies whether the Table's SelectionBackColor property
/// should be serialized at design time
/// </summary>
/// <returns>True if the SelectionBackColor property should be
/// serialized, False otherwise</returns>
private bool ShouldSerializeSelectionBackColor()
{
return (this.selectionBackColor != SystemColors.Highlight);
}
/// <summary>
/// Gets or sets the foreground color of a selected cell
/// </summary>
[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);
}
}
}
}
/// <summary>
/// Specifies whether the Table's SelectionForeColor property
/// should be serialized at design time
/// </summary>
/// <returns>True if the SelectionForeColor property should be
/// serialized, False otherwise</returns>
private bool ShouldSerializeSelectionForeColor()
{
return (this.selectionForeColor != SystemColors.HighlightText);
}
/// <summary>
/// Gets or sets the background color of a selected cell when the
/// Table doesn't have the focus
/// </summary>
[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);
}
}
}
}
/// <summary>
/// Specifies whether the Table's UnfocusedSelectionBackColor property
/// should be serialized at design time
/// </summary>
/// <returns>True if the UnfocusedSelectionBackColor property should be
/// serialized, False otherwise</returns>
private bool ShouldSerializeUnfocusedSelectionBackColor()
{
return (this.unfocusedSelectionBackColor != SystemColors.Control);
}
/// <summary>
/// Gets or sets the foreground color of a selected cell when the
/// Table doesn't have the focus
/// </summary>
[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);
}
}
}
}
/// <summary>
/// Specifies whether the Table's UnfocusedSelectionForeColor property
/// should be serialized at design time
/// </summary>
/// <returns>True if the UnfocusedSelectionForeColor property should be
/// serialized, False otherwise</returns>
private bool ShouldSerializeUnfocusedSelectionForeColor()
{
return (this.unfocusedSelectionForeColor != SystemColors.ControlText);
}
/// <summary>
/// Gets an array that contains the currently selected Rows
/// </summary>
[Browsable(false)]
public Row[] SelectedItems
{
get
{
if (this.TableModel == null)
{
return new Row[0];
}
return this.TableModel.Selections.SelectedItems;
}
}
/// <summary>
/// Gets an array that contains the indexes of the currently selected Rows
/// </summary>
[Browsable(false)]
public int[] SelectedIndicies
{
get
{
if (this.TableModel == null)
{
return new int[0];
}
return this.TableModel.Selections.SelectedIndicies;
}
}
#endregion
#region TableModel
/// <summary>
/// Gets or sets the TableModel that contains all the Rows
/// and Cells displayed in the Table
/// </summary>
[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);
}
}
}
/// <summary>
/// Gets or sets the text displayed by the Table when it doesn't
/// contain any items
/// </summary>
[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
/// <summary>
/// Gets or sets the current state of the Table
/// </summary>
protected TableState TableState
{
get
{
return this.tableState;
}
set
{
this.tableState = value;
}
}
/// <summary>
/// Calculates the state of the Table at the specified
/// client coordinates
/// </summary>
/// <param name="x">The client x coordinate</param>
/// <param name="y">The client y coordinate</param>
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;
}
}
/// <summary>
/// Gets whether the Table is able to raise events
/// </summary>
protected internal bool CanRaiseEvents
{
get
{
return (this.IsHandleCreated && this.beginUpdateCount == 0);
}
}
/// <summary>
/// Gets or sets whether the Table is being used as a preview Table in
/// a ColumnCollectionEditor
/// </summary>
internal bool Preview
{
get
{
return this.preview;
}
set
{
this.preview = value;
}
}
#endregion
#region ToolTips
/// <summary>
/// Gets the internal tooltip component
/// </summary>
internal ToolTip ToolTip
{
get
{
return this.toolTip;
}
}
/// <summary>
/// Gets or sets whether ToolTips are currently enabled for the Table
/// </summary>
[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;
}
}
/// <summary>
/// Gets or sets the automatic delay for the Table's ToolTip
/// </summary>
[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;
}
}
}
/// <summary>
/// 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
/// </summary>
[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;
}
}
}
/// <summary>
/// Gets or sets the time that passes before the Table's ToolTip appears
/// </summary>
[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;
}
}
}
/// <summary>
/// Gets or sets whether the Table's ToolTip window is
/// displayed even when its parent control is not active
/// </summary>
[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;
}
}
}
/// <summary>
///
/// </summary>
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
/// <summary>
/// Raises the CellPropertyChanged event
/// </summary>
/// <param name="e">A CellEventArgs that contains the event data</param>
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));
}
}
}
/// <summary>
/// Handler for a Cells PropertyChanged event
/// </summary>
/// <param name="sender">The object that raised the event</param>
/// <param name="e">A CellEventArgs that contains the event data</param>
private void cell_PropertyChanged(object sender, CellEventArgs e)
{
this.OnCellPropertyChanged(e);
}
#region Buttons
/// <summary>
/// Raises the CellButtonClicked event
/// </summary>
/// <param name="e">A CellButtonEventArgs that contains the event data</param>
protected internal virtual void OnCellButtonClicked(CellButtonEventArgs e)
{
if (this.CanRaiseEvents)
{
if (CellButtonClicked != null)
{
CellButtonClicked(this, e);
}
}
}
#endregion
#region CheckBox
/// <summary>
/// Raises the CellCheckChanged event
/// </summary>
/// <param name="e">A CellCheckChanged that contains the event data</param>
protected internal virtual void OnCellCheckChanged(CellCheckBoxEventArgs e)
{
if (this.CanRaiseEvents)
{
if (CellCheckChanged != null)
{
CellCheckChanged(this, e);
}
}
}
#endregion
#region Focus
/// <summary>
/// Raises the CellGotFocus event
/// </summary>
/// <param name="e">A CellFocusEventArgs that contains the event data</param>
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);
}
}
}
/// <summary>
/// Raises the GotFocus event for the Cell at the specified position
/// </summary>
/// <param name="cellPos">The position of the Cell that gained focus</param>
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);
}
}
/// <summary>
/// Raises the CellLostFocus event
/// </summary>
/// <param name="e">A CellFocusEventArgs that contains the event data</param>
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);
}
}
}
/// <summary>
/// Raises the LostFocus event for the Cell at the specified position
/// </summary>
/// <param name="cellPos">The position of the Cell that lost focus</param>
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
/// <summary>
/// Raises the CellKeyDown event
/// </summary>
/// <param name="e">A CellKeyEventArgs that contains the event data</param>
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);
}
}
}
/// <summary>
/// Raises a KeyDown event for the Cell at the specified cell position
/// </summary>
/// <param name="cellPos">The position of the Cell</param>
/// <param name="e">A KeyEventArgs that contains the event data</param>
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);
}
}
/// <summary>
/// Raises the CellKeyUp event
/// </summary>
/// <param name="e">A CellKeyEventArgs that contains the event data</param>
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);
}
}
}
/// <summary>
/// Raises a KeyUp event for the Cell at the specified cell position
/// </summary>
/// <param name="cellPos">The position of the Cell</param>
/// <param name="e">A KeyEventArgs that contains the event data</param>
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
/// <summary>
/// Raises the CellMouseEnter event
/// </summary>
/// <param name="e">A CellMouseEventArgs that contains the event data</param>
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);
}
}
}
/// <summary>
/// Raises a MouseEnter event for the Cell at the specified cell position
/// </summary>
/// <param name="cellPos">The position of the Cell</param>
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
/// <summary>
/// Raises the CellMouseLeave event
/// </summary>
/// <param name="e">A CellMouseEventArgs that contains the event data</param>
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);
}
}
}
/// <summary>
/// Raises a MouseLeave event for the Cell at the specified cell position
/// </summary>
/// <param name="cellPos">The position of the Cell</param>
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
/// <summary>
/// Raises the CellMouseUp event
/// </summary>
/// <param name="e">A CellMouseEventArgs that contains the event data</param>
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);
}
}
}
/// <summary>
/// Raises a MouseUp event for the Cell at the specified cell position
/// </summary>
/// <param name="cellPos">The position of the Cell</param>
/// <param name="e">A MouseEventArgs that contains the event data</param>
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
/// <summary>
/// Raises the CellMouseDown event
/// </summary>
/// <param name="e">A CellMouseEventArgs that contains the event data</param>
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);
}
}
}
/// <summary>
/// Raises a MouseDown event for the Cell at the specified cell position
/// </summary>
/// <param name="cellPos">The position of the Cell</param>
/// <param name="e">A MouseEventArgs that contains the event data</param>
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
/// <summary>
/// Raises the CellMouseMove event
/// </summary>
/// <param name="e">A CellMouseEventArgs that contains the event data</param>
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);
}
}
}
/// <summary>
/// Raises a MouseMove event for the Cell at the specified cell position
/// </summary>
/// <param name="cellPos">The position of the Cell</param>
/// <param name="e">A MouseEventArgs that contains the event data</param>
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);
}
}
/// <summary>
/// Resets the last known cell position that the mouse was over to empty
/// </summary>
internal void ResetLastMouseCell()
{
if (!this.lastMouseCell.IsEmpty)
{
this.ResetMouseEventArgs();
CellPos oldLastMouseCell = this.lastMouseCell;
this.lastMouseCell = CellPos.Empty;
this.RaiseCellMouseLeave(oldLastMouseCell);
}
}
#endregion
#region MouseHover
/// <summary>
/// Raises the CellHover event
/// </summary>
/// <param name="e">A CellEventArgs that contains the event data</param>
protected virtual void OnCellMouseHover(CellMouseEventArgs e)
{
if (this.CanRaiseEvents)
{
if (CellMouseHover != null)
{
CellMouseHover(e.Cell, e);
}
}
}
#endregion
#region Click
/// <summary>
/// Raises the CellClick event
/// </summary>
/// <param name="e">A CellEventArgs that contains the event data</param>
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);
}
}
}
/// <summary>
/// Raises the CellDoubleClick event
/// </summary>
/// <param name="e">A CellEventArgs that contains the event data</param>
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
/// <summary>
/// Raises the ColumnPropertyChanged event
/// </summary>
/// <param name="e">A ColumnEventArgs that contains the event data</param>
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
/// <summary>
/// Raises the HeaderMouseEnter event
/// </summary>
/// <param name="e">A HeaderMouseEventArgs that contains the event data</param>
protected virtual void OnHeaderMouseEnter(HeaderMouseEventArgs e)
{
if (this.CanRaiseEvents)
{
if (this.HeaderRenderer != null)
{
this.HeaderRenderer.OnMouseEnter(e);
}
if (HeaderMouseEnter != null)
{
HeaderMouseEnter(e.Column, e);
}
}
}
/// <summary>
/// Raises a MouseEnter event for the Column header at the specified colunm
/// index position
/// </summary>
/// <param name="index">The index of the column to recieve the event</param>
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
/// <summary>
/// Raises the HeaderMouseLeave event
/// </summary>
/// <param name="e">A HeaderMouseEventArgs that contains the event data</param>
protected virtual void OnHeaderMouseLeave(HeaderMouseEventArgs e)
{
if (this.CanRaiseEvents)
{
if (this.HeaderRenderer != null)
{
this.HeaderRenderer.OnMouseLeave(e);
}
if (HeaderMouseLeave != null)
{
HeaderMouseLeave(e.Column, e);
}
}
}
/// <summary>
/// Raises a MouseLeave event for the Column header at the specified colunm
/// index position
/// </summary>
/// <param name="index">The index of the column to recieve the event</param>
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
/// <summary>
/// Raises the HeaderMouseUp event
/// </summary>
/// <param name="e">A HeaderMouseEventArgs that contains the event data</param>
protected virtual void OnHeaderMouseUp(HeaderMouseEventArgs e)
{
if (this.CanRaiseEvents)
{
if (this.HeaderRenderer != null)
{
this.HeaderRenderer.OnMouseUp(e);
}
if (HeaderMouseUp != null)
{
HeaderMouseUp(e.Column, e);
}
}
}
/// <summary>
/// Raises a MouseUp event for the Column header at the specified colunm
/// index position
/// </summary>
/// <param name="index">The index of the column to recieve the event</param>
/// <param name="e">A HeaderMouseEventArgs that contains the event data</param>
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
/// <summary>
/// Raises the HeaderMouseDown event
/// </summary>
/// <param name="e">A HeaderMouseEventArgs that contains the event data</param>
protected virtual void OnHeaderMouseDown(HeaderMouseEventArgs e)
{
if (this.CanRaiseEvents)
{
if (this.HeaderRenderer != null)
{
this.HeaderRenderer.OnMouseDown(e);
}
if (HeaderMouseDown != null)
{
HeaderMouseDown(e.Column, e);
}
}
}
/// <summary>
/// Raises a MouseDown event for the Column header at the specified colunm
/// index position
/// </summary>
/// <param name="index">The index of the column to recieve the event</param>
/// <param name="e">A HeaderMouseEventArgs that contains the event data</param>
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
/// <summary>
/// Raises the HeaderMouseMove event
/// </summary>
/// <param name="e">A HeaderMouseEventArgs that contains the event data</param>
protected virtual void OnHeaderMouseMove(HeaderMouseEventArgs e)
{
if (this.CanRaiseEvents)
{
if (this.HeaderRenderer != null)
{
this.HeaderRenderer.OnMouseMove(e);
}
if (HeaderMouseMove != null)
{
HeaderMouseMove(e.Column, e);
}
}
}
/// <summary>
/// Raises a MouseMove event for the Column header at the specified colunm
/// index position
/// </summary>
/// <param name="index">The index of the column to recieve the event</param>
/// <param name="e">A HeaderMouseEventArgs that contains the event data</param>
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);
}
}
/// <summary>
/// Resets the current "hot" column
/// </summary>
internal void ResetHotColumn()
{
if (this.hotColumn != -1)
{
this.ResetMouseEventArgs();
int oldHotColumn = this.hotColumn;
this.hotColumn = -1;
this.RaiseHeaderMouseLeave(oldHotColumn);
}
}
#endregion
#region MouseHover
/// <summary>
/// Raises the HeaderMouseHover event
/// </summary>
/// <param name="e">A HeaderMouseEventArgs that contains the event data</param>
protected virtual void OnHeaderMouseHover(HeaderMouseEventArgs e)
{
if (this.CanRaiseEvents)
{
if (HeaderMouseHover != null)
{
HeaderMouseHover(e.Column, e);
}
}
}
#endregion
#region Click
/// <summary>
/// Raises the HeaderClick event
/// </summary>
/// <param name="e">A HeaderMouseEventArgs that contains the event data</param>
protected virtual void OnHeaderClick(HeaderMouseEventArgs e)
{
if (this.CanRaiseEvents)
{
if (this.HeaderRenderer != null)
{
this.HeaderRenderer.OnClick(e);
}
if (HeaderClick != null)
{
HeaderClick(e.Column, e);
}
}
}
/// <summary>
/// Raises the HeaderDoubleClick event
/// </summary>
/// <param name="e">A HeaderMouseEventArgs that contains the event data</param>
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
/// <summary>
/// Raises the ColumnModelChanged event
/// </summary>
/// <param name="e">An EventArgs that contains the event data</param>
protected virtual void OnColumnModelChanged(EventArgs e)
{
if (this.CanRaiseEvents)
{
this.PerformLayout();
this.Invalidate();
if (ColumnModelChanged != null)
{
ColumnModelChanged(this, e);
}
}
}
/// <summary>
/// Raises the ColumnAdded event
/// </summary>
/// <param name="e">A ColumnModelEventArgs that contains the event data</param>
protected internal virtual void OnColumnAdded(ColumnModelEventArgs e)
{
if (this.CanRaiseEvents)
{
this.PerformLayout();
this.Invalidate();
if (ColumnAdded != null)
{
ColumnAdded(this, e);
}
}
}
/// <summary>
/// Raises the ColumnRemoved event
/// </summary>
/// <param name="e">A ColumnModelEventArgs that contains the event data</param>
protected internal virtual void OnColumnRemoved(ColumnModelEventArgs e)
{
if (this.CanRaiseEvents)
{
this.PerformLayout();
this.Invalidate();
if (ColumnRemoved != null)
{
ColumnRemoved(this, e);
}
}
}
/// <summary>
/// Raises the HeaderHeightChanged event
/// </summary>
/// <param name="e">An EventArgs that contains the event data</param>
protected internal virtual void OnHeaderHeightChanged(EventArgs e)
{
if (this.CanRaiseEvents)
{
this.PerformLayout();
this.Invalidate();
if (HeaderHeightChanged != null)
{
HeaderHeightChanged(this, e);
}
}
}
#endregion
#region Editing
/// <summary>
/// Raises the BeginEditing event
/// </summary>
/// <param name="e">A CellEditEventArgs that contains the event data</param>
protected internal virtual void OnBeginEditing(CellEditEventArgs e)
{
if (this.CanRaiseEvents)
{
if (BeginEditing != null)
{
BeginEditing(e.Cell, e);
}
}
}
/// <summary>
/// Raises the EditingStopped event
/// </summary>
/// <param name="e">A CellEditEventArgs that contains the event data</param>
protected internal virtual void OnEditingStopped(CellEditEventArgs e)
{
if (this.CanRaiseEvents)
{
if (EditingStopped != null)
{
EditingStopped(e.Cell, e);
}
}
}
/// <summary>
/// Raises the EditingCancelled event
/// </summary>
/// <param name="e">A CellEditEventArgs that contains the event data</param>
protected internal virtual void OnEditingCancelled(CellEditEventArgs e)
{
if (this.CanRaiseEvents)
{
if (EditingCancelled != null)
{
EditingCancelled(e.Cell, e);
}
}
}
#endregion
#region Focus
/// <summary>
/// Raises the GotFocus event
/// </summary>
/// <param name="e">An EventArgs that contains the event data</param>
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);
}
/// <summary>
/// Raises the LostFocus event
/// </summary>
/// <param name="e">An EventArgs that contains the event data</param>
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
/// <summary>
/// Raises the KeyDown event
/// </summary>
/// <param name="e">A KeyEventArgs that contains the event data</param>
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
/// <summary>
/// Raises the KeyUp event
/// </summary>
/// <param name="e">A KeyEventArgs that contains the event data</param>
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
/// <summary>
/// Raises the Layout event
/// </summary>
/// <param name="levent">A LayoutEventArgs that contains the event data</param>
protected override void OnLayout(LayoutEventArgs levent)
{
if (!this.IsHandleCreated || this.init)
{
return;
}
base.OnLayout(levent);
this.UpdateScrollBars();
}
#endregion
#region Mouse
#region MouseUp
/// <summary>
/// Raises the MouseUp event
/// </summary>
/// <param name="e">A MouseEventArgs that contains the event data</param>
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
/// <summary>
/// Raises the MouseDown event
/// </summary>
/// <param name="e">A MouseEventArgs that contains the event data</param>
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
/// <summary>
/// Raises the MouseMove event
/// </summary>
/// <param name="e">A MouseEventArgs that contains the event data</param>
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
/// <summary>
/// Raises the MouseLeave event
/// </summary>
/// <param name="e">An EventArgs that contains the event data</param>
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
/// <summary>
/// Raises the MouseWheel event
/// </summary>
/// <param name="e">A MouseEventArgs that contains the event data</param>
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
/// <summary>
/// Raises the MouseHover event
/// </summary>
/// <param name="e">An EventArgs that contains the event data</param>
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
/// <summary>
/// Raises the Click event
/// </summary>
/// <param name="e">An EventArgs that contains the event data</param>
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))));
}
}
/// <summary>
/// Raises the DoubleClick event
/// </summary>
/// <param name="e">An EventArgs that contains the event data</param>
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
/// <summary>
/// Raises the PaintBackground event
/// </summary>
/// <param name="e">A PaintEventArgs that contains the event data</param>
protected override void OnPaintBackground(PaintEventArgs e)
{
base.OnPaintBackground(e);
}
/// <summary>
/// Raises the Paint event
/// </summary>
/// <param name="e">A PaintEventArgs that contains the event data</param>
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);
}
/// <summary>
/// Draws a reversible line at the specified screen x-coordinate
/// that is the height of the PseudoClientRect
/// </summary>
/// <param name="x">The screen x-coordinate of the reversible line
/// to be drawn</param>
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
/// <summary>
/// Paints the Table's border
/// </summary>
/// <param name="e">A PaintEventArgs that contains the event data</param>
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
/// <summary>
/// Paints the Cell at the specified row and column indexes
/// </summary>
/// <param name="e">A PaintEventArgs that contains the event data</param>
/// <param name="row">The index of the row that contains the cell to be painted</param>
/// <param name="column">The index of the column that contains the cell to be painted</param>
/// <param name="cellRect">The bounding Rectangle of the Cell</param>
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);
}
/// <summary>
/// Raises the BeforePaintCell event
/// </summary>
/// <param name="e">A PaintCellEventArgs that contains the event data</param>
protected virtual void OnBeforePaintCell(PaintCellEventArgs e)
{
if (BeforePaintCell != null)
{
BeforePaintCell(this, e);
}
}
/// <summary>
/// Raises the AfterPaintCell event
/// </summary>
/// <param name="e">A PaintCellEventArgs that contains the event data</param>
protected virtual void OnAfterPaintCell(PaintCellEventArgs e)
{
if (AfterPaintCell != null)
{
AfterPaintCell(this, e);
}
}
#endregion
#region Grid
/// <summary>
/// Paints the Table's grid
/// </summary>
/// <param name="e">A PaintEventArgs that contains the event data</param>
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<this.ColumnModel.Columns.Count; i++)
{
if (this.ColumnModel.Columns[i].Visible)
{
right += this.ColumnModel.Columns[i].Width;
if (right >= 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
/// <summary>
/// Paints the Table's Column headers
/// </summary>
/// <param name="e">A PaintEventArgs that contains the event data</param>
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<this.ColumnModel.Columns.Count; i++)
{
// check that the column isn't hidden
if (this.ColumnModel.Columns[i].Visible)
{
Rectangle colHeaderRect = new Rectangle(xPos, this.BorderWidth, this.ColumnModel.Columns[i].Width, this.HeaderHeight);
// check that the column intersects with the clipping rect
if (e.ClipRectangle.IntersectsWith(colHeaderRect))
{
// move and resize the headerRenderer
this.headerRenderer.Bounds = new Rectangle(xPos, this.BorderWidth, this.ColumnModel.Columns[i].Width, this.HeaderHeight);
// set the clipping area to the header renderers bounds
phea.Graphics.SetClip(Rectangle.Intersect(e.ClipRectangle, this.headerRenderer.Bounds));
// draw the column header
phea.SetColumn(this.ColumnModel.Columns[i]);
phea.SetColumnIndex(i);
phea.SetTable(this);
phea.SetHeaderStyle(this.HeaderStyle);
phea.SetHeaderRect(this.headerRenderer.Bounds);
// let the user get the first crack at painting the header
this.OnBeforePaintHeader(phea);
// only send to the renderer if the user hasn't
// set the handled property
if (!phea.Handled)
{
this.headerRenderer.OnPaintHeader(phea);
}
// let the user have another go
this.OnAfterPaintHeader(phea);
}
// set the next column start position
xPos += this.ColumnModel.Columns[i].Width;
// if the next start poition is past the right edge
// of the clipping rectangle then we don't need to
// draw anymore
if (xPos >= e.ClipRectangle.Right)
{
return;
}
// check is the next column position is past the
// right edge of the table. if it is, get out of
// here as we don't need to draw anymore columns
if (xPos >= this.ClientRectangle.Width)
{
needDummyHeader = false;
break;
}
}
}
if (needDummyHeader)
{
// move and resize the headerRenderer
this.headerRenderer.Bounds = new Rectangle(xPos, this.BorderWidth, this.ClientRectangle.Width - xPos + 2, this.HeaderHeight);
phea.Graphics.SetClip(Rectangle.Intersect(e.ClipRectangle, this.headerRenderer.Bounds));
phea.SetColumn(null);
phea.SetColumnIndex(-1);
phea.SetTable(this);
phea.SetHeaderStyle(this.HeaderStyle);
phea.SetHeaderRect(this.headerRenderer.Bounds);
// let the user get the first crack at painting the header
this.OnBeforePaintHeader(phea);
// only send to the renderer if the user hasn't
// set the handled property
if (!phea.Handled)
{
this.headerRenderer.OnPaintHeader(phea);
}
// let the user have another go
this.OnAfterPaintHeader(phea);
}
}
/// <summary>
/// Raises the BeforePaintHeader event
/// </summary>
/// <param name="e">A PaintCellEventArgs that contains the event data</param>
protected virtual void OnBeforePaintHeader(PaintHeaderEventArgs e)
{
if (BeforePaintHeader != null)
{
BeforePaintHeader(this, e);
}
}
/// <summary>
/// Raises the AfterPaintHeader event
/// </summary>
/// <param name="e">A PaintHeaderEventArgs that contains the event data</param>
protected virtual void OnAfterPaintHeader(PaintHeaderEventArgs e)
{
if (AfterPaintHeader != null)
{
AfterPaintHeader(this, e);
}
}
#endregion
#region Rows
/// <summary>
/// Paints the Table's Rows
/// </summary>
/// <param name="e">A PaintEventArgs that contains the event data</param>
protected void OnPaintRows(PaintEventArgs e)
{
int xPos = this.DisplayRectangle.Left;
int yPos = this.PseudoClientRect.Top;
if (this.HeaderStyle != ColumnHeaderStyle.None)
{
yPos += this.HeaderHeight;
}
Rectangle rowRect = new Rectangle(xPos, yPos, this.ColumnModel.TotalColumnWidth, this.RowHeight);
for (int i=this.TopIndex; i<Math.Min(this.TableModel.Rows.Count, this.TopIndex+this.VisibleRowCount+1); i++)
{
if (rowRect.IntersectsWith(e.ClipRectangle))
{
this.OnPaintRow(e, i, rowRect);
}
else if (rowRect.Top > e.ClipRectangle.Bottom)
{
break;
}
// move to the next row
rowRect.Y += this.RowHeight;
}
//
if (this.IsValidColumn(this.lastSortedColumn))
{
if (rowRect.Y < this.PseudoClientRect.Bottom)
{
Rectangle columnRect = this.ColumnRect(this.lastSortedColumn);
columnRect.Y = rowRect.Y;
columnRect.Height = this.PseudoClientRect.Bottom - rowRect.Y;
if (columnRect.IntersectsWith(e.ClipRectangle))
{
columnRect.Intersect(e.ClipRectangle);
e.Graphics.SetClip(columnRect);
using (SolidBrush brush = new SolidBrush(this.SortedColumnBackColor))
{
e.Graphics.FillRectangle(brush, columnRect);
}
}
}
}
}
/// <summary>
/// Paints the Row at the specified index
/// </summary>
/// <param name="e">A PaintEventArgs that contains the event data</param>
/// <param name="row">The index of the Row to be painted</param>
/// <param name="rowRect">The bounding Rectangle of the Row to be painted</param>
protected void OnPaintRow(PaintEventArgs e, int row, Rectangle rowRect)
{
Rectangle cellRect = new Rectangle(rowRect.X, rowRect.Y, 0, rowRect.Height);
//e.Graphics.SetClip(rowRect);
for (int i=0; i<this.ColumnModel.Columns.Count; i++)
{
if (this.ColumnModel.Columns[i].Visible)
{
cellRect.Width = this.ColumnModel.Columns[i].Width;
if (cellRect.IntersectsWith(e.ClipRectangle))
{
this.OnPaintCell(e, row, i, cellRect);
}
else if (cellRect.Left > e.ClipRectangle.Right)
{
break;
}
cellRect.X += this.ColumnModel.Columns[i].Width;
}
}
}
#endregion
#region Empty Table Text
/// <summary>
/// Paints the message that is displayed when the Table doen't
/// contain any items
/// </summary>
/// <param name="e">A PaintEventArgs that contains the event data</param>
protected void OnPaintEmptyTableText(PaintEventArgs e)
{
if (this.ColumnModel == null || this.RowCount == 0)
{
Rectangle client = this.CellDataRect;
client.Y += 10;
client.Height -= 10;
StringFormat format = new StringFormat();
format.Alignment = StringAlignment.Center;
using (SolidBrush brush = new SolidBrush(this.ForeColor))
{
if (this.DesignMode)
{
if (this.ColumnModel == null || this.TableModel == null)
{
string text = null;
if (this.ColumnModel == null)
{
if (this.TableModel == null)
{
text = "Table does not have a ColumnModel or TableModel";
}
else
{
text = "Table does not have a ColumnModel";
}
}
else if (this.TableModel == null)
{
text = "Table does not have a TableModel";
}
e.Graphics.DrawString(text, this.Font, brush, client, format);
}
else if (this.TableModel != null && this.TableModel.Rows.Count == 0)
{
if (this.NoItemsText != null && this.NoItemsText.Length > 0)
{
e.Graphics.DrawString(this.NoItemsText, this.Font, brush, client, format);
}
}
}
else
{
if (this.NoItemsText != null && this.NoItemsText.Length > 0)
{
e.Graphics.DrawString(this.NoItemsText, this.Font, brush, client, format);
}
}
}
}
}
#endregion
#endregion
#region Rows
/// <summary>
/// Raises the RowPropertyChanged event
/// </summary>
/// <param name="e">A RowEventArgs that contains the event data</param>
protected internal virtual void OnRowPropertyChanged(RowEventArgs e)
{
if (this.CanRaiseEvents)
{
this.InvalidateRow(e.Index);
if (RowPropertyChanged != null)
{
RowPropertyChanged(e.Row, e);
}
}
}
/// <summary>
/// Raises the CellAdded event
/// </summary>
/// <param name="e">A RowEventArgs that contains the event data</param>
protected internal virtual void OnCellAdded(RowEventArgs e)
{
if (this.CanRaiseEvents)
{
this.InvalidateRow(e.Index);
if (CellAdded != null)
{
CellAdded(e.Row, e);
}
}
}
/// <summary>
/// Raises the CellRemoved event
/// </summary>
/// <param name="e">A RowEventArgs that contains the event data</param>
protected internal virtual void OnCellRemoved(RowEventArgs e)
{
if (this.CanRaiseEvents)
{
this.InvalidateRow(e.Index);
if (CellRemoved != null)
{
CellRemoved(this, e);
}
if (e.CellFromIndex == -1 && e.CellToIndex == -1)
{
if (this.FocusedCell.Row == e.Index)
{
this.focusedCell = CellPos.Empty;
}
}
else
{
for (int i=e.CellFromIndex; i<=e.CellToIndex; i++)
{
if (this.FocusedCell.Row == e.Index && this.FocusedCell.Column == i)
{
this.focusedCell = CellPos.Empty;
break;
}
}
}
}
}
#endregion
#region Scrollbars
/// <summary>
/// Occurs when the Table's horizontal scrollbar is scrolled
/// </summary>
/// <param name="sender">The object that Raised the event</param>
/// <param name="e">A ScrollEventArgs that contains the event data</param>
protected void OnHorizontalScroll(object sender, ScrollEventArgs e)
{
// stop editing as the editor doesn't move while
// the table scrolls
if (this.IsEditing)
{
this.StopEditing();
}
if (this.CanRaiseEvents)
{
// non-solid row lines develop artifacts while scrolling
// with the thumb so we invalidate the table once thumb
// scrolling has finished to make them look nice again
if (e.Type == ScrollEventType.ThumbPosition)
{
if (this.GridLineStyle != GridLineStyle.Solid)
{
if (this.GridLines == GridLines.Rows || this.GridLines == GridLines.Both)
{
this.Invalidate(this.CellDataRect, false);
}
}
// same with the focus rect
if (this.FocusedCell != CellPos.Empty)
{
this.Invalidate(this.CellRect(this.FocusedCell), false);
}
}
else
{
this.HorizontalScroll(e.NewValue);
}
}
}
/// <summary>
/// Occurs when the Table's vertical scrollbar is scrolled
/// </summary>
/// <param name="sender">The object that Raised the event</param>
/// <param name="e">A ScrollEventArgs that contains the event data</param>
protected void OnVerticalScroll(object sender, ScrollEventArgs e)
{
// stop editing as the editor doesn't move while
// the table scrolls
if (this.IsEditing)
{
this.StopEditing();
}
if (this.CanRaiseEvents)
{
// non-solid column lines develop artifacts while scrolling
// with the thumb so we invalidate the table once thumb
// scrolling has finished to make them look nice again
if (e.Type == ScrollEventType.ThumbPosition)
{
if (this.GridLineStyle != GridLineStyle.Solid)
{
if (this.GridLines == GridLines.Columns || this.GridLines == GridLines.Both)
{
this.Invalidate(this.CellDataRect, false);
}
}
}
else
{
this.VerticalScroll(e.NewValue);
}
}
}
/// <summary>
/// Handler for a ScrollBars GotFocus event
/// </summary>
/// <param name="sender">The object that raised the event</param>
/// <param name="e">An EventArgs that contains the event data</param>
private void scrollBar_GotFocus(object sender, EventArgs e)
{
// don't let the scrollbars have focus
// (appears to slow scroll speed otherwise)
this.Focus();
}
#endregion
#region Sorting
/// <summary>
/// Raises the BeginSort event
/// </summary>
/// <param name="e">A ColumnEventArgs that contains the event data</param>
protected virtual void OnBeginSort(ColumnEventArgs e)
{
if (BeginSort != null)
{
BeginSort(this, e);
}
}
/// <summary>
/// Raises the EndSort event
/// </summary>
/// <param name="e">A ColumnEventArgs that contains the event data</param>
protected virtual void OnEndSort(ColumnEventArgs e)
{
if (EndSort != null)
{
EndSort(this, e);
}
}
#endregion
#region TableModel
/// <summary>
/// Raises the TableModelChanged event
/// </summary>
/// <param name="e">An EventArgs that contains the event data</param>
protected internal virtual void OnTableModelChanged(EventArgs e)
{
if (this.CanRaiseEvents)
{
this.PerformLayout();
this.Invalidate();
if (TableModelChanged != null)
{
TableModelChanged(this, e);
}
}
}
/// <summary>
/// Raises the SelectionChanged event
/// </summary>
/// <param name="e">A TableModelEventArgs that contains the event data</param>
protected internal virtual void OnSelectionChanged(SelectionEventArgs e)
{
if (this.CanRaiseEvents)
{
if (e.OldSelectionBounds != Rectangle.Empty)
{
Rectangle invalidateRect = new Rectangle(this.DisplayRectToClient(e.OldSelectionBounds.Location), e.OldSelectionBounds.Size);
if (this.HeaderStyle != ColumnHeaderStyle.None)
{
invalidateRect.Y += this.HeaderHeight;
}
this.Invalidate(invalidateRect);
}
if (e.NewSelectionBounds != Rectangle.Empty)
{
Rectangle invalidateRect = new Rectangle(this.DisplayRectToClient(e.NewSelectionBounds.Location), e.NewSelectionBounds.Size);
if (this.HeaderStyle != ColumnHeaderStyle.None)
{
invalidateRect.Y += this.HeaderHeight;
}
this.Invalidate(invalidateRect);
}
if (SelectionChanged != null)
{
SelectionChanged(this, e);
}
}
}
/// <summary>
/// Raises the RowHeightChanged event
/// </summary>
/// <param name="e">An EventArgs that contains the event data</param>
protected internal virtual void OnRowHeightChanged(EventArgs e)
{
if (this.CanRaiseEvents)
{
this.PerformLayout();
this.Invalidate();
if (RowHeightChanged != null)
{
RowHeightChanged(this, e);
}
}
}
/// <summary>
/// Raises the RowAdded event
/// </summary>
/// <param name="e">A TableModelEventArgs that contains the event data</param>
protected internal virtual void OnRowAdded(TableModelEventArgs e)
{
if (this.CanRaiseEvents)
{
this.PerformLayout();
this.Invalidate();
if (RowAdded != null)
{
RowAdded(e.TableModel, e);
}
}
}
/// <summary>
/// Raises the RowRemoved event
/// </summary>
/// <param name="e">A TableModelEventArgs that contains the event data</param>
protected internal virtual void OnRowRemoved(TableModelEventArgs e)
{
if (this.CanRaiseEvents)
{
this.PerformLayout();
this.Invalidate();
if (RowRemoved != null)
{
RowRemoved(e.TableModel, e);
}
}
}
#endregion
#endregion
}
}