|
//------------------------------------------------------------------------------
// <copyright file="DataGrid.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
namespace System.Windows.Forms {
using System.Text;
using System.Runtime.Serialization.Formatters;
using System.Runtime.InteropServices;
using System.Runtime.Remoting;
using System.ComponentModel;
using System.Security;
using System.Security.Permissions;
using System;
using System.Collections;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using System.ComponentModel.Design;
using System.Drawing;
using Microsoft.Win32;
using System.Windows.Forms.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid"]/*' />
/// <devdoc>
/// <para>Displays ADO.NET data in a scrollable
/// grid.</para>
/// </devdoc>
[
ComVisible(true),
ClassInterface(ClassInterfaceType.AutoDispatch),
Designer("System.Windows.Forms.Design.DataGridDesigner, " + AssemblyRef.SystemDesign),
DefaultProperty("DataSource"),
DefaultEvent("Navigate"),
ComplexBindingProperties("DataSource", "DataMember"),
]
public class DataGrid : Control, ISupportInitialize, IDataGridEditingService {
#if DEBUG
internal TraceSwitch DataGridAcc = new TraceSwitch("DataGridAcc", "Trace Windows Forms DataGrid Accessibility");
#else
internal TraceSwitch DataGridAcc = null;
#endif
private const int GRIDSTATE_allowSorting = 0x00000001;
private const int GRIDSTATE_columnHeadersVisible = 0x00000002;
private const int GRIDSTATE_rowHeadersVisible = 0x00000004;
private const int GRIDSTATE_trackColResize = 0x00000008;
private const int GRIDSTATE_trackRowResize = 0x00000010;
private const int GRIDSTATE_isLedgerStyle = 0x00000020;
private const int GRIDSTATE_isFlatMode = 0x00000040;
private const int GRIDSTATE_listHasErrors = 0x00000080;
private const int GRIDSTATE_dragging = 0x00000100;
private const int GRIDSTATE_inListAddNew = 0x00000200;
private const int GRIDSTATE_inDeleteRow = 0x00000400;
private const int GRIDSTATE_canFocus = 0x00000800;
private const int GRIDSTATE_readOnlyMode = 0x00001000;
private const int GRIDSTATE_allowNavigation = 0x00002000;
private const int GRIDSTATE_isNavigating = 0x00004000;
private const int GRIDSTATE_isEditing = 0x00008000;
private const int GRIDSTATE_editControlChanging = 0x00010000;
private const int GRIDSTATE_isScrolling = 0x00020000;
private const int GRIDSTATE_overCaption = 0x00040000;
private const int GRIDSTATE_childLinkFocused = 0x00080000;
private const int GRIDSTATE_inAddNewRow = 0x00100000;
private const int GRIDSTATE_inSetListManager = 0x00200000;
private const int GRIDSTATE_metaDataChanged = 0x00400000;
private const int GRIDSTATE_exceptionInPaint = 0x00800000;
private const int GRIDSTATE_layoutSuspended = 0x01000000;
// PERF: take all the bools and put them into a state variable
private System.Collections.Specialized.BitVector32 gridState; // see GRIDSTATE_ consts above
// for column widths
private const int NumRowsForAutoResize = 10;
private const int errorRowBitmapWidth = 15;
private const DataGridParentRowsLabelStyle defaultParentRowsLabelStyle = DataGridParentRowsLabelStyle.Both;
private const BorderStyle defaultBorderStyle = BorderStyle.Fixed3D;
private const bool defaultCaptionVisible = true;
private const bool defaultParentRowsVisible = true;
private DataGridTableStyle defaultTableStyle = new DataGridTableStyle(true);
// private bool allowSorting = true;
private SolidBrush alternatingBackBrush = DefaultAlternatingBackBrush;
// private bool columnHeadersVisible = true;
private SolidBrush gridLineBrush = DefaultGridLineBrush;
private const DataGridLineStyle defaultGridLineStyle = DataGridLineStyle.Solid;
private DataGridLineStyle gridLineStyle = defaultGridLineStyle;
private SolidBrush headerBackBrush = DefaultHeaderBackBrush;
private Font headerFont = null; // this is ambient property to Font value
private SolidBrush headerForeBrush = DefaultHeaderForeBrush;
private Pen headerForePen = DefaultHeaderForePen;
private SolidBrush linkBrush = DefaultLinkBrush;
private const int defaultPreferredColumnWidth = 75;
private int preferredColumnWidth = defaultPreferredColumnWidth;
private static int defaultFontHeight = Control.DefaultFont.Height;
private int prefferedRowHeight = defaultFontHeight + 3;
// private bool rowHeadersVisible = true;
private const int defaultRowHeaderWidth = 35;
private int rowHeaderWidth = defaultRowHeaderWidth;
private int minRowHeaderWidth;
private SolidBrush selectionBackBrush = DefaultSelectionBackBrush;
private SolidBrush selectionForeBrush = DefaultSelectionForeBrush;
// parent rows
//
private DataGridParentRows parentRows = null;
// Set_ListManager uses the originalState to determine
// if the grid should disconnect from all the MetaDataChangedEvents
// keep "originalState != null" when navigating back and forth in the grid
// and use Add/RemoveMetaDataChanged methods.
private DataGridState originalState = null;
// ui state
//
// Don't use dataGridRows, use the accessor!!!
private DataGridRow[] dataGridRows = new DataGridRow[0];
private int dataGridRowsLength = 0;
// for toolTip
private int toolTipId = 0;
private DataGridToolTip toolTipProvider = null;
private DataGridAddNewRow addNewRow = null;
private LayoutData layout = new LayoutData();
private NativeMethods.RECT[] cachedScrollableRegion = null;
// header namespace goo
//
// these are actually get/set by ColumnBehavior
internal bool allowColumnResize = true;
internal bool allowRowResize = true;
internal DataGridParentRowsLabelStyle parentRowsLabels = defaultParentRowsLabelStyle;
// information for col/row resizing
// private bool trackColResize = false;
private int trackColAnchor = 0;
private int trackColumn = 0;
// private bool trackRowResize = false;
private int trackRowAnchor = 0;
private int trackRow = 0;
private PropertyDescriptor trackColumnHeader = null;
private MouseEventArgs lastSplitBar = null;
// private bool isLedgerStyle = true;
// private bool isFlatMode = false;
private Font linkFont = null;
private SolidBrush backBrush = DefaultBackBrush;
private SolidBrush foreBrush = DefaultForeBrush;
private SolidBrush backgroundBrush = DefaultBackgroundBrush;
// font cacheing info
private int fontHeight = -1;
private int linkFontHeight = -1;
private int captionFontHeight = -1;
private int headerFontHeight = -1;
// the preffered height of the row.
// if the list has items with errors
// private bool listHasErrors = false;
// caption
private DataGridCaption caption;
// Border
//
private BorderStyle borderStyle;
// data binding
//
private object dataSource = null;
private string dataMember = "";
private CurrencyManager listManager = null;
// currently focused control
// we want to unparent it either when rebinding the grid or when the grid is disposed
Control toBeDisposedEditingControl = null;
// persistent data state
//
internal GridTableStylesCollection dataGridTables = null;
// SET myGridTable in SetDataGridTable ONLY
internal DataGridTableStyle myGridTable = null;
internal bool checkHierarchy = true;
internal bool inInit = false;
// Selection
internal int currentRow = 0;
internal int currentCol = 0;
private int numSelectedRows = 0;
private int lastRowSelected = -1;
// dragging:
// private bool dragging = false;
// addNewRow
// private bool inAddNewRow = false;
// delete Row
// private bool inDeleteRow = false;
// when we leave, we call CommitEdit
// if we leave, then do not focus the dataGrid.
// so we can't focus the grid at the following moments:
// 1. while processing the OnLayout event
// 2. while processing the OnLeave event
// private bool canFocus = true;
// for CurrentCell
#if DEBUG
private bool inDataSource_PositionChanged = false;
#endif // DEBUG
// Policy
// private bool readOnlyMode = false;
private Policy policy = new Policy();
// private bool allowNavigation = true;
// editing
// private bool isNavigating = false;
// private bool isEditing = false;
// private bool editControlChanging = false;
private DataGridColumnStyle editColumn = null;
private DataGridRow editRow = null;
// scrolling
//
private ScrollBar horizScrollBar = new HScrollBar();
private ScrollBar vertScrollBar = new VScrollBar();
// the sum of the widths of the columns preceding the firstVisibleColumn
//
private int horizontalOffset = 0;
// the number of pixels of the firstVisibleColumn which are not visible
//
private int negOffset = 0;
private int wheelDelta = 0;
// private bool isScrolling = false;
// Visibility
//
internal int firstVisibleRow = 0;
internal int firstVisibleCol = 0;
private int numVisibleRows = 0;
// the number of columns which are visible
private int numVisibleCols = 0;
private int numTotallyVisibleRows = 0;
// lastTotallyVisibleCol == -1 means that the data grid does not show any column in its entirety
private int lastTotallyVisibleCol = 0;
// mouse move hot-tracking
//
private int oldRow = -1;
// private bool overCaption = true;
// child relationships focused
//
// private bool childLinkFocused = false;
// private static readonly object EVENT_COLUMNHEADERCLICK = new object();
private static readonly object EVENT_CURRENTCELLCHANGED = new object();
// private static readonly object EVENT_COLUMNRESIZE = new object();
// private static readonly object EVENT_LINKCLICKED = new object();
private static readonly object EVENT_NODECLICKED = new object();
// private static readonly object EVENT_ROWRESIZE = new object();
private static readonly object EVENT_SCROLL = new object();
private static readonly object EVENT_BACKBUTTONCLICK = new object();
private static readonly object EVENT_DOWNBUTTONCLICK = new object();
// event handlers
//
private ItemChangedEventHandler itemChangedHandler;
private EventHandler positionChangedHandler;
private EventHandler currentChangedHandler;
private EventHandler metaDataChangedHandler;
// we have to know when the collection of dataGridTableStyles changes
private CollectionChangeEventHandler dataGridTableStylesCollectionChanged;
private EventHandler backButtonHandler;
private EventHandler downButtonHandler;
private NavigateEventHandler onNavigate;
private EventHandler onRowHeaderClick;
// forDebug
//
// private int forDebug = 0;
// =-----------------------------------------------------------------
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.DataGrid"]/*' />
/// <devdoc>
/// <para>Initializes a new instance of the <see cref='System.Windows.Forms.DataGrid'/>
/// class.</para>
/// </devdoc>
public DataGrid() : base() {
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.Opaque, false);
SetStyle(ControlStyles.SupportsTransparentBackColor, false);
SetStyle(ControlStyles.UserMouse, true);
this.gridState = new System.Collections.Specialized.BitVector32(0x00042827);
dataGridTables = new GridTableStylesCollection(this);
layout = CreateInitialLayoutState();
parentRows = new DataGridParentRows(this);
horizScrollBar.Top = ClientRectangle.Height - horizScrollBar.Height;
horizScrollBar.Left = 0;
horizScrollBar.Visible = false;
horizScrollBar.Scroll += new ScrollEventHandler(GridHScrolled);
Controls.Add(horizScrollBar);
vertScrollBar.Top = 0;
vertScrollBar.Left = ClientRectangle.Width - vertScrollBar.Width;
vertScrollBar.Visible = false;
vertScrollBar.Scroll += new ScrollEventHandler(GridVScrolled);
Controls.Add(vertScrollBar);
BackColor = DefaultBackBrush.Color;
ForeColor = DefaultForeBrush.Color;
borderStyle = defaultBorderStyle;
// create the event handlers
//
currentChangedHandler = new EventHandler(DataSource_RowChanged);
positionChangedHandler = new EventHandler(DataSource_PositionChanged);
itemChangedHandler = new ItemChangedEventHandler(DataSource_ItemChanged);
metaDataChangedHandler = new EventHandler(DataSource_MetaDataChanged);
dataGridTableStylesCollectionChanged = new CollectionChangeEventHandler(TableStylesCollectionChanged);
this.dataGridTables.CollectionChanged += dataGridTableStylesCollectionChanged;
SetDataGridTable(this.defaultTableStyle, true);
backButtonHandler = new EventHandler(OnBackButtonClicked);
downButtonHandler = new EventHandler(OnShowParentDetailsButtonClicked);
caption = new DataGridCaption(this);
caption.BackwardClicked += backButtonHandler;
caption.DownClicked += downButtonHandler;
RecalculateFonts();
Size = new Size(130, 80);
Invalidate();
PerformLayout();
}
// =------------------------------------------------------------------
// = Properties
// =------------------------------------------------------------------
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.AllowSorting"]/*' />
/// <devdoc>
/// <para>Gets or sets a value indicating whether the grid can be resorted by clicking on
/// a column header.</para>
/// </devdoc>
[
SRCategory(SR.CatBehavior),
DefaultValue(true),
SRDescription(SR.DataGridAllowSortingDescr)
]
public bool AllowSorting {
get {
return gridState[GRIDSTATE_allowSorting];
}
set {
if (AllowSorting != value) {
gridState[GRIDSTATE_allowSorting] = value;
if (!value && this.listManager != null) {
IList list = this.listManager.List;
if (list is IBindingList)
((IBindingList) list).RemoveSort();
}
}
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.AlternatingBackColor"]/*' />
[
SRCategory(SR.CatColors),
SRDescription(SR.DataGridAlternatingBackColorDescr)
]
public Color AlternatingBackColor {
get {
return alternatingBackBrush.Color;
}
set {
if (value.IsEmpty) {
throw new ArgumentException(SR.GetString(SR.DataGridEmptyColor,
"AlternatingBackColor"));
}
if (IsTransparentColor(value))
throw new ArgumentException(SR.GetString(SR.DataGridTransparentAlternatingBackColorNotAllowed));
if (!alternatingBackBrush.Color.Equals(value)) {
alternatingBackBrush = new SolidBrush(value);
InvalidateInside();
}
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ResetAlternatingBackColor"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public void ResetAlternatingBackColor() {
if (ShouldSerializeAlternatingBackColor()) {
AlternatingBackColor = DefaultAlternatingBackBrush.Color;
InvalidateInside();
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ShouldSerializeAlternatingBackColor"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected virtual bool ShouldSerializeAlternatingBackColor()
{
return !AlternatingBackBrush.Equals(DefaultAlternatingBackBrush);
}
internal Brush AlternatingBackBrush {
get {
return alternatingBackBrush;
}
}
// overrode those properties just to move the BackColor and the ForeColor
// from the Appearance group onto the Color Group
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.BackColor"]/*' />
/// <devdoc>
/// <para>Gets or sets the background color of the grid.</para>
/// </devdoc>
[
SRCategory(SR.CatColors),
SRDescription(SR.ControlBackColorDescr)
]
public override Color BackColor {
// overrode those properties just to move the BackColor and the ForeColor
// from the Appearance group onto the Color Group
get {
return base.BackColor;
}
set {
if (IsTransparentColor(value))
throw new ArgumentException(SR.GetString(SR.DataGridTransparentBackColorNotAllowed));
base.BackColor = value;
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ResetBackColor"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public override void ResetBackColor() {
if (!this.BackColor.Equals(DefaultBackBrush.Color)) {
this.BackColor = DefaultBackBrush.Color;
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ForeColor"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[
SRCategory(SR.CatColors),
SRDescription(SR.ControlForeColorDescr)
]
public override Color ForeColor {
get {
return base.ForeColor;
}
set {
base.ForeColor = value;
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ResetForeColor"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public override void ResetForeColor() {
if (!this.ForeColor.Equals(DefaultForeBrush.Color)) {
this.ForeColor = DefaultForeBrush.Color;
}
}
/// <devdoc>
/// <para>
/// Gets a value
/// indicating whether the <see cref='System.Windows.Forms.DataGrid.AlternatingBackColor'/> property should be
/// persisted.
/// </para>
/// </devdoc>
internal SolidBrush BackBrush {
get {
return backBrush;
}
}
internal SolidBrush ForeBrush {
get {
return foreBrush;
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.BorderStyle"]/*' />
/// <devdoc>
/// <para>
/// Gets or
/// sets the border style.
/// </para>
/// </devdoc>
[
SRCategory(SR.CatAppearance),
DefaultValue(defaultBorderStyle),
DispId(NativeMethods.ActiveX.DISPID_BORDERSTYLE),
SRDescription(SR.DataGridBorderStyleDescr)
]
public BorderStyle BorderStyle {
get {
return borderStyle;
}
set {
//valid values are 0x0 to 0x2.
if (!ClientUtils.IsEnumValid(value, (int)value, (int)BorderStyle.None, (int)BorderStyle.Fixed3D)){
throw new InvalidEnumArgumentException("value", (int)value, typeof(BorderStyle));
}
if (borderStyle != value) {
borderStyle = value;
PerformLayout();
Invalidate();
OnBorderStyleChanged(EventArgs.Empty);
}
}
}
private static readonly object EVENT_BORDERSTYLECHANGED = new object();
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.BorderStyleChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatPropertyChanged), SRDescription(SR.DataGridOnBorderStyleChangedDescr)]
public event EventHandler BorderStyleChanged {
add {
Events.AddHandler(EVENT_BORDERSTYLECHANGED, value);
}
remove {
Events.RemoveHandler(EVENT_BORDERSTYLECHANGED, value);
}
}
private int BorderWidth {
get {
if (BorderStyle == BorderStyle.Fixed3D) {
return SystemInformation.Border3DSize.Width;
}
else if (BorderStyle == BorderStyle.FixedSingle) {
return 2;
}
else {
return 0;
}
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.DefaultSize"]/*' />
protected override Size DefaultSize {
get {
return new Size(130, 80);
}
}
private static SolidBrush DefaultSelectionBackBrush {
get {
return (SolidBrush)SystemBrushes.ActiveCaption;
}
}
private static SolidBrush DefaultSelectionForeBrush {
get {
return (SolidBrush)SystemBrushes.ActiveCaptionText;
}
}
internal static SolidBrush DefaultBackBrush {
get {
return (SolidBrush)SystemBrushes.Window;
}
}
internal static SolidBrush DefaultForeBrush {
get {
return (SolidBrush)SystemBrushes.WindowText;
}
}
private static SolidBrush DefaultBackgroundBrush {
get {
return (SolidBrush)SystemBrushes.AppWorkspace;
}
}
internal static SolidBrush DefaultParentRowsForeBrush {
get {
return (SolidBrush)SystemBrushes.WindowText;
}
}
internal static SolidBrush DefaultParentRowsBackBrush {
get {
return (SolidBrush)SystemBrushes.Control;
}
}
internal static SolidBrush DefaultAlternatingBackBrush {
get {
return (SolidBrush)SystemBrushes.Window;
}
}
private static SolidBrush DefaultGridLineBrush {
get {
return (SolidBrush)SystemBrushes.Control;
}
}
private static SolidBrush DefaultHeaderBackBrush {
get {
return (SolidBrush)SystemBrushes.Control;
}
}
private static SolidBrush DefaultHeaderForeBrush {
get {
return (SolidBrush)SystemBrushes.ControlText;
}
}
private static Pen DefaultHeaderForePen {
get {
return new Pen(SystemColors.ControlText);
}
}
private static SolidBrush DefaultLinkBrush {
get {
return (SolidBrush)SystemBrushes.HotTrack;
}
}
private bool ListHasErrors {
get {
return gridState[GRIDSTATE_listHasErrors];
}
set {
if (ListHasErrors != value)
{
gridState[GRIDSTATE_listHasErrors] = value;
ComputeMinimumRowHeaderWidth();
if (!layout.RowHeadersVisible)
return;
if (value) {
if (myGridTable.IsDefault)
this.RowHeaderWidth += errorRowBitmapWidth;
else
this.myGridTable.RowHeaderWidth += errorRowBitmapWidth;
}
else {
if (myGridTable.IsDefault)
this.RowHeaderWidth -= errorRowBitmapWidth;
else
this.myGridTable.RowHeaderWidth -= errorRowBitmapWidth;
}
}
}
}
private bool Bound {
get {
return !(listManager == null || myGridTable == null);
}
}
internal DataGridCaption Caption {
get {
return caption;
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.CaptionBackColor"]/*' />
/// <devdoc>
/// <para>
/// Gets or sets the background color of the caption area.
/// </para>
/// </devdoc>
[
SRCategory(SR.CatColors),
SRDescription(SR.DataGridCaptionBackColorDescr)
]
public Color CaptionBackColor {
get {
return Caption.BackColor;
}
set {
if (IsTransparentColor(value))
throw new ArgumentException(SR.GetString(SR.DataGridTransparentCaptionBackColorNotAllowed));
Caption.BackColor = value;
}
}
private void ResetCaptionBackColor() {
Caption.ResetBackColor();
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ShouldSerializeCaptionBackColor"]/*' />
/// <devdoc>
/// <para>
/// Gets a value
/// indicating whether the <see cref='System.Windows.Forms.DataGrid.CaptionBackColor'/> property should be
/// persisted.
/// </para>
/// </devdoc>
protected virtual bool ShouldSerializeCaptionBackColor()
{
return Caption.ShouldSerializeBackColor();
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.CaptionForeColor"]/*' />
/// <devdoc>
/// <para>
/// Gets
/// or sets the foreground color
/// of the caption area.
/// </para>
/// </devdoc>
[
SRCategory(SR.CatColors),
SRDescription(SR.DataGridCaptionForeColorDescr)
]
public Color CaptionForeColor {
get {
return Caption.ForeColor;
}
set {
Caption.ForeColor = value;
}
}
private void ResetCaptionForeColor() {
Caption.ResetForeColor();
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ShouldSerializeCaptionForeColor"]/*' />
/// <devdoc>
/// <para>
/// Gets a value
/// indicating whether the <see cref='System.Windows.Forms.DataGrid.CaptionForeColor'/> property should be
/// persisted.
/// </para>
/// </devdoc>
protected virtual bool ShouldSerializeCaptionForeColor()
{
return Caption.ShouldSerializeForeColor();
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.CaptionFont"]/*' />
/// <devdoc>
/// <para>
/// Gets or sets the font of the grid's caption.
/// </para>
/// </devdoc>
[
SRCategory(SR.CatAppearance),
Localizable(true),
AmbientValue(null),
SRDescription(SR.DataGridCaptionFontDescr)
]
public Font CaptionFont {
get {
return Caption.Font;
}
set {
Caption.Font = value;
}
}
/// <devdoc>
/// <para>
/// Gets a value indicating whether the
/// caption's font is persisted.
/// </para>
/// </devdoc>
private bool ShouldSerializeCaptionFont() {
return Caption.ShouldSerializeFont();
}
private void ResetCaptionFont() {
Caption.ResetFont();
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.CaptionText"]/*' />
/// <devdoc>
/// <para>
/// Gets or sets the text of the grid's caption.
/// </para>
/// </devdoc>
[
SRCategory(SR.CatAppearance),
DefaultValue(""),
Localizable(true),
SRDescription(SR.DataGridCaptionTextDescr)
]
public string CaptionText {
get {
return Caption.Text;
}
set {
Caption.Text = value;
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.CaptionVisible"]/*' />
/// <devdoc>
/// <para>
/// Gets or sets a value that indicates
/// whether the grid's caption is visible.
/// </para>
/// </devdoc>
[
DefaultValue(true),
SRCategory(SR.CatDisplay),
SRDescription(SR.DataGridCaptionVisibleDescr)
]
public bool CaptionVisible {
get {
return layout.CaptionVisible;
}
set {
if (layout.CaptionVisible != value) {
layout.CaptionVisible = value;
PerformLayout();
Invalidate();
OnCaptionVisibleChanged(EventArgs.Empty);
}
}
}
private static readonly object EVENT_CAPTIONVISIBLECHANGED = new object();
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.CaptionVisibleChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatPropertyChanged), SRDescription(SR.DataGridOnCaptionVisibleChangedDescr)]
public event EventHandler CaptionVisibleChanged {
add {
Events.AddHandler(EVENT_CAPTIONVISIBLECHANGED, value);
}
remove {
Events.RemoveHandler(EVENT_CAPTIONVISIBLECHANGED, value);
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.CurrentCell"]/*' />
/// <devdoc>
/// <para>
/// Gets or sets which cell has the focus. Not available at design time.
/// </para>
/// </devdoc>
[
Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
SRDescription(SR.DataGridCurrentCellDescr)
]
public DataGridCell CurrentCell {
get {
return new DataGridCell(currentRow, currentCol);
}
set {
// if the OnLayout event was not set in the grid, then we can't
// reliably set the currentCell on the grid.
if (layout.dirty)
throw new ArgumentException(SR.GetString(SR.DataGridSettingCurrentCellNotGood));
if (value.RowNumber == currentRow && value.ColumnNumber == currentCol)
return;
// should we throw an exception, maybe?
if (DataGridRowsLength == 0 || this.myGridTable.GridColumnStyles == null || this.myGridTable.GridColumnStyles.Count == 0)
return;
EnsureBound();
int currentRowSaved = currentRow;
int currentColSaved = currentCol;
bool wasEditing = gridState[GRIDSTATE_isEditing];
bool cellChanged = false;
// if the position in the listManager changed under the DataGrid,
// then do not edit after setting the current cell
bool doNotEdit = false;
int newCol = value.ColumnNumber;
int newRow = value.RowNumber;
String errorMessage = null;
try {
int columnCount = myGridTable.GridColumnStyles.Count;
if (newCol < 0)
newCol = 0;
if (newCol >= columnCount)
newCol = columnCount - 1;
int localGridRowsLength = DataGridRowsLength;
DataGridRow[] localGridRows = DataGridRows;
if (newRow < 0) {
newRow = 0;
}
if (newRow >= localGridRowsLength) {
newRow = localGridRowsLength - 1;
}
// Current Column changing
//
if (currentCol != newCol) {
cellChanged = true;
int currentListManagerPosition = this.ListManager.Position;
int currentListManagerCount = this.ListManager.List.Count;
EndEdit();
if (this.ListManager.Position != currentListManagerPosition ||
currentListManagerCount != this.ListManager.List.Count) {
// EndEdit changed the list.
// Reset the data grid rows and the current row inside the datagrid.
// And then exit the method.
RecreateDataGridRows();
if (this.ListManager.List.Count > 0) {
this.currentRow = this.ListManager.Position;
Edit();
} else {
this.currentRow = -1;
}
return;
}
currentCol = newCol;
InvalidateRow(currentRow);
}
// Current Row changing
//
if (currentRow != newRow) {
cellChanged = true;
int currentListManagerPosition = this.ListManager.Position;
int currentListManagerCount = this.ListManager.List.Count;
EndEdit();
if (this.ListManager.Position != currentListManagerPosition ||
currentListManagerCount != this.ListManager.List.Count) {
// EndEdit changed the list.
// Reset the data grid rows and the current row inside the datagrid.
// And then exit the method.
RecreateDataGridRows();
if (this.ListManager.List.Count > 0) {
this.currentRow = this.ListManager.Position;
Edit();
} else {
this.currentRow = -1;
}
return;
}
if (currentRow < localGridRowsLength)
localGridRows[currentRow].OnRowLeave();
localGridRows[newRow].OnRowEnter();
currentRow = newRow;
if (currentRowSaved < localGridRowsLength)
InvalidateRow(currentRowSaved);
InvalidateRow(currentRow);
if (currentRowSaved != listManager.Position) {
// not in sync
#if DEBUG
Debug.Assert(inDataSource_PositionChanged, "currentRow and listManager.Position can be out of sync only when the listManager changes its position under the DataGrid or when navigating back");
Debug.Assert(ListManager.Position == currentRow || listManager.Position == -1, "DataSource_PositionChanged changes the position in the grid to the position in the listManager");
#endif //DEBUG
doNotEdit = true;
if (gridState[GRIDSTATE_isEditing])
AbortEdit();
} else if (gridState[GRIDSTATE_inAddNewRow]) {
#if DEBUG
int currentRowCount = this.DataGridRowsLength;
#endif // debug
// cancelCurrentEdit will change the position in the list
// to the last element in the list. and the grid will get an on position changed
// event, and will set the current cell to the last element in the dataSource.
// so unhook the PositionChanged event from the listManager;
this.ListManager.PositionChanged -= positionChangedHandler;
this.ListManager.CancelCurrentEdit();
this.ListManager.Position = this.currentRow;
this.ListManager.PositionChanged += positionChangedHandler;
#if DEBUG
Debug.Assert(currentRowSaved > currentRow, "we can only go up when we are inAddNewRow");
Debug.Assert(currentRowCount == this.DataGridRowsLength, "the number of rows in the dataGrid should not change");
Debug.Assert(currentRowCount == this.ListManager.Count + 1, "the listManager should have one less record");
#endif // debug
localGridRows[DataGridRowsLength - 1] = new DataGridAddNewRow(this, this.myGridTable, DataGridRowsLength-1);
SetDataGridRows(localGridRows, DataGridRowsLength);
gridState[GRIDSTATE_inAddNewRow] = false;
} else {
ListManager.EndCurrentEdit();
// some special care must be given when setting the
// position in the listManager.
// if EndCurrentEdit() deleted the current row
// ( because of some filtering problem, say )
// then we cannot go over the last row
//
if (localGridRowsLength != DataGridRowsLength)
{
Debug.Assert(localGridRowsLength == DataGridRowsLength + 1, "this is the only change that could have happened");
currentRow = (currentRow == localGridRowsLength - 1) ? DataGridRowsLength - 1 : currentRow;
}
if (currentRow == dataGridRowsLength - 1 && policy.AllowAdd) {
// it may be case ( see previous comment )
// that listManager.EndCurrentEdit changed the number of rows
// in the grid. in this case, we should not be using the old
// localGridRows in our assertion, cause they are outdated now
//
Debug.Assert(this.DataGridRows[currentRow] is DataGridAddNewRow, "the last row is the DataGridAddNewRow");
this.AddNewRow();
Debug.Assert(ListManager.Position == currentRow || listManager.Position == -1, "the listManager should be positioned at the last row");
} else {
ListManager.Position = currentRow;
}
}
}
}
catch (Exception e) {
errorMessage = e.Message;
}
if (errorMessage != null) {
DialogResult result = RTLAwareMessageBox.Show(null,
SR.GetString(SR.DataGridPushedIncorrectValueIntoColumn, errorMessage),
SR.GetString(SR.DataGridErrorMessageBoxCaption), MessageBoxButtons.YesNo,
MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, 0);
if (result == DialogResult.Yes) {
currentRow = currentRowSaved;
currentCol = currentColSaved;
Debug.Assert(currentRow == ListManager.Position || listManager.Position == -1, "the position in the list manager (" + ListManager.Position + ") is out of sync with the currentRow (" + currentRow + ")" + " and the exception is '" + errorMessage + "'");
// this will make sure the newRow will not paint the
// row selector.
InvalidateRowHeader(newRow);
// also, make sure that we get the row selector on the currentrow, too
InvalidateRowHeader(currentRow);
if (wasEditing)
Edit();
} else {
// if the user committed a row that used to be addNewRow and the backEnd rejects it,
// and then it tries to navigate down then we should stay in the addNewRow
// in this particular scenario, CancelCurrentEdit will cause the last row to be deleted,
// and this will ultimately call InvalidateRow w/ a row number larger than the number of rows
// so set the currentRow here:
if (currentRow == this.DataGridRowsLength - 1 && currentRowSaved == this.DataGridRowsLength - 2 && DataGridRows[currentRow] is DataGridAddNewRow)
newRow = currentRowSaved;
currentRow = newRow;
Debug.Assert(result == DialogResult.No, "we only put cancel and ok on the error message box");
this.listManager.PositionChanged -= positionChangedHandler;
this.listManager.CancelCurrentEdit();
this.listManager.Position = newRow;
this.listManager.PositionChanged += positionChangedHandler;
currentRow = newRow;
currentCol = newCol;
if (wasEditing)
Edit();
}
}
if (cellChanged) {
EnsureVisible(currentRow, currentCol);
OnCurrentCellChanged(EventArgs.Empty);
// if the user changed the current cell using the UI, edit the new cell
// but if the user changed the current cell by changing the position in the
// listManager, then do not continue the edit
//
if (!doNotEdit)
{
#if DEBUG
Debug.Assert(!inDataSource_PositionChanged, "if the user changed the current cell using the UI, then do not edit");
#endif // debug
Edit();
}
AccessibilityNotifyClients(AccessibleEvents.Focus, CurrentCellAccIndex);
AccessibilityNotifyClients(AccessibleEvents.Selection, CurrentCellAccIndex);
}
Debug.Assert(currentRow == ListManager.Position || listManager.Position == -1, "the position in the list manager is out of sync with the currentRow");
}
}
internal int CurrentCellAccIndex {
get {
int currentCellAccIndex = 0;
currentCellAccIndex ++; // ParentRowsAccessibleObject
currentCellAccIndex += this.myGridTable.GridColumnStyles.Count; // ColumnHeaderAccessibleObject
currentCellAccIndex += this.DataGridRows.Length; // DataGridRowAccessibleObject
if (this.horizScrollBar.Visible) // Horizontal Scroll Bar Accessible Object
currentCellAccIndex ++;
if (this.vertScrollBar.Visible) // Vertical Scroll Bar Accessible Object
currentCellAccIndex ++;
currentCellAccIndex += (this.currentRow * this.myGridTable.GridColumnStyles.Count) + this.currentCol;
return currentCellAccIndex;
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.CurrentCellChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatPropertyChanged), SRDescription(SR.DataGridOnCurrentCellChangedDescr)]
public event EventHandler CurrentCellChanged {
add {
Events.AddHandler(EVENT_CURRENTCELLCHANGED, value);
}
remove {
Events.RemoveHandler(EVENT_CURRENTCELLCHANGED, value);
}
}
private int CurrentColumn {
get {
return CurrentCell.ColumnNumber;
}
set {
this.CurrentCell = new DataGridCell(currentRow, value);
}
}
private int CurrentRow {
get {
return CurrentCell.RowNumber;
}
set {
CurrentCell = new DataGridCell(value, currentCol);
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.SelectionBackColor"]/*' />
[
SRCategory(SR.CatColors),
SRDescription(SR.DataGridSelectionBackColorDescr)
]
public Color SelectionBackColor {
get {
return selectionBackBrush.Color;
}
set {
if (value.IsEmpty)
throw new ArgumentException(SR.GetString(SR.DataGridEmptyColor, "SelectionBackColor"));
if (IsTransparentColor(value))
throw new ArgumentException(SR.GetString(SR.DataGridTransparentSelectionBackColorNotAllowed));
if (!value.Equals(selectionBackBrush.Color)) {
selectionBackBrush = new SolidBrush(value);
InvalidateInside();
}
}
}
internal SolidBrush SelectionBackBrush {
get {
return this.selectionBackBrush;
}
}
internal SolidBrush SelectionForeBrush {
get {
return this.selectionForeBrush;
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ShouldSerializeSelectionBackColor"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected bool ShouldSerializeSelectionBackColor()
{
return !DefaultSelectionBackBrush.Equals(selectionBackBrush);
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ResetSelectionBackColor"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public void ResetSelectionBackColor() {
if (ShouldSerializeSelectionBackColor())
SelectionBackColor = DefaultSelectionBackBrush.Color;
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.SelectionForeColor"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[
SRCategory(SR.CatColors),
SRDescription(SR.DataGridSelectionForeColorDescr)
]
public Color SelectionForeColor {
get {
return selectionForeBrush.Color;
}
set {
if (value.IsEmpty)
throw new ArgumentException(SR.GetString(SR.DataGridEmptyColor, "SelectionForeColor"));
if (!value.Equals(selectionForeBrush.Color)) {
selectionForeBrush = new SolidBrush(value);
InvalidateInside();
}
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ShouldSerializeSelectionForeColor"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected virtual bool ShouldSerializeSelectionForeColor()
{
return !SelectionForeBrush.Equals(DefaultSelectionForeBrush);
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ResetSelectionForeColor"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public void ResetSelectionForeColor() {
if (ShouldSerializeSelectionForeColor())
SelectionForeColor = DefaultSelectionForeBrush.Color;
}
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
internal override bool ShouldSerializeForeColor()
{
return !DefaultForeBrush.Color.Equals(this.ForeColor);
}
/// <devdoc>
/// <para>Indicates whether the <see cref='System.Windows.Forms.DataGrid.BackColor'/> property should be
/// persisted.</para>
/// </devdoc>
internal override bool ShouldSerializeBackColor()
{
return !DefaultBackBrush.Color.Equals(this.BackColor);
}
// Don't use dataGridRows, use the accessor!!!
internal DataGridRow[] DataGridRows {
get {
if (dataGridRows == null)
CreateDataGridRows();
return dataGridRows;
}
}
// ToolTipping
internal DataGridToolTip ToolTipProvider {
get {
return toolTipProvider;
}
}
internal int ToolTipId {
get {
return toolTipId;
}
set {
toolTipId = value;
}
}
private void ResetToolTip()
{
// remove all the tool tips which are stored
for (int i = 0; i < ToolTipId; i ++) {
ToolTipProvider.RemoveToolTip(new IntPtr(i));
}
// add toolTips for the backButton and
// details button on the caption.
if (!this.parentRows.IsEmpty()) {
bool alignRight = this.isRightToLeft();
int detailsButtonWidth = this.Caption.GetDetailsButtonWidth();
Rectangle backButton = this.Caption.GetBackButtonRect(this.layout.Caption, alignRight, detailsButtonWidth);
Rectangle detailsButton = this.Caption.GetDetailsButtonRect(this.layout.Caption, alignRight);
// mirror the buttons wrt RTL property
backButton.X = MirrorRectangle(backButton, layout.Inside, isRightToLeft());
detailsButton.X = MirrorRectangle(detailsButton, layout.Inside, isRightToLeft());
ToolTipProvider.AddToolTip(SR.GetString(SR.DataGridCaptionBackButtonToolTip), new IntPtr(0), backButton);
ToolTipProvider.AddToolTip(SR.GetString(SR.DataGridCaptionDetailsButtonToolTip), new IntPtr(1), detailsButton);
ToolTipId = 2;
} else {
ToolTipId = 0;
}
}
/// <devdoc>
/// Given a cursor, this will Create the right DataGridRows
/// </devdoc>
private void CreateDataGridRows() {
CurrencyManager listManager = ListManager;
DataGridTableStyle dgt = this.myGridTable;
InitializeColumnWidths();
if (listManager == null) {
SetDataGridRows(new DataGridRow[0], 0);
return;
}
int nDataGridRows = listManager.Count;
if (policy.AllowAdd)
nDataGridRows++;
DataGridRow[] rows = new DataGridRow[nDataGridRows];
for (int r = 0; r < listManager.Count; r++) {
rows[r] = new DataGridRelationshipRow(this, dgt,r);
}
if (policy.AllowAdd) {
this.addNewRow = new DataGridAddNewRow(this, dgt, nDataGridRows - 1);
rows[nDataGridRows - 1] = addNewRow;
}
else {
addNewRow = null;
}
// SetDataGridRows(rows, rows.Length);
SetDataGridRows(rows, nDataGridRows);
}
private void RecreateDataGridRows() {
int nDataGridRows = 0;
CurrencyManager listManager = ListManager;
if (listManager != null) {
nDataGridRows = listManager.Count;
if (policy.AllowAdd) {
nDataGridRows++;
}
}
SetDataGridRows(null, nDataGridRows);
}
/// <devdoc>
/// Sets the array of DataGridRow objects used for
/// all row-related logic in the DataGrid.
/// </devdoc>
internal void SetDataGridRows(DataGridRow[] newRows, int newRowsLength) {
dataGridRows = newRows;
dataGridRowsLength = newRowsLength;
// update the vertical scroll bar
vertScrollBar.Maximum = Math.Max(0, DataGridRowsLength - 1);
if (firstVisibleRow > newRowsLength) {
vertScrollBar.Value = 0;
firstVisibleRow = 0;
}
ResetUIState();
#if DEBUG
// sanity check: all the rows should have the same
// dataGridTable
if (newRows != null && newRowsLength > 0) {
DataGridTableStyle dgTable = newRows[0].DataGridTableStyle;
for (int i = 0; i < newRowsLength; i ++) {
Debug.Assert(dgTable == newRows[i].DataGridTableStyle, "how can two rows have different tableStyles?");
}
}
#endif // DEBUG
Debug.WriteLineIf(CompModSwitches.DataGridCursor.TraceVerbose, "DataGridCursor: There are now " + DataGridRowsLength.ToString(CultureInfo.InvariantCulture) + " rows.");
}
internal int DataGridRowsLength {
get {
return dataGridRowsLength;
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.DataSource"]/*' />
/// <devdoc>
/// <para>Gets or sets the data source that the grid is displaying data for.</para>
/// </devdoc>
[
DefaultValue(null),
SRCategory(SR.CatData),
RefreshProperties(RefreshProperties.Repaint),
AttributeProvider(typeof(IListSource)),
SRDescription(SR.DataGridDataSourceDescr)
]
public object DataSource {
get {
return dataSource;
}
set {
if (value != null && !(value is IList || value is IListSource))
throw new ArgumentException(SR.GetString(SR.BadDataSourceForComplexBinding));
if (dataSource != null && dataSource.Equals(value))
{
return;
}
// when the designer resets the dataSource to null, set the dataMember to null, too
if ((value == null || value == Convert.DBNull) && this.DataMember != null && this.DataMember.Length != 0 ) {
this.dataSource = null;
this.DataMember = "";
return;
}
// if we are setting the dataSource and the dataMember is not a part
// of the properties in the dataSource, then set the dataMember to ""
//
if (value != null)
EnforceValidDataMember(value);
Debug.WriteLineIf(CompModSwitches.DataGridCursor.TraceVerbose, "DataGridCursor: DataSource being set to " + ((value == null) ? "null" : value.ToString()));
// when we change the dataSource, we need to clear the parent rows.
// the same goes for all the caption UI: reset it when the datasource changes.
//
ResetParentRows();
Set_ListManager(value, this.DataMember, false);
}
}
private static readonly object EVENT_DATASOURCECHANGED = new object();
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.DataSourceChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatPropertyChanged), SRDescription(SR.DataGridOnDataSourceChangedDescr)]
public event EventHandler DataSourceChanged {
add {
Events.AddHandler(EVENT_DATASOURCECHANGED, value);
}
remove {
Events.RemoveHandler(EVENT_DATASOURCECHANGED, value);
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.DataMember"]/*' />
/// <devdoc>
/// <para>
/// Gets or sets the specific table in a DataSource for the control.
/// </para>
/// </devdoc>
[
DefaultValue(null),
SRCategory(SR.CatData),
Editor("System.Windows.Forms.Design.DataMemberListEditor, " + AssemblyRef.SystemDesign, typeof(System.Drawing.Design.UITypeEditor)),
SRDescription(SR.DataGridDataMemberDescr)
]
public string DataMember {
get {
return dataMember;
}
set {
if (dataMember != null && dataMember.Equals(value))
return;
Debug.WriteLineIf(CompModSwitches.DataGridCursor.TraceVerbose, "DataGridCursor: DataSource being set to " + ((value == null) ? "null" : value.ToString()));
// when we change the dataMember, we need to clear the parent rows.
// the same goes for all the caption UI: reset it when the datamember changes.
//
ResetParentRows();
Set_ListManager(this.DataSource, value, false);
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.SetDataBinding"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public void SetDataBinding(object dataSource, string dataMember) {
parentRows.Clear();
originalState = null;
caption.BackButtonActive = caption.DownButtonActive = caption.BackButtonVisible = false;
caption.SetDownButtonDirection(!layout.ParentRowsVisible);
Set_ListManager(dataSource, dataMember, false);
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ListManager"]/*' />
[
Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced),
SRDescription(SR.DataGridListManagerDescr)
]
internal protected CurrencyManager ListManager {
get {
//try to return something useful:
if (listManager == null && this.BindingContext != null && this.DataSource != null)
return (CurrencyManager) this.BindingContext[this.DataSource, this.DataMember];
else
return listManager;
}
set {
throw new NotSupportedException(SR.GetString(SR.DataGridSetListManager));
}
}
internal void Set_ListManager(object newDataSource, string newDataMember, bool force) {
Set_ListManager(newDataSource, newDataMember, force, true); // true for forcing column creation
}
//
// prerequisite: the dataMember and the dataSource should be set to the new values
//
// will do the following:
// call EndEdit on the current listManager, will unWire the listManager events, will set the listManager to the new
// reality, will wire the new listManager, will update the policy, will set the dataGridTable, will reset the ui state.
//
internal void Set_ListManager(object newDataSource, string newDataMember, bool force, bool forceColumnCreation)
{
bool dataSourceChanged = this.DataSource != newDataSource;
bool dataMemberChanged = this.DataMember != newDataMember;
// if nothing happened, then why do any work?
if (!force && !dataSourceChanged && !dataMemberChanged && gridState[GRIDSTATE_inSetListManager])
return;
gridState[GRIDSTATE_inSetListManager] = true;
if (this.toBeDisposedEditingControl != null) {
Debug.Assert(this.Controls.Contains(this.toBeDisposedEditingControl));
this.Controls.Remove(this.toBeDisposedEditingControl);
this.toBeDisposedEditingControl = null;
}
bool beginUpdateInternal = true;
try {
// will endEdit on the current listManager
UpdateListManager();
// unwire the events:
if (this.listManager != null)
UnWireDataSource();
CurrencyManager oldListManager = listManager;
bool listManagerChanged = false;
// set up the new listManager
// CAUTION: we need to set up the listManager in the grid before setting the dataSource/dataMember props
// in the grid. the reason is that if the BindingContext was not yet requested, and it is created in the BindingContext prop
// then the grid will call Set_ListManager again, and eventually that means that the dataGrid::listManager will
// be hooked up twice to all the events (PositionChanged, ItemChanged, CurrentChanged)
if (newDataSource != null && this.BindingContext != null && !(newDataSource == Convert.DBNull))
this.listManager = (CurrencyManager) this.BindingContext[newDataSource, newDataMember];
else
listManager = null;
// update the dataSource and the dateMember
this.dataSource = newDataSource;
this.dataMember = newDataMember == null ? "" : newDataMember;
listManagerChanged = (listManager != oldListManager);
// wire the events
if (listManager != null) {
WireDataSource();
// update the policy
policy.UpdatePolicy(this.listManager, this.ReadOnly);
}
if (!Initializing)
{
if (listManager == null)
{
if (this.ContainsFocus && this.ParentInternal == null) {
Debug.Assert(this.toBeDisposedEditingControl == null, "we should have removed the toBeDisposedEditingControl already");
// if we unparent the active control then the form won't close
for (int i = 0; i < this.Controls.Count; i++) {
if (this.Controls[i].Focused) {
this.toBeDisposedEditingControl = this.Controls[i];
break;
}
}
if (this.toBeDisposedEditingControl == this.horizScrollBar || this.toBeDisposedEditingControl == this.vertScrollBar) {
this.toBeDisposedEditingControl = null;
}
#if DEBUG
else {
Debug.Assert(this.toBeDisposedEditingControl != null, "if the grid contains the focus, then the active control should be in the children of data grid control");
Debug.Assert(this.editColumn != null, "if we have an editing control should be a control in the data grid column");
if (this.editColumn is DataGridTextBoxColumn) {
Debug.Assert(((DataGridTextBoxColumn) this.editColumn).TextBox == this.toBeDisposedEditingControl, "if we have an editing control should be a control in the data grid column");
}
}
#endif // debug;
}
SetDataGridRows(null, 0);
this.defaultTableStyle.GridColumnStyles.Clear();
SetDataGridTable(this.defaultTableStyle, forceColumnCreation);
if (this.toBeDisposedEditingControl != null) {
this.Controls.Add(this.toBeDisposedEditingControl);
}
}
}
// PERF: if the listManager did not change, then do not:
// 1. create new rows
// 2. create new columns
// 3. compute the errors in the list
//
// when the metaDataChanges, we need to recreate
// the rows and the columns
//
if (listManagerChanged || gridState[GRIDSTATE_metaDataChanged]) {
if (this.Visible) BeginUpdateInternal();
if (listManager != null)
{
// get rid of the old gridColumns
// we need to clear the old column collection even when navigating to
// a list that has a table style associated w/ it. Why? because the
// old column collection will be used by the parent rows to paint
this.defaultTableStyle.GridColumnStyles.ResetDefaultColumnCollection();
DataGridTableStyle newGridTable = this.dataGridTables[listManager.GetListName()];
if (newGridTable == null) {
SetDataGridTable(this.defaultTableStyle, forceColumnCreation);
} else {
SetDataGridTable(newGridTable, forceColumnCreation);
}
// set the currentRow in ssync w/ the position in the listManager
currentRow = listManager.Position == -1 ? 0 : listManager.Position;
}
// when we create the rows we need to use the current dataGridTable
//
RecreateDataGridRows();
if (this.Visible) EndUpdateInternal();
beginUpdateInternal = false;
ComputeMinimumRowHeaderWidth();
if (this.myGridTable.IsDefault)
this.RowHeaderWidth = Math.Max(this.minRowHeaderWidth, this.RowHeaderWidth);
else
this.myGridTable.RowHeaderWidth = Math.Max(this.minRowHeaderWidth, this.RowHeaderWidth);
ListHasErrors = DataGridSourceHasErrors();
// build the list of columns and relationships
// wipe out the now invalid states
//ResetMouseState();
ResetUIState();
//layout.CaptionVisible = dataCursor == null ? false : true;
OnDataSourceChanged(EventArgs.Empty);
}
} finally {
gridState[GRIDSTATE_inSetListManager] = false;
// start painting again
if (beginUpdateInternal && this.Visible) EndUpdateInternal();
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.CurrentRowIndex"]/*' />
/// <devdoc>
/// <para>Gets or sets index of the selected row.</para>
/// </devdoc>
// will set the position in the ListManager
//
[
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
Browsable(false),
SRDescription(SR.DataGridSelectedIndexDescr)
]
public int CurrentRowIndex {
get {
if (originalState == null)
return this.listManager == null ? - 1 : this.listManager.Position;
else {
if (this.BindingContext == null)
return -1;
CurrencyManager originalListManager = (CurrencyManager) this.BindingContext[originalState.DataSource, originalState.DataMember];
return originalListManager.Position;
}
}
set {
if (this.listManager == null)
throw new InvalidOperationException(SR.GetString(SR.DataGridSetSelectIndex));
if (originalState == null) {
this.listManager.Position = value;
currentRow = value;
return;
}
// if we have a this.ListManager, then this.BindingManager cannot be null
//
CurrencyManager originalListManager = (CurrencyManager) this.BindingContext[originalState.DataSource, originalState.DataMember];
originalListManager.Position = value;
// this is for parent rows
originalState.LinkingRow = originalState.DataGridRows[value];
// Invalidate everything
Invalidate();
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.TableStyles"]/*' />
/// <devdoc>
/// <para>Gets the collection of tables for the grid.</para>
/// </devdoc>
[
SRCategory(SR.CatData),
DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
Localizable(true),
SRDescription(SR.DataGridGridTablesDescr)
]
public GridTableStylesCollection TableStyles {
get {
return dataGridTables;
}
}
internal new int FontHeight {
get {
return fontHeight;
}
}
internal AccessibleObject ParentRowsAccessibleObject {
get {
return parentRows.AccessibleObject;
}
}
internal Rectangle ParentRowsBounds {
get {
return layout.ParentRows;
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.GridLineColor"]/*' />
/// <devdoc>
/// <para>Gets or sets the color of the grid lines.</para>
/// </devdoc>
[
SRCategory(SR.CatColors),
SRDescription(SR.DataGridGridLineColorDescr)
]
public Color GridLineColor {
get {
return gridLineBrush.Color;
}
set {
if (gridLineBrush.Color != value) {
if (value.IsEmpty)
throw new ArgumentException(SR.GetString(SR.DataGridEmptyColor, "GridLineColor"));
gridLineBrush = new SolidBrush(value);
Invalidate(layout.Data);
}
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ShouldSerializeGridLineColor"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected virtual bool ShouldSerializeGridLineColor()
{
return !GridLineBrush.Equals(DefaultGridLineBrush);
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ResetGridLineColor"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public void ResetGridLineColor() {
if (ShouldSerializeGridLineColor()) {
GridLineColor = DefaultGridLineBrush.Color;
}
}
internal SolidBrush GridLineBrush {
get {
return gridLineBrush;
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.GridLineStyle"]/*' />
/// <devdoc>
/// <para>
/// Gets or sets the line style of the grid.
/// </para>
/// </devdoc>
[
SRCategory(SR.CatAppearance),
DefaultValue(defaultGridLineStyle),
SRDescription(SR.DataGridGridLineStyleDescr)
]
public DataGridLineStyle GridLineStyle {
get {
return gridLineStyle;
}
set {
//valid values are 0x0 to 0x1.
if (!ClientUtils.IsEnumValid(value, (int)value, (int)DataGridLineStyle.None, (int)DataGridLineStyle.Solid))
{
throw new InvalidEnumArgumentException("value", (int)value, typeof(DataGridLineStyle));
}
if (gridLineStyle != value) {
gridLineStyle = value;
this.myGridTable.ResetRelationsUI();
Invalidate(layout.Data);
}
}
}
internal int GridLineWidth {
get {
Debug.Assert(this.GridLineStyle == DataGridLineStyle.Solid || this.GridLineStyle == DataGridLineStyle.None, "are there any other styles?");
return GridLineStyle == DataGridLineStyle.Solid ? 1 : 0;
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ParentRowsLabelStyle"]/*' />
/// <devdoc>
/// <para>
/// Gets or
/// sets the
/// way parent row labels are displayed.
/// </para>
/// </devdoc>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
DefaultValue(defaultParentRowsLabelStyle),
SRCategory(SR.CatDisplay),
SRDescription(SR.DataGridParentRowsLabelStyleDescr)
]
public DataGridParentRowsLabelStyle ParentRowsLabelStyle {
get {
return parentRowsLabels;
}
set {
//valid values are 0x0 to 0x3
if (!ClientUtils.IsEnumValid(value, (int)value, (int)DataGridParentRowsLabelStyle.None, (int)DataGridParentRowsLabelStyle.Both))
{
throw new InvalidEnumArgumentException("value", (int)value, typeof(DataGridParentRowsLabelStyle));
}
if (parentRowsLabels != value)
{
parentRowsLabels = value;
Invalidate(layout.ParentRows);
OnParentRowsLabelStyleChanged(EventArgs.Empty);
}
}
}
private static readonly object EVENT_PARENTROWSLABELSTYLECHANGED = new object();
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ParentRowsLabelStyleChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatPropertyChanged), SRDescription(SR.DataGridOnParentRowsLabelStyleChangedDescr)]
public event EventHandler ParentRowsLabelStyleChanged {
add {
Events.AddHandler(EVENT_PARENTROWSLABELSTYLECHANGED, value);
}
remove {
Events.RemoveHandler(EVENT_PARENTROWSLABELSTYLECHANGED, value);
}
}
internal bool Initializing {
get {
return inInit;
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.FirstVisibleColumn"]/*' />
/// <devdoc>
/// <para>
/// Gets the index of the first visible column in a grid.
/// </para>
/// </devdoc>
[
Browsable(false),
SRDescription(SR.DataGridFirstVisibleColumnDescr)
]
public int FirstVisibleColumn {
get {
return firstVisibleCol;
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.FlatMode"]/*' />
/// <devdoc>
/// <para>
/// Gets or sets a value indicating whether the grid displays in flat mode.
/// </para>
/// </devdoc>
[
DefaultValue(false),
SRCategory(SR.CatAppearance),
SRDescription(SR.DataGridFlatModeDescr)
]
public bool FlatMode {
get {
return gridState[GRIDSTATE_isFlatMode];
}
set {
if (value != FlatMode) {
gridState[GRIDSTATE_isFlatMode] = value;
Invalidate(layout.Inside);
OnFlatModeChanged(EventArgs.Empty);
}
}
}
private static readonly object EVENT_FLATMODECHANGED = new object();
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.FlatModeChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatPropertyChanged), SRDescription(SR.DataGridOnFlatModeChangedDescr)]
public event EventHandler FlatModeChanged {
add {
Events.AddHandler(EVENT_FLATMODECHANGED, value);
}
remove {
Events.RemoveHandler(EVENT_FLATMODECHANGED, value);
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.HeaderBackColor"]/*' />
/// <devdoc>
/// <para>
/// Gets or
/// sets the background color of all row and column headers.
/// </para>
/// </devdoc>
[
SRCategory(SR.CatColors),
SRDescription(SR.DataGridHeaderBackColorDescr)
]
public Color HeaderBackColor {
get {
return headerBackBrush.Color;
}
set {
if (value.IsEmpty)
throw new ArgumentException(SR.GetString(SR.DataGridEmptyColor, "HeaderBackColor"));
if (IsTransparentColor(value))
throw new ArgumentException(SR.GetString(SR.DataGridTransparentHeaderBackColorNotAllowed));
if (!value.Equals(headerBackBrush.Color)) {
headerBackBrush = new SolidBrush(value);
if (layout.RowHeadersVisible)
Invalidate(layout.RowHeaders);
if (layout.ColumnHeadersVisible)
Invalidate(layout.ColumnHeaders);
Invalidate(layout.TopLeftHeader);
}
}
}
internal SolidBrush HeaderBackBrush {
get {
return headerBackBrush;
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ShouldSerializeHeaderBackColor"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected virtual bool ShouldSerializeHeaderBackColor()
{
return !HeaderBackBrush.Equals(DefaultHeaderBackBrush);
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ResetHeaderBackColor"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public void ResetHeaderBackColor() {
if(ShouldSerializeHeaderBackColor()) {
HeaderBackColor = DefaultHeaderBackBrush.Color;
}
}
internal SolidBrush BackgroundBrush {
get {
return backgroundBrush;
}
}
private void ResetBackgroundColor() {
if (backgroundBrush != null && BackgroundBrush != DefaultBackgroundBrush) {
backgroundBrush.Dispose();
backgroundBrush = null;
}
backgroundBrush = DefaultBackgroundBrush;
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ShouldSerializeBackgroundColor"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected virtual bool ShouldSerializeBackgroundColor()
{
return !BackgroundBrush.Equals(DefaultBackgroundBrush);
}
// using this property, the user can set the backGround color
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.BackgroundColor"]/*' />
/// <devdoc>
/// <para>Gets or sets the background color of the grid.</para>
/// </devdoc>
[
SRCategory(SR.CatColors),
SRDescription(SR.DataGridBackgroundColorDescr)
]
public Color BackgroundColor {
get {
return backgroundBrush.Color;
}
set {
if (value.IsEmpty)
throw new ArgumentException(SR.GetString(SR.DataGridEmptyColor, "BackgroundColor"));
if (!value.Equals(backgroundBrush.Color)) {
if (backgroundBrush != null && BackgroundBrush != DefaultBackgroundBrush) {
backgroundBrush.Dispose();
backgroundBrush = null;
}
backgroundBrush = new SolidBrush(value);
Invalidate(layout.Inside);
OnBackgroundColorChanged(EventArgs.Empty);
}
}
}
private static readonly object EVENT_BACKGROUNDCOLORCHANGED = new object();
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.BackgroundColorChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatPropertyChanged), SRDescription(SR.DataGridOnBackgroundColorChangedDescr)]
public event EventHandler BackgroundColorChanged {
add {
Events.AddHandler(EVENT_BACKGROUNDCOLORCHANGED, value);
}
remove {
Events.RemoveHandler(EVENT_BACKGROUNDCOLORCHANGED, value);
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.HeaderFont"]/*' />
/// <devdoc>
/// <para>
/// Indicates whether the <see cref='System.Windows.Forms.DataGrid.HeaderFont'/> property should be persisted.
/// </para>
/// </devdoc>
[
SRCategory(SR.CatAppearance),
SRDescription(SR.DataGridHeaderFontDescr)
]
public Font HeaderFont {
get {
return(headerFont == null ? this.Font : headerFont);
}
set {
if (value == null)
throw new ArgumentNullException("HeaderFont");
if (!value.Equals(headerFont)) {
headerFont = value;
RecalculateFonts();
PerformLayout();
Invalidate(layout.Inside);
}
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ShouldSerializeHeaderFont"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected bool ShouldSerializeHeaderFont() {
return(headerFont != null);
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ResetHeaderFont"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public void ResetHeaderFont() {
if (headerFont != null) {
headerFont = null;
RecalculateFonts();
PerformLayout();
Invalidate(layout.Inside);
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.HeaderForeColor"]/*' />
/// <devdoc>
/// <para>
/// Resets the <see cref='System.Windows.Forms.DataGrid.HeaderFont'/> property to its default value.
/// </para>
/// </devdoc>
/// <devdoc>
/// <para>
/// Gets or sets the foreground color of the grid's headers.
/// </para>
/// </devdoc>
[
SRCategory(SR.CatColors),
SRDescription(SR.DataGridHeaderForeColorDescr)
]
public Color HeaderForeColor {
get {
return headerForePen.Color;
}
set {
if (value.IsEmpty)
throw new ArgumentException(SR.GetString(SR.DataGridEmptyColor, "HeaderForeColor"));
if (!value.Equals(headerForePen.Color)) {
headerForePen = new Pen(value);
headerForeBrush = new SolidBrush(value);
if (layout.RowHeadersVisible)
Invalidate(layout.RowHeaders);
if (layout.ColumnHeadersVisible)
Invalidate(layout.ColumnHeaders);
Invalidate(layout.TopLeftHeader);
}
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ShouldSerializeHeaderForeColor"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected virtual bool ShouldSerializeHeaderForeColor()
{
return !HeaderForePen.Equals(DefaultHeaderForePen);
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ResetHeaderForeColor"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public void ResetHeaderForeColor() {
if(ShouldSerializeHeaderForeColor()) {
HeaderForeColor = DefaultHeaderForeBrush.Color;
}
}
internal SolidBrush HeaderForeBrush {
get {
return this.headerForeBrush;
}
}
internal Pen HeaderForePen {
get {
return headerForePen;
}
}
private void ResetHorizontalOffset() {
horizontalOffset = 0;
negOffset = 0;
firstVisibleCol = 0;
numVisibleCols = 0;
lastTotallyVisibleCol = -1;
}
internal int HorizontalOffset {
get {
return horizontalOffset;
}
set {
//if (CompModSwitches.DataGridScrolling.TraceVerbose) Debug.WriteLine("DataGridScrolling: Set_HorizontalOffset, value = " + value.ToString());
if (value < 0)
value = 0;
//
// if the dataGrid is not bound ( listManager == null || gridTable == null)
// then use ResetHorizontalOffset();
//
int totalWidth = GetColumnWidthSum();
int widthNotVisible = totalWidth - layout.Data.Width;
if (value > widthNotVisible && widthNotVisible > 0)
value = widthNotVisible;
if (value == horizontalOffset)
return;
int change = horizontalOffset - value;
horizScrollBar.Value = value;
Rectangle scroll = layout.Data;
if (layout.ColumnHeadersVisible)
scroll = Rectangle.Union(scroll, layout.ColumnHeaders);
horizontalOffset = value;
this.firstVisibleCol = ComputeFirstVisibleColumn();
// update the lastTotallyVisibleCol
ComputeVisibleColumns();
if (gridState[GRIDSTATE_isScrolling])
{
// if the user did not click on the grid yet, then do not put the edit
// control when scrolling
if (currentCol >= firstVisibleCol && currentCol < firstVisibleCol + numVisibleCols - 1 && (gridState[GRIDSTATE_isEditing] || gridState[GRIDSTATE_isNavigating]))
Edit();
else
EndEdit();
// isScrolling is set to TRUE when the user scrolls.
// once we move the edit box, we finished processing the scroll event, so set isScrolling to FALSE
// to set isScrolling to TRUE, we need another scroll event.
gridState[GRIDSTATE_isScrolling] = false;
}
else
{
EndEdit();
}
NativeMethods.RECT[] rects = CreateScrollableRegion(scroll);
ScrollRectangles(rects, change);
OnScroll(EventArgs.Empty);
}
}
private void ScrollRectangles(NativeMethods.RECT[] rects, int change) {
if (rects != null) {
NativeMethods.RECT scroll;
if (isRightToLeft()) change = -change;
for (int r = 0; r < rects.Length; r++) {
scroll = rects[r];
SafeNativeMethods.ScrollWindow(new HandleRef(this, Handle),
change,
0,
ref scroll,
ref scroll);
}
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.HorizScrollBar"]/*' />
/// <devdoc>
/// </devdoc>
[
SRDescription(SR.DataGridHorizScrollBarDescr)
]
protected ScrollBar HorizScrollBar {
get {
return horizScrollBar;
}
}
/// <devdoc>
/// <para>
/// Retrieves a value indicating whether odd and even
/// rows are painted using a different background color.
/// </para>
/// </devdoc>
// CUT by 53973 - Cleanup eventually to be static.
internal bool LedgerStyle {
get {
return gridState[GRIDSTATE_isLedgerStyle];
}
/*
set {
if (isLedgerStyle != value) {
isLedgerStyle = value;
InvalidateInside();
}
}
*/
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.LinkColor"]/*' />
/// <devdoc>
/// <para>
/// Indicates whether the <see cref='System.Windows.Forms.DataGrid.LinkColor'/> property should be persisted.
/// </para>
/// </devdoc>
[
SRCategory(SR.CatColors),
SRDescription(SR.DataGridLinkColorDescr)
]
public Color LinkColor {
get {
return linkBrush.Color;
}
set {
if (value.IsEmpty)
throw new ArgumentException(SR.GetString(SR.DataGridEmptyColor, "LinkColor"));
if (!linkBrush.Color.Equals(value)) {
linkBrush = new SolidBrush(value);
Invalidate(layout.Data);
}
}
}
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
internal virtual bool ShouldSerializeLinkColor()
{
return !LinkBrush.Equals(DefaultLinkBrush);
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ResetLinkColor"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public void ResetLinkColor() {
if (ShouldSerializeLinkColor())
LinkColor = DefaultLinkBrush.Color;
}
internal Brush LinkBrush {
get {
return linkBrush;
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.LinkHoverColor"]/*' />
/// <devdoc>
/// <para>
/// Gets
/// or sets the color a link changes to when
/// the mouse pointer moves over it.
/// </para>
/// </devdoc>
[
SRDescription(SR.DataGridLinkHoverColorDescr),
SRCategory(SR.CatColors),
Browsable(false),
EditorBrowsable(EditorBrowsableState.Never)
]
public Color LinkHoverColor {
get {
return this.LinkColor;
}
set {
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ShouldSerializeLinkHoverColor"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected virtual bool ShouldSerializeLinkHoverColor()
{
return false;
// return !LinkHoverBrush.Equals(defaultLinkHoverBrush);
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ResetLinkHoverColor"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public void ResetLinkHoverColor() {
/*
if (ShouldSerializeLinkHoverColor())
LinkHoverColor = defaultLinkHoverBrush.Color;*/
}
/// <devdoc>
/// <para>
/// Indicates whether the <see cref='System.Windows.Forms.DataGrid.LinkHoverColor'/> property should be
/// persisted.
/// </para>
/// </devdoc>
internal Font LinkFont {
get {
return linkFont;
}
}
internal int LinkFontHeight {
get {
return linkFontHeight;
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.AllowNavigation"]/*' />
/// <devdoc>
/// <para>Gets or sets a value
/// that specifies which links are shown and in what context.</para>
/// </devdoc>
[
DefaultValue(true),
SRDescription(SR.DataGridNavigationModeDescr),
SRCategory(SR.CatBehavior)
]
public bool AllowNavigation {
get {
return gridState[GRIDSTATE_allowNavigation];
}
set {
if (AllowNavigation != value) {
gridState[GRIDSTATE_allowNavigation] = value;
// let the Caption know about this:
this.Caption.BackButtonActive = !parentRows.IsEmpty() && (value);
this.Caption.BackButtonVisible = this.Caption.BackButtonActive;
RecreateDataGridRows();
OnAllowNavigationChanged(EventArgs.Empty);
}
}
}
private static readonly object EVENT_ALLOWNAVIGATIONCHANGED = new object();
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.AllowNavigationChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatPropertyChanged), SRDescription(SR.DataGridOnNavigationModeChangedDescr)]
public event EventHandler AllowNavigationChanged {
add {
Events.AddHandler(EVENT_ALLOWNAVIGATIONCHANGED, value);
}
remove {
Events.RemoveHandler(EVENT_ALLOWNAVIGATIONCHANGED, value);
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.Cursor"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[
Browsable(false), EditorBrowsable(EditorBrowsableState.Never)
]
public override Cursor Cursor {
// get the cursor out of the propertyGrid.
get {
return base.Cursor;
}
set {
base.Cursor = value;
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.CursorChanged"]/*' />
/// <internalonly/>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
new public event EventHandler CursorChanged {
add {
base.CursorChanged += value;
}
remove {
base.CursorChanged -= value;
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.BackgroundImage"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[
Browsable(false), EditorBrowsable(EditorBrowsableState.Never)
]
public override Image BackgroundImage {
// get the BackgroundImage out of the propertyGrid.
get {
return base.BackgroundImage;
}
set {
base.BackgroundImage = value;
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.BackgroundImageLayout"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[
Browsable(false), EditorBrowsable(EditorBrowsableState.Never)
]
public override ImageLayout BackgroundImageLayout {
// get the BackgroundImage out of the propertyGrid.
get {
return base.BackgroundImageLayout;
}
set {
base.BackgroundImageLayout = value;
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.BackgroundImageChanged"]/*' />
/// <internalonly/>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
new public event EventHandler BackgroundImageChanged {
add {
base.BackgroundImageChanged += value;
}
remove {
base.BackgroundImageChanged -= value;
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.BackgroundImageLayoutChanged"]/*' />
/// <internalonly/>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
new public event EventHandler BackgroundImageLayoutChanged {
add {
base.BackgroundImageLayoutChanged += value;
}
remove {
base.BackgroundImageLayoutChanged -= value;
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ParentRowsBackColor"]/*' />
/// <devdoc>
/// <para>
/// Gets or sets the background color of parent rows.
/// </para>
/// </devdoc>
[
SRCategory(SR.CatColors),
SRDescription(SR.DataGridParentRowsBackColorDescr)
]
public Color ParentRowsBackColor {
get {
return parentRows.BackColor;
}
set {
if (IsTransparentColor(value))
throw new ArgumentException(SR.GetString(SR.DataGridTransparentParentRowsBackColorNotAllowed));
this.parentRows.BackColor = value;
}
}
internal SolidBrush ParentRowsBackBrush {
get {
return parentRows.BackBrush;
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ShouldSerializeParentRowsBackColor"]/*' />
/// <devdoc>
/// <para>
/// Indicates whether the <see cref='System.Windows.Forms.DataGrid.ParentRowsBackColor'/> property should be
/// persisted.
/// </para>
/// </devdoc>
protected virtual bool ShouldSerializeParentRowsBackColor()
{
return !ParentRowsBackBrush.Equals(DefaultParentRowsBackBrush);
}
private void ResetParentRowsBackColor() {
if (ShouldSerializeParentRowsBackColor())
parentRows.BackBrush = DefaultParentRowsBackBrush;
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ParentRowsForeColor"]/*' />
/// <devdoc>
/// <para>
/// Gets or sets the foreground color of parent rows.
/// </para>
/// </devdoc>
[
SRCategory(SR.CatColors),
SRDescription(SR.DataGridParentRowsForeColorDescr)
]
public Color ParentRowsForeColor {
get {
return parentRows.ForeColor;
}
set {
this.parentRows.ForeColor = value;
}
}
internal SolidBrush ParentRowsForeBrush {
get {
return parentRows.ForeBrush;
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ShouldSerializeParentRowsForeColor"]/*' />
/// <devdoc>
/// <para>
/// Indicates whether the <see cref='System.Windows.Forms.DataGrid.ParentRowsForeColor'/> property should be
/// persisted.
/// </para>
/// </devdoc>
protected virtual bool ShouldSerializeParentRowsForeColor()
{
return !ParentRowsForeBrush.Equals(DefaultParentRowsForeBrush);
}
private void ResetParentRowsForeColor() {
if (ShouldSerializeParentRowsForeColor())
parentRows.ForeBrush = DefaultParentRowsForeBrush;
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.PreferredColumnWidth"]/*' />
/// <devdoc>
/// <para>
/// Gets
/// or sets the default width of the grid columns in
/// pixels.
/// </para>
/// </devdoc>
[
DefaultValue(defaultPreferredColumnWidth),
SRCategory(SR.CatLayout),
SRDescription(SR.DataGridPreferredColumnWidthDescr),
TypeConverter(typeof(DataGridPreferredColumnWidthTypeConverter))
]
public int PreferredColumnWidth {
get {
return preferredColumnWidth;
}
set {
if (value < 0)
throw new ArgumentException(SR.GetString(SR.DataGridColumnWidth), "PreferredColumnWidth");
if (preferredColumnWidth != value) {
preferredColumnWidth = value;
}
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.PreferredRowHeight"]/*' />
/// <devdoc>
/// <para>
/// Gets or sets the preferred row height for the <see cref='System.Windows.Forms.DataGrid'/> control.
/// </para>
/// </devdoc>
[
SRCategory(SR.CatLayout),
SRDescription(SR.DataGridPreferredRowHeightDescr)
]
public int PreferredRowHeight {
get {
return prefferedRowHeight;
}
set {
if (value < 0)
throw new ArgumentException(SR.GetString(SR.DataGridRowRowHeight));
prefferedRowHeight = value;
}
}
private void ResetPreferredRowHeight() {
prefferedRowHeight = defaultFontHeight + 3;
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ShouldSerializePreferredRowHeight"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected bool ShouldSerializePreferredRowHeight()
{
return prefferedRowHeight != defaultFontHeight + 3;
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ReadOnly"]/*' />
/// <devdoc>
/// <para>Gets or sets a value indicating whether the grid
/// is in read-only mode.</para>
/// </devdoc>
[
DefaultValue(false),
SRCategory(SR.CatBehavior),
SRDescription(SR.DataGridReadOnlyDescr)
]
public bool ReadOnly {
get {
return gridState[GRIDSTATE_readOnlyMode];
}
set {
if (ReadOnly != value) {
bool recreateRows = false;
if (value) {
// AllowAdd happens to have the same boolean value as whether we need to recreate rows.
recreateRows = policy.AllowAdd;
policy.AllowRemove = false;
policy.AllowEdit = false;
policy.AllowAdd = false;
}
else {
recreateRows |= policy.UpdatePolicy(this.listManager, value);
}
gridState[GRIDSTATE_readOnlyMode] = value;
DataGridRow[] dataGridRows = this.DataGridRows;
if (recreateRows) {
RecreateDataGridRows();
// keep the selected rows
DataGridRow[] currentDataGridRows = this.DataGridRows;
int rowCount = Math.Min(currentDataGridRows.Length, dataGridRows.Length);
for (int i = 0; i < rowCount; i++) {
if (dataGridRows[i].Selected)
currentDataGridRows[i].Selected = true;
}
}
// the addnew row needs to be updated.
PerformLayout();
InvalidateInside();
OnReadOnlyChanged(EventArgs.Empty);
}
}
}
private static readonly object EVENT_READONLYCHANGED = new object();
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ReadOnlyChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatPropertyChanged), SRDescription(SR.DataGridOnReadOnlyChangedDescr)]
public event EventHandler ReadOnlyChanged {
add {
Events.AddHandler(EVENT_READONLYCHANGED, value);
}
remove {
Events.RemoveHandler(EVENT_READONLYCHANGED, value);
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ColumnHeadersVisible"]/*' />
/// <devdoc>
/// <para>
/// Gets
/// or sets a value indicating if the grid's column headers are visible.
/// </para>
/// </devdoc>
[
SRCategory(SR.CatDisplay),
DefaultValue(true),
SRDescription(SR.DataGridColumnHeadersVisibleDescr)
]
public bool ColumnHeadersVisible {
get {
return gridState[GRIDSTATE_columnHeadersVisible];
}
set {
if (ColumnHeadersVisible != value) {
gridState[GRIDSTATE_columnHeadersVisible] = value;
layout.ColumnHeadersVisible = value;
PerformLayout();
InvalidateInside();
}
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ParentRowsVisible"]/*' />
/// <devdoc>
/// <para>
/// Gets or sets a value indicating whether the parent rows of a table are
/// visible.
/// </para>
/// </devdoc>
[
SRCategory(SR.CatDisplay),
DefaultValue(true),
SRDescription(SR.DataGridParentRowsVisibleDescr)
]
public bool ParentRowsVisible {
get {
return layout.ParentRowsVisible;
}
set {
if (layout.ParentRowsVisible != value) {
SetParentRowsVisibility(value);
// update the caption: parentDownVisible == false corresponds to DownButtonDown == true;
//
caption.SetDownButtonDirection(!value);
OnParentRowsVisibleChanged(EventArgs.Empty);
}
}
}
private static readonly object EVENT_PARENTROWSVISIBLECHANGED = new object();
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ParentRowsVisibleChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatPropertyChanged), SRDescription(SR.DataGridOnParentRowsVisibleChangedDescr)]
public event EventHandler ParentRowsVisibleChanged {
add {
Events.AddHandler(EVENT_PARENTROWSVISIBLECHANGED, value);
}
remove {
Events.RemoveHandler(EVENT_PARENTROWSVISIBLECHANGED, value);
}
}
internal bool ParentRowsIsEmpty() {
return parentRows.IsEmpty();
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.RowHeadersVisible"]/*' />
/// <devdoc>
/// <para>
/// Gets or sets a value indicating whether the data grid's row headers are
/// visible.
/// </para>
/// </devdoc>
[
SRCategory(SR.CatDisplay),
DefaultValue(true),
SRDescription(SR.DataGridRowHeadersVisibleDescr)
]
public bool RowHeadersVisible {
get {
return gridState[GRIDSTATE_rowHeadersVisible];
}
set {
if (RowHeadersVisible != value) {
gridState[GRIDSTATE_rowHeadersVisible] = value;
PerformLayout();
InvalidateInside();
}
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.RowHeaderWidth"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[
SRCategory(SR.CatLayout),
DefaultValue(defaultRowHeaderWidth),
SRDescription(SR.DataGridRowHeaderWidthDescr)
]
public int RowHeaderWidth {
get {
return rowHeaderWidth;
}
set {
value = Math.Max(this.minRowHeaderWidth, value);
if (rowHeaderWidth != value)
{
rowHeaderWidth = value;
if (layout.RowHeadersVisible)
{
PerformLayout();
InvalidateInside();
}
}
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.Text"]/*' />
/// <devdoc>
/// <para>
/// Gets or sets the width of headers.
/// </para>
/// </devdoc>
[
Browsable(false), EditorBrowsable(EditorBrowsableState.Never),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
Bindable(false)
]
public override string Text {
get {
return base.Text;
}
set {
base.Text = value;
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.TextChanged"]/*' />
/// <internalonly/>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
new public event EventHandler TextChanged {
add {
base.TextChanged += value;
}
remove {
base.TextChanged -= value;
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.VertScrollBar"]/*' />
/// <devdoc>
/// <para>
/// Gets the vertical scroll bar of the control.
/// </para>
/// </devdoc>
[
Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced),
SRDescription(SR.DataGridVertScrollBarDescr)
]
protected ScrollBar VertScrollBar {
get {
return vertScrollBar;
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.VisibleColumnCount"]/*' />
/// <devdoc>
/// <para>
/// Gets the number of visible columns.
/// </para>
/// </devdoc>
[
Browsable(false),
SRDescription(SR.DataGridVisibleColumnCountDescr)
]
public int VisibleColumnCount {
get {
return Math.Min(numVisibleCols, this.myGridTable == null ? 0 : this.myGridTable.GridColumnStyles.Count);
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.VisibleRowCount"]/*' />
/// <devdoc>
/// <para>
/// Gets the number of rows visible.
/// </para>
/// </devdoc>
[
Browsable(false),
SRDescription(SR.DataGridVisibleRowCountDescr)
]
public int VisibleRowCount {
get {
return numVisibleRows;
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.this"]/*' />
/// <devdoc>
/// <para>Gets or sets the value of the cell at
/// the specified the row and column.</para>
/// </devdoc>
public object this[int rowIndex, int columnIndex] {
get {
EnsureBound();
if (rowIndex < 0 || rowIndex >= DataGridRowsLength)
throw new ArgumentOutOfRangeException("rowIndex");
if (columnIndex < 0 || columnIndex >= myGridTable.GridColumnStyles.Count)
throw new ArgumentOutOfRangeException("columnIndex");
CurrencyManager listManager = this.listManager;
DataGridColumnStyle column = myGridTable.GridColumnStyles[columnIndex];
return column.GetColumnValueAtRow(listManager, rowIndex);
}
set {
EnsureBound();
if (rowIndex < 0 || rowIndex >= DataGridRowsLength)
throw new ArgumentOutOfRangeException("rowIndex");
if (columnIndex < 0 || columnIndex >= myGridTable.GridColumnStyles.Count)
throw new ArgumentOutOfRangeException("columnIndex");
CurrencyManager listManager = this.listManager;
if (listManager.Position != rowIndex)
listManager.Position = rowIndex;
DataGridColumnStyle column = myGridTable.GridColumnStyles[columnIndex];
column.SetColumnValueAtRow(listManager, rowIndex, value);
// invalidate the bounds of the cell only if the cell is visible
if (columnIndex >= firstVisibleCol && columnIndex <= firstVisibleCol+ numVisibleCols - 1 &&
rowIndex >= firstVisibleRow && rowIndex <= firstVisibleRow + numVisibleRows) {
Rectangle bounds = GetCellBounds(rowIndex, columnIndex);
this.Invalidate(bounds);
}
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.this1"]/*' />
/// <devdoc>
/// <para>Gets or sets the value of a specified <see cref='System.Windows.Forms.DataGridCell'/>.</para>
/// </devdoc>
public object this[DataGridCell cell] {
get {
return this[cell.RowNumber, cell.ColumnNumber];
}
set {
this[cell.RowNumber, cell.ColumnNumber] = value;
}
}
private void WireTableStylePropChanged(DataGridTableStyle gridTable) {
gridTable.GridLineColorChanged += new EventHandler(GridLineColorChanged);
gridTable.GridLineStyleChanged += new EventHandler(GridLineStyleChanged);
gridTable.HeaderBackColorChanged += new EventHandler(HeaderBackColorChanged);
gridTable.HeaderFontChanged += new EventHandler(HeaderFontChanged);
gridTable.HeaderForeColorChanged += new EventHandler(HeaderForeColorChanged);
gridTable.LinkColorChanged += new EventHandler(LinkColorChanged);
gridTable.LinkHoverColorChanged += new EventHandler(LinkHoverColorChanged);
gridTable.PreferredColumnWidthChanged += new EventHandler(PreferredColumnWidthChanged);
gridTable.RowHeadersVisibleChanged += new EventHandler(RowHeadersVisibleChanged);
gridTable.ColumnHeadersVisibleChanged += new EventHandler(ColumnHeadersVisibleChanged);
gridTable.RowHeaderWidthChanged += new EventHandler(RowHeaderWidthChanged);
gridTable.AllowSortingChanged += new EventHandler(AllowSortingChanged);
}
private void UnWireTableStylePropChanged(DataGridTableStyle gridTable) {
gridTable.GridLineColorChanged -= new EventHandler(GridLineColorChanged);
gridTable.GridLineStyleChanged -= new EventHandler(GridLineStyleChanged);
gridTable.HeaderBackColorChanged -= new EventHandler(HeaderBackColorChanged);
gridTable.HeaderFontChanged -= new EventHandler(HeaderFontChanged);
gridTable.HeaderForeColorChanged -= new EventHandler(HeaderForeColorChanged);
gridTable.LinkColorChanged -= new EventHandler(LinkColorChanged);
gridTable.LinkHoverColorChanged -= new EventHandler(LinkHoverColorChanged);
gridTable.PreferredColumnWidthChanged -= new EventHandler(PreferredColumnWidthChanged);
gridTable.RowHeadersVisibleChanged -= new EventHandler(RowHeadersVisibleChanged);
gridTable.ColumnHeadersVisibleChanged -= new EventHandler(ColumnHeadersVisibleChanged);
gridTable.RowHeaderWidthChanged -= new EventHandler(RowHeaderWidthChanged);
gridTable.AllowSortingChanged -= new EventHandler(AllowSortingChanged);
}
/// <devdoc>
/// DataSource events are handled
/// </devdoc>
private void WireDataSource() {
Debug.WriteLineIf(CompModSwitches.DataGridCursor.TraceVerbose, "DataGridCursor: WireDataSource");
Debug.Assert(listManager!= null, "Can't wire up to a null DataSource");
listManager.CurrentChanged += currentChangedHandler;
listManager.PositionChanged += positionChangedHandler;
listManager.ItemChanged += itemChangedHandler;
listManager.MetaDataChanged += metaDataChangedHandler;
}
private void UnWireDataSource() {
Debug.WriteLineIf(CompModSwitches.DataGridCursor.TraceVerbose, "DataGridCursor: UnWireDataSource");
Debug.Assert(listManager != null, "Can't un wire from a null DataSource");
listManager.CurrentChanged -= currentChangedHandler;
listManager.PositionChanged -= positionChangedHandler;
listManager.ItemChanged -= itemChangedHandler;
listManager.MetaDataChanged -= metaDataChangedHandler;
}
// This is called after a row has been added. And I think whenever
// a row gets deleted, etc.
// We recreate our datagrid rows at this point.
private void DataSource_Changed(object sender, EventArgs ea) {
Debug.WriteLineIf(CompModSwitches.DataGridCursor.TraceVerbose, "DataGridCursor: DataSource_Changed");
// the grid will receive the dataSource_Changed event when
// allowAdd changes on the dataView.
policy.UpdatePolicy(this.ListManager, this.ReadOnly);
if (gridState[GRIDSTATE_inListAddNew]) {
// we are adding a new row
// keep the old rows, w/ their height, expanded/collapsed information
//
Debug.Assert(policy.AllowAdd, "how can we add a new row if the policy does not allow this?");
Debug.Assert(DataGridRowsLength == DataGridRows.Length, "how can this fail?");
DataGridRow[] gridRows = DataGridRows;
int currentRowCount = DataGridRowsLength;
// put the added row:
//
gridRows[currentRowCount - 1] = new DataGridRelationshipRow(this, this.myGridTable, currentRowCount - 1);
SetDataGridRows(gridRows, currentRowCount);
} else if (gridState[GRIDSTATE_inAddNewRow] && !gridState[GRIDSTATE_inDeleteRow]) {
// when the backEnd adds a row and we are still inAddNewRow
listManager.CancelCurrentEdit();
gridState[GRIDSTATE_inAddNewRow] = false;
RecreateDataGridRows();
} else if (!gridState[GRIDSTATE_inDeleteRow]) {
RecreateDataGridRows();
currentRow = Math.Min(currentRow, this.listManager.Count);
}
bool oldListHasErrors = ListHasErrors;
ListHasErrors = DataGridSourceHasErrors();
// if we changed the ListHasErrors, then the grid is already invalidated
if (oldListHasErrors == ListHasErrors)
InvalidateInside();
}
private void GridLineColorChanged(object sender, EventArgs e) {
Invalidate(layout.Data);
}
private void GridLineStyleChanged(object sender, EventArgs e) {
this.myGridTable.ResetRelationsUI();
Invalidate(layout.Data);
}
private void HeaderBackColorChanged(object sender, EventArgs e) {
if (layout.RowHeadersVisible)
Invalidate(layout.RowHeaders);
if (layout.ColumnHeadersVisible)
Invalidate(layout.ColumnHeaders);
Invalidate(layout.TopLeftHeader);
}
private void HeaderFontChanged(object sender, EventArgs e) {
RecalculateFonts();
PerformLayout();
Invalidate(layout.Inside);
}
private void HeaderForeColorChanged(object sender, EventArgs e) {
if (layout.RowHeadersVisible)
Invalidate(layout.RowHeaders);
if (layout.ColumnHeadersVisible)
Invalidate(layout.ColumnHeaders);
Invalidate(layout.TopLeftHeader);
}
private void LinkColorChanged(object sender, EventArgs e) {
Invalidate(layout.Data);
}
private void LinkHoverColorChanged(object sender, EventArgs e) {
Invalidate(layout.Data);
}
private void PreferredColumnWidthChanged(object sender, EventArgs e) {
// reset the dataGridRows
SetDataGridRows(null, this.DataGridRowsLength);
// layout the horizontal scroll bar
PerformLayout();
// invalidate everything
Invalidate();
}
private void RowHeadersVisibleChanged(object sender, EventArgs e) {
layout.RowHeadersVisible = this.myGridTable == null ? false : this.myGridTable.RowHeadersVisible;
PerformLayout();
InvalidateInside();
}
private void ColumnHeadersVisibleChanged(object sender, EventArgs e) {
layout.ColumnHeadersVisible = this.myGridTable == null ? false : this.myGridTable.ColumnHeadersVisible;
PerformLayout();
InvalidateInside();
}
private void RowHeaderWidthChanged(object sender, EventArgs e) {
if (layout.RowHeadersVisible)
{
PerformLayout();
InvalidateInside();
}
}
private void AllowSortingChanged(object sender, EventArgs e) {
if (!this.myGridTable.AllowSorting && this.listManager != null) {
IList list = this.listManager.List;
if (list is IBindingList)
((IBindingList) list).RemoveSort();
}
}
private void DataSource_RowChanged(object sender, EventArgs ea) {
Debug.WriteLineIf(CompModSwitches.DataGridCursor.TraceVerbose, "DataGridCursor: DataSource_RowChanged");
// it may be the case that our cache was not updated
// to the latest changes in the list : CurrentChanged is fired before
// ListChanged.
// So invalidate the row if there is something to invalidate
DataGridRow[] rows = this.DataGridRows;
if (currentRow < this.DataGridRowsLength) {
InvalidateRow(currentRow);
}
}
/// <devdoc>
/// <para>
/// Fired by the DataSource when row position moves.
/// </para>
/// </devdoc>
private void DataSource_PositionChanged(object sender, EventArgs ea) {
#if DEBUG
inDataSource_PositionChanged = true;
#endif
Debug.WriteLineIf(CompModSwitches.DataGridCursor.TraceVerbose, "DataGridCursor: DataSource_PositionChanged to " + listManager.Position.ToString(CultureInfo.InvariantCulture));
// the grid will get the PositionChanged event
// before the OnItemChanged event when a row will be deleted in the backEnd;
// we still want to keep the old rows when the user deletes the rows using the grid
// and we do not want to do the same work twice when the user adds a row via the grid
if (this.DataGridRowsLength > this.listManager.Count + (policy.AllowAdd?1:0) && !gridState[GRIDSTATE_inDeleteRow]) {
Debug.Assert(!gridState[GRIDSTATE_inAddNewRow] && !gridState[GRIDSTATE_inListAddNew], "how can the list decrease when we are adding a row?");
RecreateDataGridRows();
}
if (this.ListManager.Position != currentRow) {
CurrentCell = new DataGridCell(listManager.Position, currentCol);
}
#if DEBUG
inDataSource_PositionChanged = false;
#endif
}
internal void DataSource_MetaDataChanged(object sender, EventArgs e) {
MetaDataChanged();
}
private bool DataGridSourceHasErrors()
{
if (this.listManager == null)
return false;
for (int i = 0; i < this.listManager.Count; i++)
{
object errObj = this.listManager[i];
if (errObj is IDataErrorInfo)
{
string errString = ((IDataErrorInfo)errObj).Error;
if (errString != null && errString.Length != 0)
return true;
}
}
return false;
}
private void TableStylesCollectionChanged(object sender, CollectionChangeEventArgs ccea) {
// if the users changed the collection of tableStyles
if (sender != this.dataGridTables)
return;
if (this.listManager == null)
return;
if (ccea.Action == CollectionChangeAction.Add) {
DataGridTableStyle tableStyle = (DataGridTableStyle) ccea.Element;
if (this.listManager.GetListName().Equals(tableStyle.MappingName)) {
Debug.Assert(this.myGridTable.IsDefault, "if the table is not default, then it had a name. how can one add another table to the collection w/ the same name and not throw an exception");
SetDataGridTable(tableStyle, true); // true for forcing column creation
SetDataGridRows(null, 0);
}
} else if (ccea.Action == CollectionChangeAction.Remove) {
DataGridTableStyle tableStyle = (DataGridTableStyle) ccea.Element;
if (this.myGridTable.MappingName.Equals(tableStyle.MappingName)) {
Debug.Assert(this.myGridTable.IsDefault, "if the table is not default, then it had a name. how can one add another table to the collection w/ the same name and not throw an exception");
this.defaultTableStyle.GridColumnStyles.ResetDefaultColumnCollection();
SetDataGridTable(this.defaultTableStyle, true); // true for forcing column creation
SetDataGridRows(null, 0);
}
} else {
Debug.Assert(ccea.Action == CollectionChangeAction.Refresh, "what else is possible?");
// we have to search to see if the collection of table styles contains one
// w/ the same name as the list in the dataGrid
DataGridTableStyle newGridTable = this.dataGridTables[listManager.GetListName()];
if (newGridTable == null) {
if (!this.myGridTable.IsDefault) {
// get rid of the old gridColumns
this.defaultTableStyle.GridColumnStyles.ResetDefaultColumnCollection();
SetDataGridTable(this.defaultTableStyle, true); // true for forcing column creation
SetDataGridRows(null, 0);
}
} else {
SetDataGridTable(newGridTable, true); // true for forcing column creation
SetDataGridRows(null, 0);
}
}
}
private void DataSource_ItemChanged(object sender, ItemChangedEventArgs ea) {
Debug.WriteLineIf(CompModSwitches.DataGridCursor.TraceVerbose, "DataGridCursor: DataSource_ItemChanged at index " + ea.Index.ToString(CultureInfo.InvariantCulture));
// if ea.Index == -1, then we invalidate all rows.
if (ea.Index == -1)
{
DataSource_Changed(sender, EventArgs.Empty);
/*
// if there are rows which are invisible, it is more efficient to invalidata layout.Data
if (numVisibleRows <= dataGridRowsLength)
Invalidate(layout.Data);
else
{
Debug.Assert(firstVisibleRow == 0, "if all rows are visible, then how come that first row is not visible?");
for (int i = 0; i < numVisibleRows; i++)
InvalidateRow(firstVisibleRow + numVisibleRows);
}
*/
}
else
{
// let's see how we are doing w/ the errors
object errObj = this.listManager[ea.Index];
bool oldListHasErrors = ListHasErrors;
if (errObj is IDataErrorInfo)
{
if (((IDataErrorInfo)errObj).Error.Length != 0)
ListHasErrors = true;
else if (ListHasErrors)
{
// maybe there was an error that now is fixed
ListHasErrors = DataGridSourceHasErrors();
}
}
// Invalidate the row only if we did not change the ListHasErrors
if (oldListHasErrors == ListHasErrors)
InvalidateRow(ea.Index);
// we need to update the edit box:
// we update the text in the edit box only when the currentRow
// equals the ea.Index
if (editColumn != null && ea.Index == currentRow)
editColumn.UpdateUI(this.ListManager, ea.Index, null);
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.OnBorderStyleChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected virtual void OnBorderStyleChanged(EventArgs e) {
EventHandler eh = Events[EVENT_BORDERSTYLECHANGED] as EventHandler;
if (eh != null) {
eh(this, e);
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.OnCaptionVisibleChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected virtual void OnCaptionVisibleChanged(EventArgs e) {
EventHandler eh = Events[EVENT_CAPTIONVISIBLECHANGED] as EventHandler;
if (eh != null) {
eh(this, e);
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.OnCurrentCellChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected virtual void OnCurrentCellChanged(EventArgs e) {
EventHandler eh = Events[EVENT_CURRENTCELLCHANGED] as EventHandler;
if (eh != null) {
eh(this, e);
}
}
/*
/// <summary>
/// <para>
/// Raises the <see cref='System.Windows.Forms.DataGrid.ColumnHeaderClick'/> event.
/// </para>
/// </summary>
/// <param name='e'>
/// An <see cref='System.EventArgs'/> that contains the event data.
/// </param>
/// <remarks>
/// <para>
/// Raising an event invokes the event-handling method through a delegate. For an
/// overview, see <see topic='cpconProvidingEventFunctionality'/>.
/// </para>
/// <see langword='Notes to Inheritors '/>When overriding <see cref='System.Windows.Forms.DataGrid.OnColumnHeaderClick'/> in an
/// derived class, be sure to call the base class's <see cref='System.Windows.Forms.DataGrid.OnColumnHeaderClick'/> method.
/// </remarks>
protected void OnColumnHeaderClick(EventArgs e) {
RaiseEvent(EVENT_COLUMNHEADERCLICK, e);
}
*/
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.OnFlatModeChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected virtual void OnFlatModeChanged(EventArgs e) {
EventHandler eh = Events[EVENT_FLATMODECHANGED] as EventHandler;
if (eh != null) {
eh(this, e);
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.OnBackgroundColorChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected virtual void OnBackgroundColorChanged(EventArgs e) {
EventHandler eh = Events[EVENT_BACKGROUNDCOLORCHANGED] as EventHandler;
if (eh != null) {
eh(this, e);
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.OnAllowNavigationChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected virtual void OnAllowNavigationChanged(EventArgs e) {
EventHandler eh = Events[EVENT_ALLOWNAVIGATIONCHANGED] as EventHandler;
if (eh != null) {
eh(this, e);
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.OnParentRowsVisibleChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected virtual void OnParentRowsVisibleChanged(EventArgs e) {
EventHandler eh = Events[EVENT_PARENTROWSVISIBLECHANGED] as EventHandler;
if (eh != null) {
eh(this, e);
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.OnParentRowsLabelStyleChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected virtual void OnParentRowsLabelStyleChanged(EventArgs e) {
EventHandler eh = Events[EVENT_PARENTROWSLABELSTYLECHANGED] as EventHandler;
if (eh != null) {
eh(this, e);
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.OnReadOnlyChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected virtual void OnReadOnlyChanged(EventArgs e) {
EventHandler eh = Events[EVENT_READONLYCHANGED] as EventHandler;
if (eh != null) {
eh(this, e);
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.OnNavigate"]/*' />
/// <devdoc>
/// <para>
/// Raises the <see cref='System.Windows.Forms.DataGrid.Navigate'/>
/// event.
/// </para>
/// </devdoc>
protected void OnNavigate(NavigateEventArgs e) {
if (onNavigate != null)
onNavigate(this, e);
}
/*
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.OnNodeClick"]/*' />
/// <devdoc>
/// <para>
/// Raises the <see cref='System.Windows.Forms.DataGrid.ColumnResize'/> event.
/// </para>
/// </devdoc>
protected void OnColumnResize(EventArgs e) {
RaiseEvent(EVENT_COLUMNRESIZE, e);
}
internal void OnLinkClick(EventArgs e) {
RaiseEvent(EVENT_LINKCLICKED, e);
}
*/
internal void OnNodeClick(EventArgs e) {
// if we expanded/collapsed the RelationshipRow
// then we need to layout the vertical scroll bar
//
PerformLayout();
// also, we need to let the hosted edit control that its
// boundaries possibly changed. do this with a call to Edit()
// do this only if the firstVisibleColumn is the editColumn
//
GridColumnStylesCollection columns = this.myGridTable.GridColumnStyles;
if (firstVisibleCol > -1 && firstVisibleCol < columns.Count && columns[firstVisibleCol] == editColumn)
Edit();
// Raise the event for the event listeners
EventHandler handler = (EventHandler)Events[EVENT_NODECLICKED];
if (handler != null) {
handler(this, e);
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.OnRowHeaderClick"]/*' />
/// <devdoc>
/// <para>Raises the <see cref='System.Windows.Forms.DataGrid.RowHeaderClick'/> event.</para>
/// </devdoc>
protected void OnRowHeaderClick(EventArgs e) {
if (onRowHeaderClick != null)
onRowHeaderClick(this, e);
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.OnScroll"]/*' />
/// <devdoc>
/// <para>
/// Raises the <see cref='System.Windows.Forms.DataGrid.Scroll'/> event.
/// </para>
/// </devdoc>
protected void OnScroll(EventArgs e) {
// reset the toolTip information
if (ToolTipProvider != null)
ResetToolTip();
EventHandler handler = (EventHandler)Events[EVENT_SCROLL];
if (handler != null) {
handler(this, e);
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.GridHScrolled"]/*' />
/// <devdoc>
/// <para>
/// Listens
/// for the horizontal scrollbar's scroll
/// event.
/// </para>
/// </devdoc>
protected virtual void GridHScrolled(object sender, ScrollEventArgs se) {
if (!Enabled)
return;
if (DataSource == null) {
Debug.Fail("Horizontal Scrollbar should be disabled without a DataSource.");
return;
}
gridState[GRIDSTATE_isScrolling] = true;
#if DEBUG
Debug.WriteLineIf(CompModSwitches.DataGridScrolling.TraceVerbose, "DataGridScrolling: in GridHScrolled: the scroll event type:");
switch (se.Type)
{
case ScrollEventType.SmallIncrement:
Debug.WriteLineIf(CompModSwitches.DataGridScrolling.TraceVerbose, "small increment");
break;
case ScrollEventType.SmallDecrement:
Debug.WriteLineIf(CompModSwitches.DataGridScrolling.TraceVerbose, "small decrement");
break;
case ScrollEventType.LargeIncrement:
Debug.WriteLineIf(CompModSwitches.DataGridScrolling.TraceVerbose, "Large decrement");
break;
case ScrollEventType.LargeDecrement:
Debug.WriteLineIf(CompModSwitches.DataGridScrolling.TraceVerbose, "small decrement");
break;
case ScrollEventType.ThumbPosition:
Debug.WriteLineIf(CompModSwitches.DataGridScrolling.TraceVerbose, "Thumb Position");
break;
case ScrollEventType.ThumbTrack:
Debug.WriteLineIf(CompModSwitches.DataGridScrolling.TraceVerbose, "Thumb Track");
break;
case ScrollEventType.First:
Debug.WriteLineIf(CompModSwitches.DataGridScrolling.TraceVerbose, "First");
break;
case ScrollEventType.Last:
Debug.WriteLineIf(CompModSwitches.DataGridScrolling.TraceVerbose, "Last");
break;
case ScrollEventType.EndScroll:
Debug.WriteLineIf(CompModSwitches.DataGridScrolling.TraceVerbose, "EndScroll");
break;
}
#endif // DEBUG
if (se.Type == ScrollEventType.SmallIncrement ||
se.Type == ScrollEventType.SmallDecrement)
{
int dCols = (se.Type == ScrollEventType.SmallIncrement)? 1:-1;
if (se.Type == ScrollEventType.SmallDecrement && this.negOffset == 0) {
GridColumnStylesCollection cols = this.myGridTable.GridColumnStyles;
// if the column before the first visible column has width == 0 then skip it
for (int i = this.firstVisibleCol - 1; i >= 0 && cols[i].Width == 0; i --) {
dCols --;
}
}
if (se.Type == ScrollEventType.SmallIncrement && this.negOffset == 0) {
GridColumnStylesCollection cols = this.myGridTable.GridColumnStyles;
for (int i = this.firstVisibleCol; i > -1 && i < cols.Count && cols[i].Width == 0; i ++) {
dCols ++;
}
}
ScrollRight(dCols);
se.NewValue = HorizontalOffset;
}
else if (se.Type != ScrollEventType.EndScroll)
{
HorizontalOffset = se.NewValue;
}
this.gridState[GRIDSTATE_isScrolling] = false;
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.GridVScrolled"]/*' />
/// <devdoc>
/// <para>
/// Listens
/// for the vertical scrollbar's scroll event.
/// </para>
/// </devdoc>
protected virtual void GridVScrolled(object sender, ScrollEventArgs se) {
if (!Enabled)
return;
if (DataSource == null) {
Debug.Fail("Vertical Scrollbar should be disabled without a DataSource.");
return;
}
gridState[GRIDSTATE_isScrolling] = true;
try {
se.NewValue = Math.Min(se.NewValue, DataGridRowsLength - numTotallyVisibleRows);
int dRows = se.NewValue - firstVisibleRow;
ScrollDown(dRows);
} finally {
gridState[GRIDSTATE_isScrolling] = false;
}
}
private void HandleEndCurrentEdit() {
int currentRowSaved = currentRow;
int currentColSaved = currentCol;
String errorMessage = null;
try {
listManager.EndCurrentEdit();
}
catch (Exception e) {
errorMessage = e.Message;
}
if (errorMessage != null) {
DialogResult result = RTLAwareMessageBox.Show(null, SR.GetString(SR.DataGridPushedIncorrectValueIntoColumn,
errorMessage), SR.GetString(SR.DataGridErrorMessageBoxCaption), MessageBoxButtons.YesNo,
MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, 0);
if (result == DialogResult.Yes) {
currentRow = currentRowSaved;
currentCol = currentColSaved;
Debug.Assert(currentRow == ListManager.Position || listManager.Position == -1, "the position in the list manager (" + ListManager.Position + ") is out of sync with the currentRow (" + currentRow + ")" + " and the exception is '" + errorMessage + "'");
// also, make sure that we get the row selector on the currentrow, too
InvalidateRowHeader(currentRow);
Edit();
} else {
// if the user committed a row that used to be addNewRow and the backEnd rejects it,
// and then it tries to navigate down then we should stay in the addNewRow
// in this particular scenario, CancelCurrentEdit will cause the last row to be deleted,
// and this will ultimately call InvalidateRow w/ a row number larger than the number of rows
// so set the currentRow here:
Debug.Assert(result == DialogResult.No, "we only put cancel and ok on the error message box");
this.listManager.PositionChanged -= positionChangedHandler;
this.listManager.CancelCurrentEdit();
this.listManager.Position = currentRow;
this.listManager.PositionChanged += positionChangedHandler;
}
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.OnBackButtonClicked"]/*' />
/// <devdoc>
/// <para>
/// Listens
/// for the caption's back button clicked event.
/// </para>
/// </devdoc>
protected void OnBackButtonClicked(object sender, EventArgs e) {
NavigateBack();
EventHandler handler = (EventHandler)Events[EVENT_BACKBUTTONCLICK];
if (handler != null) handler(this,e);
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.OnBackColorChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected override void OnBackColorChanged(EventArgs e) {
backBrush = new SolidBrush(BackColor);
Invalidate();
base.OnBackColorChanged(e);
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.OnBindingContextChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected override void OnBindingContextChanged(EventArgs e) {
if (this.DataSource != null && !gridState[GRIDSTATE_inSetListManager])
try {
Set_ListManager(this.DataSource, this.DataMember, true, false); // we do not want to create columns
// if the columns are already created
// the grid should not rely on OnBindingContextChanged
// to create columns.
} catch {
// at runtime we will rethrow the exception
if (this.Site == null || !this.Site.DesignMode)
throw;
RTLAwareMessageBox.Show(null, SR.GetString(SR.DataGridExceptionInPaint), null,
MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, 0);
if (this.Visible) BeginUpdateInternal();
ResetParentRows();
Set_ListManager(null, String.Empty, true);
if (this.Visible) EndUpdateInternal();
}
base.OnBindingContextChanged(e);
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.OnDataSourceChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected virtual void OnDataSourceChanged(EventArgs e) {
EventHandler eh = Events[EVENT_DATASOURCECHANGED] as EventHandler;
if (eh != null) {
eh(this, e);
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.OnShowParentDetailsButtonClicked"]/*' />
/// <devdoc>
/// <para>
/// Listens for
/// the caption's down button clicked event.
/// </para>
/// </devdoc>
protected void OnShowParentDetailsButtonClicked(object sender, EventArgs e) {
// we need to fire the ParentRowsVisibleChanged event
// and the ParentRowsVisible property just calls SetParentRowsVisibility and
// then fires the event.
this.ParentRowsVisible = !caption.ToggleDownButtonDirection();
EventHandler handler = (EventHandler)Events[EVENT_DOWNBUTTONCLICK];
if (handler != null) handler(this,e);
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.OnForeColorChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected override void OnForeColorChanged(EventArgs e) {
foreBrush = new SolidBrush(ForeColor);
Invalidate();
base.OnForeColorChanged(e);
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.OnFontChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected override void OnFontChanged(EventArgs e) {
// let the caption know about the event changed
//
this.Caption.OnGridFontChanged();
RecalculateFonts();
RecreateDataGridRows();
// get all the rows in the parentRows stack, and modify their height
if (originalState != null) {
Debug.Assert(!parentRows.IsEmpty(), "if the originalState is not null, then parentRows contains at least one row");
Stack parentStack = new Stack();
// this is a huge performance hit:
// everytime we get/put something from/to
// the parentRows, the buttons in the dataGridCaption
// are invalidated
while (!parentRows.IsEmpty()) {
DataGridState dgs = parentRows.PopTop();
int rowCount = dgs.DataGridRowsLength;
for(int i = 0; i < rowCount; i++) {
// performance hit: this will cause to invalidate a bunch of
// stuff
dgs.DataGridRows[i].Height = dgs.DataGridRows[i].MinimumRowHeight(dgs.GridColumnStyles);
}
parentStack.Push(dgs);
}
while(parentStack.Count != 0) {
parentRows.AddParent((DataGridState)parentStack.Pop());
}
}
base.OnFontChanged(e);
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.OnPaintBackground"]/*' />
/// <devdoc>
/// <para>
/// Raises the <see cref='System.Windows.Forms.Control.PaintBackground'/>
/// event.
/// </para>
/// </devdoc>
protected override void OnPaintBackground(PaintEventArgs ebe) {
// null body
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.OnLayout"]/*' />
/// <devdoc>
/// <para>
/// Raises the <see cref='System.Windows.Forms.Control.Layout'/> event which
/// repositions controls
/// and updates scroll bars.
/// </para>
/// </devdoc>
protected override void OnLayout(LayoutEventArgs levent) {
// if we get a OnLayout event while the editControl changes, then just
// ignore it
//
if (gridState[GRIDSTATE_editControlChanging]) return;
Debug.WriteLineIf(CompModSwitches.DataGridLayout.TraceVerbose, "DataGridLayout: OnLayout");
base.OnLayout(levent);
if (gridState[GRIDSTATE_layoutSuspended]) return;
gridState[GRIDSTATE_canFocus] = false;
try {
if (IsHandleCreated) {
if (layout.ParentRowsVisible)
parentRows.OnLayout();
// reset the toolTip information
if (ToolTipProvider != null)
ResetToolTip();
ComputeLayout();
}
} finally {
gridState[GRIDSTATE_canFocus] = true;
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.OnHandleCreated"]/*' />
/// <devdoc>
/// <para>
/// Raises the <see cref='System.Windows.Forms.Control.CreateHandle'/>
/// event.
/// </para>
/// </devdoc>
protected override void OnHandleCreated(EventArgs e) {
base.OnHandleCreated(e);
// toolTipping
toolTipProvider = new DataGridToolTip(this);
toolTipProvider.CreateToolTipHandle();
toolTipId = 0;
PerformLayout();
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.OnHandleDestroyed"]/*' />
/// <devdoc>
/// <para>
/// Raises the <see cref='System.Windows.Forms.Control.DestroyHandle'/>
/// event.
/// </para>
/// </devdoc>
protected override void OnHandleDestroyed(EventArgs e)
{
base.OnHandleDestroyed(e);
// toolTipping
if (toolTipProvider != null) {
toolTipProvider.Destroy();
toolTipProvider = null;
}
toolTipId = 0;
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.OnEnter"]/*' />
/// <devdoc>
/// <para>Raises the <see cref='System.Windows.Forms.Control.Enter'/>
/// event.</para>
/// </devdoc>
protected override void OnEnter(EventArgs e) {
if (gridState[GRIDSTATE_canFocus] && !gridState[GRIDSTATE_editControlChanging]) {
if (Bound)
{
Edit();
}
base.OnEnter(e);
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.OnLeave"]/*' />
/// <devdoc>
/// <para>Raises the <see cref='System.Windows.Forms.Control.Leave'/>
/// event.</para>
/// </devdoc>
protected override void OnLeave(EventArgs e) {
OnLeave_Grid();
base.OnLeave(e);
}
private void OnLeave_Grid() {
gridState[GRIDSTATE_canFocus] = false;
try {
EndEdit();
if (this.listManager != null && !this.gridState[GRIDSTATE_editControlChanging]) {
if (gridState[GRIDSTATE_inAddNewRow]) {
// if the user did not type anything
// in the addNewRow, then cancel the currentedit
this.listManager.CancelCurrentEdit();
// set the addNewRow back
DataGridRow[] localGridRows = this.DataGridRows;
localGridRows[DataGridRowsLength - 1] = new DataGridAddNewRow(this, this.myGridTable, DataGridRowsLength-1);
SetDataGridRows(localGridRows, DataGridRowsLength);
} else {
// this.listManager.EndCurrentEdit();
HandleEndCurrentEdit();
}
}
} finally {
gridState[GRIDSTATE_canFocus] = true;
// inAddNewRow should be set to false if the control was
// not changing
if (!this.gridState[GRIDSTATE_editControlChanging])
gridState[GRIDSTATE_inAddNewRow] = false;
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.OnKeyDown"]/*' />
/// <devdoc>
/// <para>
/// Raises the <see cref='System.Windows.Forms.Control.KeyDown'/>
/// event.
/// </para>
/// </devdoc>
protected override void OnKeyDown(KeyEventArgs ke) {
Debug.WriteLineIf(CompModSwitches.DataGridKeys.TraceVerbose, "DataGridKeys: OnKeyDown ");
base.OnKeyDown(ke);
ProcessGridKey(ke);
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.OnKeyPress"]/*' />
/// <devdoc>
/// <para>Raises the <see cref='System.Windows.Forms.Control.KeyPress'/> event.</para>
/// </devdoc>
protected override void OnKeyPress(KeyPressEventArgs kpe) {
Debug.WriteLineIf(CompModSwitches.DataGridKeys.TraceVerbose, "DataGridKeys: OnKeyPress " + TypeDescriptor.GetConverter(typeof(Keys)).ConvertToString(kpe.KeyChar));
base.OnKeyPress(kpe);
GridColumnStylesCollection coll = this.myGridTable.GridColumnStyles;
if (coll != null && currentCol > 0 && currentCol < coll.Count) {
if (!coll[currentCol].ReadOnly)
if (kpe.KeyChar > 32) {
Edit(new string(new char [] { (char) kpe.KeyChar}));
}
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.OnMouseDown"]/*' />
/// <devdoc>
/// <para>Raises the <see cref='System.Windows.Forms.Control.MouseDown'/> event.</para>
/// </devdoc>
protected override void OnMouseDown(MouseEventArgs e) {
base.OnMouseDown(e);
gridState[GRIDSTATE_childLinkFocused] = false;
gridState[GRIDSTATE_dragging] = false;
if (listManager == null)
return;
HitTestInfo location = HitTest(e.X, e.Y);
Keys nModifier = ModifierKeys;
bool isControlDown = (nModifier & Keys.Control) == Keys.Control && (nModifier & Keys.Alt) == 0;
bool isShiftDown = (nModifier & Keys.Shift) == Keys.Shift;
// Only left clicks for now
if (e.Button != MouseButtons.Left)
return;
// Check column resize
if (location.type == HitTestType.ColumnResize) {
if (e.Clicks > 1) {
ColAutoResize(location.col);
}
else
ColResizeBegin(e, location.col);
return;
}
// Check row resize
if (location.type == HitTestType.RowResize)
{
if (e.Clicks > 1)
{
RowAutoResize(location.row);
}
else
{
RowResizeBegin(e, location.row);
}
return;
}
// Check column headers
if (location.type == HitTestType.ColumnHeader) {
trackColumnHeader = this.myGridTable.GridColumnStyles[location.col].PropertyDescriptor;
return;
}
if (location.type == HitTestType.Caption) {
Rectangle captionRect = layout.Caption;
caption.MouseDown(e.X - captionRect.X, e.Y - captionRect.Y);
return;
}
if (layout.Data.Contains(e.X, e.Y) || layout.RowHeaders.Contains(e.X, e.Y)) {
// Check with the row underneath the mouse
int row = GetRowFromY(e.Y);
if (row > -1) {
Point p = NormalizeToRow(e.X, e.Y, row);
DataGridRow[] localGridRows = DataGridRows;
if (localGridRows[row].OnMouseDown(p.X, p.Y,
layout.RowHeaders,
isRightToLeft())) {
CommitEdit();
// possibly this was the last row, so then the link may not
// be visible. make it visible, by making the row visible.
// how can we be sure that the user did not click
// on a relationship and navigated to the child rows?
// check if the row is expanded: if the row is expanded, then the user clicked
// on the node. when we navigate to child rows the rows are recreated
// and are initially collapsed
localGridRows = this.DataGridRows;
if (row < DataGridRowsLength && (localGridRows[row] is DataGridRelationshipRow) && ((DataGridRelationshipRow)localGridRows[row]).Expanded)
EnsureVisible(row, 0);
// show the edit box
//
Edit();
return;
}
}
}
// Check row headers
//
if (location.type == HitTestType.RowHeader) {
EndEdit();
if (!(DataGridRows[location.row] is DataGridAddNewRow)) {
int savedCurrentRow = this.currentRow;
CurrentCell = new DataGridCell(location.row, currentCol);
if (location.row != savedCurrentRow &&
this.currentRow != location.row &&
this.currentRow == savedCurrentRow) {
// The data grid was not able to move away from its previous current row.
// Be defensive and don't select the row.
return;
}
}
if (isControlDown) {
if (IsSelected(location.row))
UnSelect(location.row);
else
Select(location.row);
}
else {
if (lastRowSelected == -1 || !isShiftDown)
{
ResetSelection();
Select(location.row);
}
else
{
int lowerRow = Math.Min(lastRowSelected, location.row);
int upperRow = Math.Max(lastRowSelected, location.row);
// we need to reset the old SelectedRows.
// ResetSelection() will also reset lastRowSelected, so we
// need to save it
int saveLastRowSelected = lastRowSelected;
ResetSelection();
lastRowSelected = saveLastRowSelected;
DataGridRow[] rows = DataGridRows;
for (int i = lowerRow; i <= upperRow; i++)
{
rows[i].Selected = true;
numSelectedRows ++;
}
// hide the edit box:
//
EndEdit();
return;
}
}
lastRowSelected = location.row;
// OnRowHeaderClick(EventArgs.Empty);
return;
}
// Check parentRows
//
if (location.type == HitTestType.ParentRows)
{
EndEdit();
parentRows.OnMouseDown(e.X, e.Y, isRightToLeft());
}
// Check cell clicks
//
if (location.type == HitTestType.Cell) {
if (this.myGridTable.GridColumnStyles[location.col].MouseDown(location.row, e.X, e.Y))
return;
DataGridCell target = new DataGridCell(location.row, location.col);
if (policy.AllowEdit && CurrentCell.Equals(target)) {
ResetSelection();
//
// what if only a part of the current cell is visible?
//
EnsureVisible(currentRow, currentCol);
Edit();
}
else {
ResetSelection();
CurrentCell = target;
}
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.OnMouseLeave"]/*' />
/// <devdoc>
/// <para>Creates the <see cref='System.Windows.Forms.Control.MouseLeave'/>
/// event.</para>
/// </devdoc>
protected override void OnMouseLeave(EventArgs e) {
base.OnMouseLeave(e);
if (oldRow != -1) {
DataGridRow[] localGridRows = DataGridRows;
localGridRows[oldRow].OnMouseLeft(layout.RowHeaders, isRightToLeft());
}
if (gridState[GRIDSTATE_overCaption]) {
caption.MouseLeft();
}
// when the mouse leaves the grid control, reset the cursor to arrow
Cursor = null;
}
internal void TextBoxOnMouseWheel(MouseEventArgs e) {
this.OnMouseWheel(e);
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.OnMouseMove"]/*' />
/// <devdoc>
/// <para>Raises the <see cref='System.Windows.Forms.Control.MouseMove'/>
/// event.</para>
/// </devdoc>
protected override void OnMouseMove(MouseEventArgs e) {
base.OnMouseMove(e);
if (listManager == null)
return;
HitTestInfo location = HitTest(e.X, e.Y);
bool alignToRight = isRightToLeft();
// We need to give UI feedback when the user is resizing a column
if (gridState[GRIDSTATE_trackColResize]) {
ColResizeMove(e);
}
if (gridState[GRIDSTATE_trackRowResize])
{
RowResizeMove(e);
}
if (gridState[GRIDSTATE_trackColResize] || location.type == HitTestType.ColumnResize)
{
Cursor = Cursors.SizeWE;
return;
}
else if ( gridState[GRIDSTATE_trackRowResize] || location.type == HitTestType.RowResize)
{
Cursor = Cursors.SizeNS;
return;
}
else
{
Cursor = null;
}
if ((layout.Data.Contains(e.X, e.Y)
|| (layout.RowHeadersVisible && layout.RowHeaders.Contains(e.X, e.Y)))) {
// && (isNavigating || isEditing)) {
DataGridRow[] localGridRows = DataGridRows;
// If we are over a row, let it know about the mouse move.
int rowOver = GetRowFromY(e.Y);
// set the dragging bit:
if (lastRowSelected != -1 && !gridState[GRIDSTATE_dragging]) {
int topRow = GetRowTop(lastRowSelected);
int bottomRow = topRow + localGridRows[lastRowSelected].Height;
int dragHeight = SystemInformation.DragSize.Height;
gridState[GRIDSTATE_dragging] = ((e.Y - topRow < dragHeight && topRow - e.Y < dragHeight) || (e.Y - bottomRow < dragHeight && bottomRow - e.Y < dragHeight));
}
if (rowOver > -1) {
Point p = NormalizeToRow(e.X, e.Y, rowOver);
if (!localGridRows[rowOver].OnMouseMove(p.X, p.Y, layout.RowHeaders, alignToRight) && gridState[GRIDSTATE_dragging]) {
// if the row did not use this, see if selection can use it
MouseButtons mouse = MouseButtons;
if (lastRowSelected != -1 && (mouse & MouseButtons.Left) == MouseButtons.Left
&& !(((Control.ModifierKeys & Keys.Control) == Keys.Control) && (Control.ModifierKeys & Keys.Alt) == 0)){
// ResetSelection() will reset the lastRowSelected too
//
int saveLastRowSelected = lastRowSelected;
ResetSelection();
lastRowSelected = saveLastRowSelected;
int lowerRow = Math.Min(lastRowSelected, rowOver);
int upperRow = Math.Max(lastRowSelected, rowOver);
DataGridRow[] rows = DataGridRows;
for (int i = lowerRow; i <= upperRow; i++)
{
rows[i].Selected = true;
numSelectedRows ++;
}
}
}
}
if (oldRow != rowOver && oldRow != -1) {
localGridRows[oldRow].OnMouseLeft(layout.RowHeaders, alignToRight);
}
oldRow = rowOver;
}
// check parentRows
//
if (location.type == HitTestType.ParentRows)
{
if (parentRows != null)
{
parentRows.OnMouseMove(e.X, e.Y);
}
}
if (location.type == HitTestType.Caption) {
gridState[GRIDSTATE_overCaption] = true;
Rectangle captionRect = layout.Caption;
caption.MouseOver(e.X - captionRect.X, e.Y - captionRect.Y);
return;
}
else {
if (gridState[GRIDSTATE_overCaption]) {
gridState[GRIDSTATE_overCaption] = false;
caption.MouseLeft();
}
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.OnMouseUp"]/*' />
/// <devdoc>
/// <para>Raises the <see cref='System.Windows.Forms.Control.MouseUp'/> event.</para>
/// </devdoc>
protected override void OnMouseUp(MouseEventArgs e) {
base.OnMouseUp(e);
gridState[GRIDSTATE_dragging] = false;
if (listManager == null || myGridTable == null)
return;
if (gridState[GRIDSTATE_trackColResize]) {
ColResizeEnd(e);
}
if (gridState[GRIDSTATE_trackRowResize])
{
RowResizeEnd(e);
}
gridState[GRIDSTATE_trackColResize] = false;
gridState[GRIDSTATE_trackRowResize] = false;
HitTestInfo ci = HitTest(e.X, e.Y);
if ((ci.type & HitTestType.Caption) == HitTestType.Caption) {
caption.MouseUp(e.X, e.Y);
}
// Check column headers
if (ci.type == HitTestType.ColumnHeader) {
PropertyDescriptor prop = this.myGridTable.GridColumnStyles[ci.col].PropertyDescriptor;
if (prop == trackColumnHeader) {
ColumnHeaderClicked(trackColumnHeader);
}
}
trackColumnHeader = null;
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.OnMouseWheel"]/*' />
/// <devdoc>
/// <para>Raises the <see cref='System.Windows.Forms.Control.MouseWheel'/> event.</para>
/// </devdoc>
protected override void OnMouseWheel(MouseEventArgs e) {
base.OnMouseWheel(e);
if (e is HandledMouseEventArgs) {
if (((HandledMouseEventArgs) e).Handled) {
// The application event handler handled the scrolling - don't do anything more.
return;
}
((HandledMouseEventArgs) e).Handled = true;
}
bool wheelingDown = true;
if ((ModifierKeys & Keys.Control) != 0)
wheelingDown = false;
if (listManager == null || myGridTable == null)
return;
ScrollBar sb = wheelingDown ? vertScrollBar : horizScrollBar;
if (!sb.Visible)
return;
// so we scroll. we have to know this, cause otherwise we will call EndEdit
// and that would be wrong.
gridState[GRIDSTATE_isScrolling] = true;
wheelDelta += e.Delta;
float movePerc = (float)wheelDelta / (float)NativeMethods.WHEEL_DELTA;
int move = (int)((float)SystemInformation.MouseWheelScrollLines * movePerc);
if (move != 0) {
wheelDelta = 0;
Debug.WriteLineIf(CompModSwitches.DataGridLayout.TraceVerbose, "DataGridLayout: OnMouseWheel move="+move.ToString(CultureInfo.InvariantCulture));
if (wheelingDown) {
int newRow = firstVisibleRow - move;
newRow = Math.Max(0, Math.Min(newRow, DataGridRowsLength - numTotallyVisibleRows));
ScrollDown(newRow - firstVisibleRow);
}
else {
int newValue = horizScrollBar.Value + (move < 0 ? 1 : -1) * horizScrollBar.LargeChange;
HorizontalOffset = newValue;
}
}
gridState[GRIDSTATE_isScrolling] = false;
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.OnPaint"]/*' />
/// <devdoc>
/// <para>Raises the <see cref='System.Windows.Forms.Control.Paint'/>
/// event.</para>
/// </devdoc>
protected override void OnPaint(PaintEventArgs pe) {
try {
CheckHierarchyState();
if (this.layout.dirty)
ComputeLayout();
Graphics g = pe.Graphics;
Region clipRegion = g.Clip;
if (layout.CaptionVisible)
caption.Paint(g, layout.Caption, isRightToLeft());
if (layout.ParentRowsVisible) {
Debug.WriteLineIf(CompModSwitches.DataGridParents.TraceVerbose, "DataGridParents: Painting ParentRows " + layout.ParentRows.ToString());
g.FillRectangle(SystemBrushes.AppWorkspace, layout.ParentRows);
parentRows.Paint(g, layout.ParentRows, isRightToLeft());
}
Rectangle gridRect = layout.Data;
if (layout.RowHeadersVisible)
gridRect = Rectangle.Union(gridRect, layout.RowHeaders);
if (layout.ColumnHeadersVisible)
gridRect = Rectangle.Union(gridRect, layout.ColumnHeaders);
g.SetClip(gridRect);
PaintGrid(g, gridRect);
g.Clip = clipRegion;
clipRegion.Dispose();
PaintBorder(g, layout.ClientRectangle);
g.FillRectangle(DefaultHeaderBackBrush, layout.ResizeBoxRect);
base.OnPaint(pe); // raise paint event
}
catch {
// at runtime we will rethrow the exception
if (this.Site == null || !this.Site.DesignMode)
throw;
gridState[GRIDSTATE_exceptionInPaint] = true;
try {
RTLAwareMessageBox.Show(null, SR.GetString(SR.DataGridExceptionInPaint), null, MessageBoxButtons.OK,
MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, 0);
if (this.Visible) BeginUpdateInternal();
ResetParentRows();
Set_ListManager(null, String.Empty, true);
} finally {
gridState[GRIDSTATE_exceptionInPaint] = false;
if (this.Visible) EndUpdateInternal();
}
}
}
// Since Win32 only invalidates the area that gets uncovered,
// we have to manually invalidate the old border area
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.OnResize"]/*' />
/// <devdoc>
/// <para>Raises the <see cref='System.Windows.Forms.Control.Resize'/> event.</para>
/// </devdoc>
protected override void OnResize(EventArgs e) {
Debug.WriteLineIf(CompModSwitches.DataGridLayout.TraceVerbose, "DataGridLayout: OnResize");
if (layout.CaptionVisible)
Invalidate(layout.Caption);
if (layout.ParentRowsVisible)
parentRows.OnResize(layout.ParentRows);
int borderWidth = BorderWidth;
Rectangle right;
Rectangle bottom;
Rectangle oldClientRectangle = layout.ClientRectangle;
right = new Rectangle(oldClientRectangle.X + oldClientRectangle.Width - borderWidth,
oldClientRectangle.Y,
borderWidth,
oldClientRectangle.Height);
bottom = new Rectangle(oldClientRectangle.X,
oldClientRectangle.Y + oldClientRectangle.Height - borderWidth,
oldClientRectangle.Width,
borderWidth);
Rectangle newClientRectangle = this.ClientRectangle;
if (newClientRectangle.Width != oldClientRectangle.Width) {
Invalidate(right);
right = new Rectangle(newClientRectangle.X + newClientRectangle.Width - borderWidth,
newClientRectangle.Y,
borderWidth,
newClientRectangle.Height);
Invalidate(right);
}
if (newClientRectangle.Height != oldClientRectangle.Height) {
Invalidate(bottom);
bottom = new Rectangle(newClientRectangle.X,
newClientRectangle.Y + newClientRectangle.Height - borderWidth,
newClientRectangle.Width,
borderWidth);
Invalidate(bottom);
}
//also, invalidate the ResizeBoxRect
if (!this.layout.ResizeBoxRect.IsEmpty)
Invalidate(layout.ResizeBoxRect);
layout.ClientRectangle = newClientRectangle;
int oldFirstVisibleRow = firstVisibleRow;
base.OnResize(e);
if (isRightToLeft() || oldFirstVisibleRow != firstVisibleRow)
Invalidate();
}
internal void OnRowHeightChanged(DataGridRow row) {
ClearRegionCache();
int cy = GetRowTop(row.RowNumber);
if (cy > 0) {
Rectangle refresh = new Rectangle();
refresh.Y = cy;
refresh.X = layout.Inside.X;
refresh.Width = layout.Inside.Width;
refresh.Height = layout.Inside.Bottom - cy;
Invalidate(refresh);
}
}
internal void ParentRowsDataChanged() {
Debug.Assert(originalState != null, "how can we get a list changed event from another listmanager/list while not navigating");
// do the reset work that is done in SetDataBindings, set_DataSource, set_DataMember;
parentRows.Clear();
caption.BackButtonActive = caption.DownButtonActive = caption.BackButtonVisible = false;
caption.SetDownButtonDirection(!layout.ParentRowsVisible);
object dSource = originalState.DataSource;
string dMember = originalState.DataMember;
// we don't need to set the GRIDSTATE_metaDataChanged bit, cause
// the listManager from the originalState should be different from the current listManager
//
// set the originalState to null so that Set_ListManager knows that
// it has to unhook the MetaDataChanged events
originalState = null;
Set_ListManager(dSource, dMember, true);
}
// =------------------------------------------------------------------
// = Methods
// =------------------------------------------------------------------
private void AbortEdit() {
Debug.WriteLineIf(CompModSwitches.DataGridEditing.TraceVerbose, "DataGridEditing: \t! AbortEdit");
Debug.Assert(gridState[GRIDSTATE_isEditing], "Can't abort an edit that is not happening!");
// the same rules from editColumn.OnEdit
// while changing the editControl's visibility, do not
// PerformLayout on the entire DataGrid
gridState[GRIDSTATE_editControlChanging] = true;
editColumn.Abort(editRow.RowNumber);
// reset the editControl flag:
gridState[GRIDSTATE_editControlChanging] = false;
gridState[GRIDSTATE_isEditing] = false;
editRow = null;
editColumn = null;
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.Navigate"]/*' />
/// <devdoc>
/// <para>Occurs when the user navigates to a new table.</para>
/// </devdoc>
[SRCategory(SR.CatAction), SRDescription(SR.DataGridNavigateEventDescr)]
public event NavigateEventHandler Navigate {
add {
onNavigate += value;
}
remove {
onNavigate -= value;
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.RowHeaderClick"]/*' />
/// <devdoc>
/// <para>Occurs when a row header is clicked.</para>
/// </devdoc>
protected event EventHandler RowHeaderClick {
add {
onRowHeaderClick += value;
}
remove {
onRowHeaderClick -= value;
}
}
/// <devdoc>
/// <para>
/// Adds an event handler for the 'System.Windows.Forms.DataGrid.OnNodeClick'
/// event.
/// </para>
/// </devdoc>
[SRCategory(SR.CatAction), SRDescription(SR.DataGridNodeClickEventDescr)]
internal event EventHandler NodeClick {
add {
Events.AddHandler(EVENT_NODECLICKED, value);
}
remove {
Events.RemoveHandler(EVENT_NODECLICKED, value);
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.Scroll"]/*' />
/// <devdoc>
/// <para>
/// Occurs when the user scrolls the <see cref='System.Windows.Forms.DataGrid'/> control.
/// </para>
/// </devdoc>
[SRCategory(SR.CatAction), SRDescription(SR.DataGridScrollEventDescr)]
public event EventHandler Scroll {
add {
Events.AddHandler(EVENT_SCROLL, value);
}
remove {
Events.RemoveHandler(EVENT_SCROLL, value);
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.Site"]/*' />
public override ISite Site {
get {
return base.Site;
}
set {
ISite temp = this.Site;
base.Site = value;
if (value != temp && !Disposing) {
// we should site the tables and the columns
// only when our site changes
SubObjectsSiteChange(false);
SubObjectsSiteChange(true);
}
}
}
internal void AddNewRow() {
EnsureBound();
ResetSelection();
// EndEdit();
UpdateListManager();
gridState[GRIDSTATE_inListAddNew] = true;
gridState[GRIDSTATE_inAddNewRow] = true;
try {
this.ListManager.AddNew();
} catch {
gridState[GRIDSTATE_inListAddNew] = false;
gridState[GRIDSTATE_inAddNewRow] = false;
PerformLayout();
InvalidateInside();
throw;
}
gridState[GRIDSTATE_inListAddNew] = false;
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.BeginEdit"]/*' />
/// <devdoc>
/// <para>Attempts to
/// put the grid into a state where editing is
/// allowed.</para>
/// </devdoc>
public bool BeginEdit(DataGridColumnStyle gridColumn, int rowNumber) {
if (this.DataSource == null || this.myGridTable == null)
return false;
// We deny edit requests if we are already editing a cell.
if (gridState[GRIDSTATE_isEditing])
return false;
else {
int col = -1;
if ((col = this.myGridTable.GridColumnStyles.IndexOf(gridColumn)) < 0)
return false;
CurrentCell = new DataGridCell(rowNumber, col);
ResetSelection();
Edit();
return true;
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.BeginInit"]/*' />
/// <devdoc>
/// <para>Specifies the beginning of the initialization code.</para>
/// </devdoc>
public void BeginInit() {
if (inInit)
throw new InvalidOperationException(SR.GetString(SR.DataGridBeginInit));
inInit = true;
}
private Rectangle CalcRowResizeFeedbackRect(MouseEventArgs e) {
Rectangle inside = layout.Data;
Rectangle r = new Rectangle(inside.X, e.Y, inside.Width, 3);
r.Y = Math.Min(inside.Bottom - 3, r.Y);
r.Y = Math.Max(r.Y , 0);
return r;
}
private Rectangle CalcColResizeFeedbackRect(MouseEventArgs e) {
Rectangle inside = layout.Data;
Rectangle r = new Rectangle(e.X, inside.Y, 3, inside.Height);
r.X = Math.Min(inside.Right - 3, r.X);
r.X = Math.Max(r.X , 0);
return r;
}
private void CancelCursorUpdate() {
Debug.WriteLineIf(CompModSwitches.DataGridCursor.TraceVerbose, "DataGridCursor: Requesting CancelEdit()");
if (listManager != null) {
EndEdit();
listManager.CancelCurrentEdit();
}
}
private void CheckHierarchyState() {
if (checkHierarchy && this.listManager != null && this.myGridTable != null) {
if (myGridTable == null)
// there was nothing to check
return;
for (int j = 0; j < myGridTable.GridColumnStyles.Count; j++) {
DataGridColumnStyle gridColumn = myGridTable.GridColumnStyles[j];
}
checkHierarchy = false;
}
}
/// <devdoc>
/// The DataGrid caches an array of rectangular areas
/// which represent the area which scrolls left to right.
/// This method is invoked whenever the DataGrid's
/// scrollable regions change in such a way as to require
/// a re-recalculation.
/// </devdoc>
private void ClearRegionCache() {
cachedScrollableRegion = null;
}
/// <devdoc>
/// Determines the best fit size for the given column.
/// </devdoc>
private void ColAutoResize(int col) {
Debug.WriteLineIf(CompModSwitches.DataGridLayout.TraceVerbose, "DataGridLayout: ColAutoResize");
EndEdit();
CurrencyManager listManager = this.listManager;
if (listManager == null)
return;
int size;
Graphics g = CreateGraphicsInternal();
try {
DataGridColumnStyle column = myGridTable.GridColumnStyles[col];
string columnName = column.HeaderText;
Font headerFont;
if (this.myGridTable.IsDefault)
headerFont = this.HeaderFont;
else
headerFont = this.myGridTable.HeaderFont;
size = (int) g.MeasureString(columnName, headerFont).Width + layout.ColumnHeaders.Height + 1; // This is not a bug, the sort triangle's width is equal to it's height.
int rowCount = listManager.Count;
for (int row = 0; row < rowCount; ++row) {
object value = column.GetColumnValueAtRow(listManager, row);
int width = column.GetPreferredSize(g, value).Width;
if (width > size)
size = width;
}
if (column.Width != size) {
column.width = size;
ComputeVisibleColumns();
bool lastColumnIsLastTotallyVisibleCol = true;
if (this.lastTotallyVisibleCol != -1) {
for (int i = this.lastTotallyVisibleCol + 1; i < this.myGridTable.GridColumnStyles.Count; i ++) {
if (this.myGridTable.GridColumnStyles[i].PropertyDescriptor != null) {
lastColumnIsLastTotallyVisibleCol = false;
break;
}
}
} else {
lastColumnIsLastTotallyVisibleCol = false;
}
// if the column shrank and the last totally visible column was the last column
// then we need to recompute the horizontalOffset, firstVisibleCol, negOffset.
// lastTotallyVisibleCol remains the last column
if (lastColumnIsLastTotallyVisibleCol &&
(this.negOffset != 0 || this.horizontalOffset != 0)) {
// update the column width
column.width = size;
int cx = 0;
int colCount = this.myGridTable.GridColumnStyles.Count;
int visibleWidth = layout.Data.Width;
GridColumnStylesCollection cols = this.myGridTable.GridColumnStyles;
// assume everything fits
this.negOffset = 0;
this.horizontalOffset = 0;
this.firstVisibleCol= 0;
for (int i = colCount - 1; i >= 0; i --) {
if (cols[i].PropertyDescriptor == null) {
continue;
}
cx += cols[i].Width;
if (cx > visibleWidth) {
if (this.negOffset == 0) {
this.firstVisibleCol = i;
this.negOffset = cx - visibleWidth;
this.horizontalOffset = this.negOffset;
this.numVisibleCols ++;
} else {
this.horizontalOffset += cols[i].Width;
}
} else {
this.numVisibleCols ++;
}
}
// refresh the horizontal scrollbar
PerformLayout();
// we need to invalidate the layout.Data and layout.ColumnHeaders
Invalidate(Rectangle.Union(layout.Data, layout.ColumnHeaders));
} else {
// need to refresh the scroll bar
PerformLayout();
Rectangle rightArea = layout.Data;
if (layout.ColumnHeadersVisible)
rightArea = Rectangle.Union(rightArea, layout.ColumnHeaders);
int left = GetColBeg(col);
if (!isRightToLeft()) {
rightArea.Width -= left - rightArea.X;
rightArea.X = left;
} else {
rightArea.Width = rightArea.Width - left;
}
Invalidate(rightArea);
}
}
}
finally {
g.Dispose();
}
if (this.horizScrollBar.Visible) {
this.horizScrollBar.Value = HorizontalOffset;
}
// OnColumnResize(EventArgs.Empty);
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.Collapse"]/*' />
/// <devdoc>
/// <para>
/// Collapses child relations, if any exist for all rows, or for a
/// specified row.
/// </para>
/// </devdoc>
public void Collapse(int row) {
SetRowExpansionState(row, false);
}
private void ColResizeBegin(MouseEventArgs e, int col) {
Debug.WriteLineIf(CompModSwitches.DataGridLayout.TraceVerbose, "DataGridLayout: ColResizeBegin");
Debug.Assert(myGridTable != null, "Column resizing operations can't be called when myGridTable == null.");
int x = e.X;
EndEdit();
Rectangle clip = Rectangle.Union(layout.ColumnHeaders, layout.Data);
if (isRightToLeft())
{
clip.Width = GetColBeg(col) - layout.Data.X - 2;
}
else
{
int leftEdge = GetColBeg(col);
clip.X = leftEdge + 3;
clip.Width = layout.Data.X + layout.Data.Width - leftEdge - 2;
}
CaptureInternal = true;
Cursor.ClipInternal = RectangleToScreen(clip);
gridState[GRIDSTATE_trackColResize] = true;
trackColAnchor = x;
trackColumn = col;
DrawColSplitBar(e);
lastSplitBar = e;
}
private void ColResizeMove(MouseEventArgs e) {
if (lastSplitBar != null) {
DrawColSplitBar(lastSplitBar);
lastSplitBar = e;
}
DrawColSplitBar(e);
}
private void ColResizeEnd(MouseEventArgs e) {
Debug.WriteLineIf(CompModSwitches.DataGridLayout.TraceVerbose, "DataGridLayout: ColResizeEnd");
Debug.Assert(myGridTable != null, "Column resizing operations can't be called when myGridTable == null.");
this.gridState[GRIDSTATE_layoutSuspended] = true;
try {
if (lastSplitBar != null) {
DrawColSplitBar(lastSplitBar);
lastSplitBar = null;
}
bool rightToLeft = isRightToLeft();
int x = rightToLeft ? Math.Max(e.X, layout.Data.X) : Math.Min(e.X, layout.Data.Right + 1);
int delta = x - GetColEnd(trackColumn);
if (rightToLeft) delta = -delta;
if (trackColAnchor != x && delta != 0) {
DataGridColumnStyle column = myGridTable.GridColumnStyles[trackColumn];
int proposed = column.Width + delta;
proposed = Math.Max(proposed, 3);
column.Width = proposed;
// refresh scrolling data: horizontalOffset, negOffset, firstVisibleCol, numVisibleCols, lastTotallyVisibleCol
ComputeVisibleColumns();
bool lastColumnIsLastTotallyVisibleCol = true;
for (int i = this.lastTotallyVisibleCol + 1; i < this.myGridTable.GridColumnStyles.Count; i ++) {
if (this.myGridTable.GridColumnStyles[i].PropertyDescriptor != null) {
lastColumnIsLastTotallyVisibleCol = false;
break;
}
}
if (lastColumnIsLastTotallyVisibleCol &&
(this.negOffset != 0 || this.horizontalOffset != 0)) {
int cx = 0;
int colCount = this.myGridTable.GridColumnStyles.Count;
int visibleWidth = this.layout.Data.Width;
GridColumnStylesCollection cols = this.myGridTable.GridColumnStyles;
// assume everything fits
this.negOffset = 0;
this.horizontalOffset = 0;
this.firstVisibleCol = 0;
for (int i = colCount - 1; i > -1; i --) {
if (cols[i].PropertyDescriptor == null) {
continue;
}
cx += cols[i].Width;
if (cx > visibleWidth) {
if (this.negOffset == 0) {
this.negOffset = cx - visibleWidth;
this.firstVisibleCol = i;
this.horizontalOffset = negOffset;
this.numVisibleCols ++;
} else {
this.horizontalOffset += cols[i].Width;
}
} else {
this.numVisibleCols ++;
}
}
// and invalidate pretty much everything
Invalidate(Rectangle.Union(this.layout.Data, this.layout.ColumnHeaders));
} else {
Rectangle rightArea = Rectangle.Union(layout.ColumnHeaders, layout.Data);
int left = GetColBeg(trackColumn);
rightArea.Width -= rightToLeft ? rightArea.Right - left : left - rightArea.X;
rightArea.X = rightToLeft ? layout.Data.X : left;
Invalidate(rightArea);
}
}
}
finally {
Cursor.ClipInternal = Rectangle.Empty;
CaptureInternal = false;
this.gridState[GRIDSTATE_layoutSuspended] = false;
}
PerformLayout();
if (this.horizScrollBar.Visible) {
this.horizScrollBar.Value = HorizontalOffset;
}
// OnColumnResize(EventArgs.Empty);
}
private void MetaDataChanged() {
// when we reset the Binding in the grid, we need to clear the parent rows.
// the same goes for all the caption UI: reset it when the datasource changes.
//
parentRows.Clear();
caption.BackButtonActive = caption.DownButtonActive = caption.BackButtonVisible = false;
caption.SetDownButtonDirection(!layout.ParentRowsVisible);
gridState[GRIDSTATE_metaDataChanged] = true;
try {
if (originalState != null) {
// set the originalState to null so that Set_ListManager knows that
// it has to unhook the MetaDataChanged events
Set_ListManager(originalState.DataSource, originalState.DataMember, true);
originalState = null;
} else {
Set_ListManager(this.DataSource, this.DataMember, true);
}
} finally {
gridState[GRIDSTATE_metaDataChanged] = false;
}
}
// =------------------------------------------------------------------
// = Functions to resize rows
// =------------------------------------------------------------------
// will autoResize "row"
private void RowAutoResize(int row)
{
Debug.WriteLineIf(CompModSwitches.DataGridLayout.TraceVerbose, "DataGridLayout: RowAutoResize");
EndEdit();
CurrencyManager listManager = this.ListManager;
if (listManager == null)
return;
Graphics g = CreateGraphicsInternal();
try
{
GridColumnStylesCollection columns = myGridTable.GridColumnStyles;
DataGridRow resizeRow = DataGridRows[row];
int rowCount = listManager.Count;
int resizeHeight = 0;
// compute the height that we should resize to:
int columnsCount = columns.Count;
for (int col = 0; col < columnsCount; col++) {
object value = columns[col].GetColumnValueAtRow(listManager, row);
resizeHeight = Math.Max(resizeHeight, columns[col].GetPreferredHeight(g, value));
}
if (resizeRow.Height != resizeHeight)
{
resizeRow.Height = resizeHeight;
// needed to refresh scrollbar properties
PerformLayout();
Rectangle rightArea = layout.Data;
if (layout.RowHeadersVisible)
rightArea = Rectangle.Union(rightArea, layout.RowHeaders);
int top = GetRowTop(row);
rightArea.Height -= rightArea.Y - top;
rightArea.Y = top;
Invalidate(rightArea);
}
}
finally
{
g.Dispose();
}
// OnRowResize(EventArgs.Empty);
return;
}
private void RowResizeBegin(MouseEventArgs e, int row)
{
Debug.WriteLineIf(CompModSwitches.DataGridLayout.TraceVerbose, "DataGridLayout: RowResizeBegin");
Debug.Assert(myGridTable != null, "Row resizing operations can't be called when myGridTable == null.");
int y = e.Y;
EndEdit();
Rectangle clip = Rectangle.Union(layout.RowHeaders, layout.Data);
int topEdge = GetRowTop(row);
clip.Y = topEdge + 3;
clip.Height = layout.Data.Y + layout.Data.Height - topEdge - 2;
CaptureInternal = true;
Cursor.ClipInternal = RectangleToScreen(clip);
gridState[GRIDSTATE_trackRowResize] = true;
trackRowAnchor = y;
trackRow = row;
DrawRowSplitBar(e);
lastSplitBar = e;
}
private void RowResizeMove(MouseEventArgs e)
{
if (lastSplitBar != null) {
DrawRowSplitBar(lastSplitBar);
lastSplitBar = e;
}
DrawRowSplitBar(e);
}
private void RowResizeEnd(MouseEventArgs e)
{
Debug.WriteLineIf(CompModSwitches.DataGridLayout.TraceVerbose, "DataGridLayout: RowResizeEnd");
Debug.Assert(myGridTable != null, "Row resizing operations can't be called when myGridTable == null.");
try {
if (lastSplitBar != null) {
DrawRowSplitBar(lastSplitBar);
lastSplitBar = null;
}
int y = Math.Min(e.Y, layout.Data.Y + layout.Data.Height + 1);
int delta = y - GetRowBottom(trackRow);
if (trackRowAnchor != y && delta != 0) {
DataGridRow row = DataGridRows[trackRow];
int proposed = row.Height + delta;
proposed = Math.Max(proposed, 3);
row.Height = proposed;
// needed to refresh scrollbar properties
PerformLayout();
Rectangle rightArea = Rectangle.Union(layout.RowHeaders, layout.Data);
int top = GetRowTop(trackRow);
rightArea.Height -= rightArea.Y - top;
rightArea.Y = top;
Invalidate(rightArea);
}
}
finally {
Cursor.ClipInternal = Rectangle.Empty;
CaptureInternal = false;
}
// OnRowResize(EventArgs.Empty);
}
/// <devdoc>
/// Fires the ColumnHeaderClicked event and handles column
/// sorting.
/// </devdoc>
private void ColumnHeaderClicked(PropertyDescriptor prop) {
if (!CommitEdit())
return;
// OnColumnHeaderClick(EventArgs.Empty);
bool allowSorting;
if (this.myGridTable.IsDefault)
allowSorting = this.AllowSorting;
else
allowSorting = myGridTable.AllowSorting;
if (!allowSorting)
return;
// if (CompModSwitches.DataGridCursor.OutputVerbose) Debug.WriteLine("DataGridCursor: We are about to sort column " + col.ToString());
ListSortDirection direction = this.ListManager.GetSortDirection();
PropertyDescriptor sortColumn = this.ListManager.GetSortProperty();
if (sortColumn != null && sortColumn.Equals(prop))
direction = (direction == ListSortDirection.Ascending) ? ListSortDirection.Descending : ListSortDirection.Ascending;
else
// defaultSortDirection : ascending
direction = ListSortDirection.Ascending;
if (listManager.Count == 0)
return;
this.ListManager.SetSort(prop, direction);
ResetSelection();
InvalidateInside();
}
/// <devdoc>
/// Attempts to commit editing if a cell is being edited.
/// Return true if successfully commited editing.
/// Return false if editing can not be completed and the gird must
/// remain in our current Edit state.
/// </devdoc>
private bool CommitEdit() {
Debug.WriteLineIf(CompModSwitches.DataGridEditing.TraceVerbose, "DataGridEditing: \t CommitEdit " + (editRow == null ? "" : editRow.RowNumber.ToString(CultureInfo.InvariantCulture)));
// we want to commit the editing if
// 1. the user was editing or navigating around the data grid and
// 2. this is not the result of moving focus inside the data grid and
// 3. if the user was scrolling
if (!gridState[GRIDSTATE_isEditing] && !gridState[GRIDSTATE_isNavigating] || (gridState[GRIDSTATE_editControlChanging] && !gridState[GRIDSTATE_isScrolling]))
return true;
// the same rules from editColumn.OnEdit
// flag that we are editing the Edit control, so if we get a OnLayout on the
// datagrid side of things while the edit control changes its visibility and bounds
// the datagrid does not perform a layout
gridState[GRIDSTATE_editControlChanging] = true;
if (editColumn.ReadOnly || gridState[GRIDSTATE_inAddNewRow]) {
bool focusTheGrid = false;
if (this.ContainsFocus) {
focusTheGrid = true;
}
if (focusTheGrid && gridState[GRIDSTATE_canFocus])
this.FocusInternal();
editColumn.ConcedeFocus();
// set the focus back to the grid
if (focusTheGrid && gridState[GRIDSTATE_canFocus] && CanFocus && !Focused)
this.FocusInternal();
// reset the editControl flag
gridState[GRIDSTATE_editControlChanging] = false;
return true;
}
bool retVal = editColumn.Commit(ListManager, currentRow);
// reset the editControl flag
gridState[GRIDSTATE_editControlChanging] = false;
if (retVal)
gridState[GRIDSTATE_isEditing] = false;
return retVal;
}
/// <devdoc>
/// Figure out how many rows we need to scroll down
/// to move targetRow into visibility.
/// </devdoc>
private int ComputeDeltaRows(int targetRow) {
//Debug.WriteLineIf(CompModSwitches.DataGridScrolling.TraceVerbose, "DataGridScrolling: ComputeDeltaRows, firstVisibleRow = "
// + firstVisibleRow.ToString() + ", "
// + "targetRow = " + targetRow.ToString());
if (firstVisibleRow == targetRow)
return 0;
int dRows = 0;
int firstVisibleRowLogicalTop = -1;
int targetRowLogicalTop = -1;
int nRows = DataGridRowsLength;
int cy = 0;
DataGridRow[] localGridRows = DataGridRows;
for (int row = 0; row < nRows; ++row) {
if (row == firstVisibleRow)
firstVisibleRowLogicalTop = cy;
if (row == targetRow)
targetRowLogicalTop = cy;
if (targetRowLogicalTop != -1 && firstVisibleRowLogicalTop != -1)
break;
cy += localGridRows[row].Height;
}
int targetRowLogicalBottom = targetRowLogicalTop + localGridRows[targetRow].Height;
int dataLogicalBottom = layout.Data.Height + firstVisibleRowLogicalTop;
if (targetRowLogicalBottom > dataLogicalBottom) {
// we need to move down.
int downDelta = targetRowLogicalBottom - dataLogicalBottom;
firstVisibleRowLogicalTop += downDelta;
}
else if (firstVisibleRowLogicalTop < targetRowLogicalTop) {
// we don't need to move
return 0;
}
else {
// we need to move up.
int upDelta = firstVisibleRowLogicalTop - targetRowLogicalTop;
firstVisibleRowLogicalTop -= upDelta;
}
int newFirstRow = ComputeFirstVisibleRow(firstVisibleRowLogicalTop);
dRows = (newFirstRow - firstVisibleRow);
//Debug.WriteLineIf(CompModSwitches.DataGridScrolling.TraceVerbose, "DataGridScrolling: ComputeDeltaRows returning " + dRows.ToString());
return dRows;
}
/// <devdoc>
/// Given the a logical vertical offset, figure out
/// which row number should be the first fully visible
/// row on or after the offset.
/// </devdoc>
private int ComputeFirstVisibleRow(int firstVisibleRowLogicalTop) {
int first;
int nRows = DataGridRowsLength;
int cy = 0;
DataGridRow[] localGridRows = DataGridRows;
for (first = 0; first < nRows; ++first) {
if (cy >= firstVisibleRowLogicalTop)
break;
cy += localGridRows[first].Height;
}
return first;
}
/// <devdoc>
/// Constructs an updated Layout object.
/// </devdoc>
private void ComputeLayout() {
Debug.WriteLineIf(CompModSwitches.DataGridLayout.TraceVerbose, "DataGridLayout: ComputeLayout");
bool alignLeft = ! isRightToLeft();
Rectangle oldResizeRect = this.layout.ResizeBoxRect;
// hide the EditBox
EndEdit();
ClearRegionCache();
// NOTE : Since Rectangles are structs, then assignment is a
// : copy. Therefore, after saying "Rectangle inside = newLayout.Inside",
// : we must always assign back to newLayout.Inside.
//
// Important since all of the visibility flags will move
// to the new layout here.
LayoutData newLayout = new LayoutData(layout);
// Inside
newLayout.Inside = ClientRectangle;
Rectangle inside = newLayout.Inside;
int borderWidth = BorderWidth;
inside.Inflate(-borderWidth, -borderWidth);
Rectangle insideLeft = inside;
// Caption
if (layout.CaptionVisible) {
int captionHeight = captionFontHeight + 6;
Rectangle cap = newLayout.Caption;
cap = insideLeft;
cap.Height = captionHeight;
insideLeft.Y += captionHeight;
insideLeft.Height -= captionHeight;
newLayout.Caption = cap;
}
else {
newLayout.Caption = Rectangle.Empty;
}
// Parent Rows
if (layout.ParentRowsVisible) {
Rectangle parents = newLayout.ParentRows;
int parentHeight = parentRows.Height;
parents = insideLeft;
parents.Height = parentHeight;
insideLeft.Y += parentHeight;
insideLeft.Height -= parentHeight;
newLayout.ParentRows = parents;
}
else {
newLayout.ParentRows = Rectangle.Empty;
}
// Headers
//
int columnHeaderHeight = headerFontHeight + 6;
if (layout.ColumnHeadersVisible) {
Rectangle colHeaders = newLayout.ColumnHeaders;
colHeaders = insideLeft;
colHeaders.Height = columnHeaderHeight;
insideLeft.Y += columnHeaderHeight;
insideLeft.Height -= columnHeaderHeight;
newLayout.ColumnHeaders = colHeaders;
}
else {
newLayout.ColumnHeaders = Rectangle.Empty;
}
bool newRowHeadersVisible = this.myGridTable.IsDefault ? this.RowHeadersVisible : this.myGridTable.RowHeadersVisible;
int newRowHeaderWidth = this.myGridTable.IsDefault ? this.RowHeaderWidth : this.myGridTable.RowHeaderWidth;
newLayout.RowHeadersVisible = newRowHeadersVisible;
if (this.myGridTable != null && newRowHeadersVisible) {
Rectangle rowHeaders = newLayout.RowHeaders;
if (alignLeft)
{
rowHeaders = insideLeft;
rowHeaders.Width = newRowHeaderWidth;
insideLeft.X += newRowHeaderWidth;
insideLeft.Width -= newRowHeaderWidth;
}
else
{
rowHeaders = insideLeft;
rowHeaders.Width = newRowHeaderWidth;
rowHeaders.X = insideLeft.Right - newRowHeaderWidth;
insideLeft.Width -= newRowHeaderWidth;
}
newLayout.RowHeaders = rowHeaders;
if (layout.ColumnHeadersVisible) {
Rectangle topLeft = newLayout.TopLeftHeader;
Rectangle colHeaders = newLayout.ColumnHeaders;
if (alignLeft)
{
topLeft = colHeaders;
topLeft.Width = newRowHeaderWidth;
colHeaders.Width -= newRowHeaderWidth;
colHeaders.X += newRowHeaderWidth;
}
else
{
topLeft = colHeaders;
topLeft.Width = newRowHeaderWidth;
topLeft.X = colHeaders.Right - newRowHeaderWidth;
colHeaders.Width -= newRowHeaderWidth;
}
newLayout.TopLeftHeader = topLeft;
newLayout.ColumnHeaders = colHeaders;
}
else {
newLayout.TopLeftHeader = Rectangle.Empty;
}
}
else {
newLayout.RowHeaders = Rectangle.Empty;
newLayout.TopLeftHeader = Rectangle.Empty;
}
// The Data region
newLayout.Data = insideLeft;
newLayout.Inside = inside;
this.layout = newLayout;
LayoutScrollBars();
// if the user shrank the grid client area, then OnResize invalidated the old
// resize area. however, we need to invalidate the left upper corner in the new ResizeArea
// note that we can't take the Invalidate call from the OnResize method, because if the
// user enlarges the form then the old area will not be invalidated.
//
if (!oldResizeRect.Equals(this.layout.ResizeBoxRect) && !this.layout.ResizeBoxRect.IsEmpty)
Invalidate(this.layout.ResizeBoxRect);
this.layout.dirty = false;
Debug.WriteLineIf(CompModSwitches.DataGridLayout.TraceVerbose, "DataGridLayout: " + layout.ToString());
}
/// <devdoc>
/// Computes the number of pixels to scroll to scroll from one
/// row to another.
/// </devdoc>
private int ComputeRowDelta(int from, int to) {
int first = from;
int last = to;
int sign = -1;
if (first > last) {
first = to;
last = from;
sign = 1;
}
DataGridRow[] localGridRows = DataGridRows;
int delta = 0;
for (int row = first; row < last; ++row) {
delta += localGridRows[row].Height;
}
return sign * delta;
}
internal int MinimumRowHeaderWidth() {
return minRowHeaderWidth;
}
internal void ComputeMinimumRowHeaderWidth() {
minRowHeaderWidth = errorRowBitmapWidth; // the size of the pencil, star and row selector images are the same as the image for the error bitmap
if (this.ListHasErrors)
minRowHeaderWidth += errorRowBitmapWidth;
if (this.myGridTable != null && this.myGridTable.RelationsList.Count != 0)
minRowHeaderWidth += 15; // the size of the plus/minus glyph and spacing around it
}
/// <internalonly/>
/// <devdoc>
/// Updates the internal variables with the number of columns visible
/// inside the Grid's client rectangle.
/// </devdoc>
private void ComputeVisibleColumns() {
Debug.WriteLineIf(CompModSwitches.DataGridLayout.TraceVerbose, "DataGridLayout: ComputeVisibleColumns");
EnsureBound();
GridColumnStylesCollection columns = myGridTable.GridColumnStyles;
int nGridCols = columns.Count;
int cx = - negOffset;
int visibleColumns = 0;
int visibleWidth = layout.Data.Width;
int curCol = firstVisibleCol;
// the same problem with negative numbers:
// if the width passed in is negative, then return 0
//
// added the check for the columns.Count == 0 ( Microsoft, November 14, 2000)
//
if (visibleWidth < 0 || columns.Count == 0)
{
numVisibleCols = firstVisibleCol = 0;
lastTotallyVisibleCol = -1;
return;
}
while (cx < visibleWidth && curCol < nGridCols)
{
// if (columns.Visible && columns.PropertyDescriptor != null)
if (columns[curCol].PropertyDescriptor != null)
cx += columns[curCol].Width;
curCol++;
visibleColumns ++;
}
numVisibleCols = visibleColumns;
// if we inflate the data area
// then we paint columns to the left of firstVisibleColumn
if (cx < visibleWidth)
{
for (int i = firstVisibleCol -1; i > 0; i--)
{
if (cx + columns[i].Width > visibleWidth)
break;
// if (columns.Visible && columns.PropertyDescriptor != null)
if (columns[i].PropertyDescriptor != null)
cx += columns[i].Width;
visibleColumns ++;
firstVisibleCol --;
}
if (numVisibleCols != visibleColumns)
{
Debug.Assert(numVisibleCols < visibleColumns, "the number of visible columns can only grow");
// is there space for more columns than were visible?
// if so, then we need to repaint Data and ColumnHeaders
Invalidate(layout.Data);
Invalidate(layout.ColumnHeaders);
// update the number of visible columns to the new reality
numVisibleCols = visibleColumns;
}
}
lastTotallyVisibleCol = firstVisibleCol + numVisibleCols - 1;
if (cx > visibleWidth) {
if (numVisibleCols <= 1 || (numVisibleCols == 2 && this.negOffset != 0)) {
// no column is entirely visible
lastTotallyVisibleCol = -1;
} else {
lastTotallyVisibleCol--;
}
}
}
/// <devdoc>
/// Determines which column is the first visible given
/// the object's horizontalOffset.
/// </devdoc>
private int ComputeFirstVisibleColumn() {
int first = 0;
if (this.horizontalOffset == 0) {
negOffset = 0;
return 0;
}
// we will check to see if myGridTables.GridColumns.Count != 0
// because when we reset the dataGridTable, we don't have any columns, and we still
// call HorizontalOffset = 0, and that will call ComputeFirstVisibleColumn with an empty collection of columns.
if (myGridTable != null && myGridTable.GridColumnStyles != null && myGridTable.GridColumnStyles.Count != 0) {
GridColumnStylesCollection columns = myGridTable.GridColumnStyles;
int cx = 0;
int nGridCols = columns.Count;
if (columns[0].Width == -1) {
// the columns are not initialized yet
//
#if DEBUG
for (int i = 0; i < nGridCols; i++) {
Debug.Assert(columns[i].Width == -1, "the columns' widths should not be initialized");
}
#endif // DEBUG
negOffset = 0;
return 0;
}
for (first = 0; first < nGridCols; first++) {
// if (columns[first].Visible && columns[first].PropertyDescriptor != null);
if (columns[first].PropertyDescriptor != null)
cx += columns[first].Width;
if (cx > horizontalOffset)
break;
}
// first may actually be the number of columns
// in that case all the columns fit in the layout data
if (first == nGridCols) {
Debug.Assert(cx <= horizontalOffset, "look at the for loop before: we only exit that loop early if the cx is over the horizontal offset");
negOffset = 0;
return 0;
}
negOffset = columns[first].Width - (cx - horizontalOffset);
//Debug.WriteLineIf(CompModSwitches.DataGridScrolling.TraceVerbose, "DataGridScrolling: ComputeFirstVisibleColumn, ret = " + first.ToString() + ", negOffset = " + negOffset.ToString());
}
return first;
}
/// <devdoc>
/// Updates the internal variables with the number of rows visible
/// in a given DataGrid Layout.
/// </devdoc>
private void ComputeVisibleRows() {
Debug.WriteLineIf(CompModSwitches.DataGridLayout.TraceVerbose, "DataGridLayout: ComputeVisibleRows");
EnsureBound();
Rectangle data = layout.Data;
int visibleHeight = data.Height;
int cy = 0;
int visibleRows = 0;
DataGridRow[] localGridRows = DataGridRows;
int numRows = DataGridRowsLength;
// when minimizing the dataGrid window, we will get negative values for the
// layout.Data.Width and layout.Data.Height ( is this a bug or not? if layout.Data.Height == 0 in that case,
// the old code would have worked )
//
// if this is the case, then set numVisibleRows = numTotallyVisibleRows = 0;
//
if (visibleHeight < 0)
{
numVisibleRows = numTotallyVisibleRows = 0;
return;
}
for (int i = firstVisibleRow; i < numRows; ++i) {
if (cy > visibleHeight)
break;
cy += localGridRows[i].Height;
visibleRows++;
}
if (cy < visibleHeight) {
for (int i = firstVisibleRow-1; i >= 0; i--) {
int height = localGridRows[i].Height;
if (cy + height > visibleHeight)
break;
cy += height;
firstVisibleRow--;
visibleRows++;
}
}
numVisibleRows = numTotallyVisibleRows = visibleRows;
if (cy > visibleHeight)
numTotallyVisibleRows--;
Debug.Assert(numVisibleRows >= 0, "the number of visible rows can't be negative");
Debug.Assert(numTotallyVisibleRows >= 0, "the number of totally visible rows can't be negative");
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.CreateAccessibilityInstance"]/*' />
/// <devdoc>
/// Constructs the new instance of the accessibility object for this control. Subclasses
/// should not call base.CreateAccessibilityObject.
/// </devdoc>
protected override AccessibleObject CreateAccessibilityInstance() {
return new DataGridAccessibleObject(this);
}
/// <internalonly/>
/// <devdoc>
/// Creates a DataGridState representing the child table retrieved
/// from the passed DataRelation.
/// </devdoc>
private DataGridState CreateChildState(string relationName, DataGridRow source) {
DataGridState dgs = new DataGridState();
string newDataMember;
if (String.IsNullOrEmpty(this.DataMember))
{
newDataMember = relationName;
}
else
{
newDataMember = this.DataMember + "." + relationName;
}
CurrencyManager childLM = (CurrencyManager) this.BindingContext[this.DataSource, newDataMember];
dgs.DataSource = this.DataSource;
dgs.DataMember = newDataMember;
dgs.ListManager = childLM;
dgs.DataGridRows = null;
dgs.DataGridRowsLength = childLM.Count + (policy.AllowAdd ? 1 : 0);
return dgs;
}
/// <devdoc>
/// Constructs a Layout object containing the state
/// for a newly constructed DataGrid.
/// </devdoc>
private LayoutData CreateInitialLayoutState() {
Debug.WriteLineIf(CompModSwitches.DataGridLayout.TraceVerbose, "DataGridLayout: CreateInitialLayoutState");
LayoutData newLayout = new LayoutData();
newLayout.Inside = new Rectangle();
newLayout.TopLeftHeader = new Rectangle();
newLayout.ColumnHeaders = new Rectangle();
newLayout.RowHeaders = new Rectangle();
newLayout.Data = new Rectangle();
newLayout.Caption = new Rectangle();
newLayout.ParentRows = new Rectangle();
newLayout.ResizeBoxRect = new Rectangle();
newLayout.ColumnHeadersVisible = true;
newLayout.RowHeadersVisible = true;
newLayout.CaptionVisible = defaultCaptionVisible;
newLayout.ParentRowsVisible = defaultParentRowsVisible;
newLayout.ClientRectangle = ClientRectangle;
return newLayout;
}
/// <devdoc>
/// The DataGrid caches an array of rectangular areas
/// which represent the area which scrolls left to right.
/// This method is invoked whenever the DataGrid needs
/// this scrollable region.
/// </devdoc>
private NativeMethods.RECT[] CreateScrollableRegion(Rectangle scroll) {
if (cachedScrollableRegion != null) {
return cachedScrollableRegion;
}
bool alignToRight = isRightToLeft();
using(Region region = new Region(scroll)) {
int nRows = numVisibleRows;
int cy = layout.Data.Y;
int cx = layout.Data.X;
DataGridRow[] localGridRows = DataGridRows;
for (int r = firstVisibleRow; r < nRows; r++) {
int rowHeight = localGridRows[r].Height;
Rectangle rowExclude = localGridRows[r].GetNonScrollableArea();
rowExclude.X += cx;
rowExclude.X = MirrorRectangle(rowExclude, layout.Data, alignToRight);
if (!rowExclude.IsEmpty) {
region.Exclude(new Rectangle(rowExclude.X,
rowExclude.Y + cy,
rowExclude.Width,
rowExclude.Height));
}
cy += rowHeight;
}
using (Graphics graphics = CreateGraphicsInternal()) {
IntPtr handle = region.GetHrgn(graphics);
if (handle != IntPtr.Zero) {
cachedScrollableRegion = UnsafeNativeMethods.GetRectsFromRegion(handle);
// SECREVIEW : This assert is safe since we created the native region.
//
IntSecurity.ObjectFromWin32Handle.Assert();
try {
region.ReleaseHrgn(handle);
} finally {
CodeAccessPermission.RevertAssert();
}
}
}
}
return cachedScrollableRegion;
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.Dispose"]/*' />
/// <devdoc>
/// Disposes of the resources (other than memory) used
/// by the <see cref='System.Windows.Forms.DataGrid'/>.
/// </devdoc>
protected override void Dispose(bool disposing) {
if (disposing) {
if (vertScrollBar != null)
vertScrollBar.Dispose();
if (horizScrollBar != null)
horizScrollBar.Dispose();
if (this.toBeDisposedEditingControl != null) {
this.toBeDisposedEditingControl.Dispose();
this.toBeDisposedEditingControl = null;
}
GridTableStylesCollection tables = this.TableStyles;
if (tables != null) {
#if DEBUG
Debug.Assert(this.myGridTable == null || this.myGridTable.IsDefault || tables.Contains(this.myGridTable), "how come that the currentTable is not in the list of tables?");
#endif // DEBUG
for (int i = 0; i < tables.Count; i++)
tables[i].Dispose();
}
}
base.Dispose(disposing);
}
/// <devdoc>
/// Draws an XOR region to give UI feedback for Column Resizing.
/// This looks just like the Splitter control's UI when resizing.
/// </devdoc>
private void DrawColSplitBar(MouseEventArgs e)
{
Rectangle r = CalcColResizeFeedbackRect(e);
DrawSplitBar(r);
}
/// <devdoc>
/// Draws an XOR region to give UI feedback for Row Resizing.
/// This looks just like the Splitter control's UI when resizing.
/// </devdoc>
private void DrawRowSplitBar(MouseEventArgs e)
{
Rectangle r = CalcRowResizeFeedbackRect(e);
DrawSplitBar(r);
}
/// <devdoc>
/// Draws an XOR region to give UI feedback for Column/Row Resizing.
/// This looks just like the Splitter control's UI when resizing.
/// </devdoc>
private void DrawSplitBar(Rectangle r)
{
IntPtr parentHandle = Handle;
IntPtr dc = UnsafeNativeMethods.GetDCEx(new HandleRef(this, parentHandle), NativeMethods.NullHandleRef, NativeMethods.DCX_CACHE | NativeMethods.DCX_LOCKWINDOWUPDATE);
IntPtr halftone = ControlPaint.CreateHalftoneHBRUSH();
IntPtr saveBrush = SafeNativeMethods.SelectObject(new HandleRef(this, dc), new HandleRef(null, halftone));
SafeNativeMethods.PatBlt(new HandleRef(this, dc), r.X, r.Y, r.Width, r.Height, NativeMethods.PATINVERT);
SafeNativeMethods.SelectObject(new HandleRef(this, dc), new HandleRef(null, saveBrush));
SafeNativeMethods.DeleteObject(new HandleRef(null, halftone));
UnsafeNativeMethods.ReleaseDC(new HandleRef(this, parentHandle), new HandleRef(this, dc));
}
/// <devdoc>
/// Begin in-place editing of a cell. Any editing is commited
/// before the new edit takes place.
///
/// This will always edit the currentCell
/// If you want to edit another cell than the current one, just reset CurrentCell
/// </devdoc>
private void Edit() {
Edit(null);
}
private void Edit(string displayText) {
EnsureBound();
// we want to be able to edit a cell which is not visible, as in the case with editing and scrolling
// at the same time. So do not call Ensure Visible
//
// EnsureVisible(currentRow, currentCol);
bool cellIsVisible = true;
// whoever needs to call ResetSelection should not rely on
// Edit() to call it;
//
// ResetSelection();
EndEdit();
Debug.WriteLineIf(CompModSwitches.DataGridEditing.TraceVerbose, "DataGridEditing: Edit, currentRow = " + currentRow.ToString(CultureInfo.InvariantCulture) +
", currentCol = " + currentCol.ToString(CultureInfo.InvariantCulture) + (displayText != null ? ", displayText= " + displayText : ""));
/* allow navigation even if the policy does not allow editing
if (!policy.AllowEdit)
return;
*/
DataGridRow[] localGridRows = DataGridRows;
// what do you want to edit when there are no rows?
if (DataGridRowsLength == 0)
return;
localGridRows[currentRow].OnEdit();
editRow = localGridRows[currentRow];
// if the list has no columns, then what good is an edit?
if (this.myGridTable.GridColumnStyles.Count == 0)
return;
// what if the currentCol does not have a propDesc?
editColumn = myGridTable.GridColumnStyles[currentCol];
if(editColumn.PropertyDescriptor == null)
return;
Rectangle cellBounds = Rectangle.Empty;
if (currentRow < firstVisibleRow || currentRow > firstVisibleRow + numVisibleRows ||
currentCol < firstVisibleCol || currentCol > firstVisibleCol + numVisibleCols - 1 ||
(currentCol == firstVisibleCol && negOffset != 0))
{
cellIsVisible = false;
}
else
{
cellBounds = GetCellBounds(currentRow, currentCol);
}
gridState[GRIDSTATE_isNavigating] = true;
gridState[GRIDSTATE_isEditing] = false;
// once we call editColumn.Edit on a DataGridTextBoxColumn
// the edit control will become visible, and its bounds will get set.
// both actions cause Edit.Parent.OnLayout
// so we flag this change, cause we don't want to PerformLayout on the entire DataGrid
// everytime the edit column gets edited
gridState[GRIDSTATE_editControlChanging] = true;
editColumn.Edit(this.ListManager,
currentRow,
cellBounds,
myGridTable.ReadOnly || this.ReadOnly || !policy.AllowEdit,
displayText,
cellIsVisible);
// reset the editControlChanging to false
gridState[GRIDSTATE_editControlChanging] = false;
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.EndEdit"]/*' />
/// <devdoc>
/// <para>Requests an end to an edit operation taking place on the
/// <see cref='System.Windows.Forms.DataGrid'/>
/// control.</para>
/// </devdoc>
public bool EndEdit(DataGridColumnStyle gridColumn, int rowNumber, bool shouldAbort) {
bool ret = false;
if (gridState[GRIDSTATE_isEditing]) {
if (gridColumn != editColumn) {
Debug.WriteLineIf(CompModSwitches.DataGridEditing.TraceVerbose, "DataGridEditing: EndEdit requested on a column we are not editing.");
}
if (rowNumber != editRow.RowNumber) {
Debug.WriteLineIf(CompModSwitches.DataGridEditing.TraceVerbose, "DataGridEditing: EndEdit requested on a row we are not editing.");
}
if (shouldAbort) {
AbortEdit();
ret = true;
}
else
ret = CommitEdit();
}
return ret;
}
/// <devdoc>
/// Ends any editing in progress by attempting to commit and then
/// aborting if not possible.
/// </devdoc>
private void EndEdit() {
Debug.WriteLineIf(CompModSwitches.DataGridEditing.TraceVerbose, "DataGridEditing: EndEdit");
if (!gridState[GRIDSTATE_isEditing] && !gridState[GRIDSTATE_isNavigating])
return;
if (!CommitEdit()) {
AbortEdit();
}
}
// PERF: we attempt to create a ListManager for the DataSource/DateMember combination
// we do this in order to check for a valid DataMember
// if the check succeeds, then it means that we actully put the listManager in the BindingContext's
// list of BindingManagers. this is fine, cause if the check succeds, then Set_ListManager
// will be called, and this will get the listManager from the bindingManagerBase hashTable kept in the BindingContext
//
// this will work if the dataMember does not contain any dots ('.')
// if the dataMember contains dots, then it will be more complicated: maybe part of the binding path
// is valid w/ the new dataSource
// but we can leave w/ this, cause in the designer the dataMember will be only a column name. and the DataSource/DataMember
// properties are for use w/ the designer.
//
private void EnforceValidDataMember(object value) {
Debug.Assert(value != null, "we should not have a null dataSource when we want to check for a valid dataMember");
if (this.DataMember == null || this.DataMember.Length == 0)
return;
if (this.BindingContext == null)
return;
//
try {
BindingManagerBase bm = this.BindingContext[value, this.dataMember];
}
catch {
this.dataMember = "";
}
}
// will be used by the columns to tell the grid that
// editing is taken place (ie, the grid is no longer in the
// editOrNavigateMode)
//
// also, tell the current row to lose child focus
//
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ColumnStartedEditing"]/*' />
internal protected virtual void ColumnStartedEditing(Rectangle bounds) {
Debug.Assert( currentRow >= firstVisibleRow && currentRow <= firstVisibleRow + numVisibleRows, "how can one edit a row which is invisible?");
DataGridRow[] localGridRows = DataGridRows;
if (bounds.IsEmpty && this.editColumn is DataGridTextBoxColumn && this.currentRow != -1 && this.currentCol != -1) {
// set the bounds on the control
// this will only work w/ our DataGridTexBox control
DataGridTextBoxColumn col = this.editColumn as DataGridTextBoxColumn;
Rectangle editBounds = this.GetCellBounds(this.currentRow, this.currentCol);
gridState[GRIDSTATE_editControlChanging] = true;
try {
col.TextBox.Bounds = editBounds;
} finally {
gridState[GRIDSTATE_editControlChanging] = false;
}
}
if (gridState[GRIDSTATE_inAddNewRow]) {
int currentRowCount = this.DataGridRowsLength;
DataGridRow[] newDataGridRows = new DataGridRow[currentRowCount + 1];
for (int i = 0; i < currentRowCount; i++) {
newDataGridRows[i] = localGridRows[i];
}
// put the AddNewRow
newDataGridRows[currentRowCount] = new DataGridAddNewRow(this, this.myGridTable, currentRowCount);
SetDataGridRows(newDataGridRows, currentRowCount + 1);
Edit();
// put this after the call to edit so that
// CommitEdit knows that the inAddNewRow is true;
gridState[GRIDSTATE_inAddNewRow] = false;
gridState[GRIDSTATE_isEditing] = true;
gridState[GRIDSTATE_isNavigating] = false;
return;
}
gridState[GRIDSTATE_isEditing] = true;
gridState[GRIDSTATE_isNavigating] = false;
InvalidateRowHeader(this.currentRow);
// tell the current row to lose the childFocuse
localGridRows[currentRow].LoseChildFocus(layout.RowHeaders, isRightToLeft());
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ColumnStartedEditing1"]/*' />
internal protected virtual void ColumnStartedEditing(Control editingControl) {
this.ColumnStartedEditing(editingControl.Bounds);
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.Expand"]/*' />
/// <devdoc>
/// <para>
/// Displays child relations, if any exist, for all rows or a
/// specific row.
/// </para>
/// </devdoc>
public void Expand(int row) {
SetRowExpansionState(row, true);
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.CreateGridColumn"]/*' />
/// <devdoc>
/// <para>
/// Creates a <see cref='System.Windows.Forms.DataGridColumnStyle'/> using the specified <see cref='System.ComponentModel.PropertyDescriptor'/>.
/// </para>
/// </devdoc>
// protected and virtual because the SimpleDropdownDataGrid will override this
protected virtual DataGridColumnStyle CreateGridColumn(PropertyDescriptor prop, bool isDefault) {
return myGridTable == null ? null : myGridTable.CreateGridColumn(prop, isDefault);
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.CreateGridColumn1"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected virtual DataGridColumnStyle CreateGridColumn(PropertyDescriptor prop)
{
return myGridTable == null ? null : myGridTable.CreateGridColumn(prop);
}
#if PARENT_LINKS
private ListManager ListManagerForChildColumn(ListManager childListManager, PropertyDescriptor prop)
{
/*
DataKey key;
RelationsCollection relCollection = dataColumn.Table.ParentRelations;
*/
// this will give us the list of properties of the child
PropertyDescriptorCollection propCollection = childListManager.GetItemProperties();
int relCount = propCollection.Count;
for (int i=0;i<relCount;i++)
{
PropertyDescriptor currRelation = propCollection[i];
if (typeof(IList).IsAssignableFrom(currRelation.PropertyType))
{
object childRelation = currRelation.GetValue(childListManager.Current);
ListManager childTable = this.BindingContexgt == null ? null : this.BindingManager[childRelation, ""];
if (childTable == null)
continue;
// now loop thru all properties in the childTable...
PropertyDescriptorCollection childProps = childTable.GetItemProperties();
int colCount = childProps.Count;
for (int j = 0; j < colCount; j++)
{
PropertyDescriptor currCol = childProps[j];
if (currCol.Name != null && currCol.Name.Equals(prop.Name))
// there is a column in the parent with the same name
return childTable;
}
}
/*
DataColumn[] childCols = relCollection[i].ChildColumns;
DataColumn[] parentCols = relCollection[i].ParentColumns;
// if the relationship involves more than one column in the child
// table, then look at the next relationship
if (childCols.Count != 1)
continue;
// if the relationship involves more than one column in the parent
// table, then look at the next relationship
if (parentCols.Count != 1)
continue;
if (childCols[0] == dataColumn)
{
dataColumn = parentCols[0];
return new DataSource(relCollection[i].ParentTable);
}
*/
}
return null;
}
#endif
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.EndInit"]/*' />
/// <devdoc>
/// <para>Specifies the end of the initialization code.</para>
/// </devdoc>
public void EndInit() {
inInit = false;
if (myGridTable == null && this.ListManager != null) {
SetDataGridTable(this.TableStyles[this.ListManager.GetListName()], true); // true for forcing column creation
}
if (myGridTable != null)
myGridTable.DataGrid = this;
}
/// <devdoc>
/// Given a x coordinate, returns the column it is over.
/// </devdoc>
private int GetColFromX(int x) {
if (myGridTable == null)
return -1;
Rectangle inside = layout.Data;
Debug.Assert(x >= inside.X && x < inside.Right, "x must be inside the horizontal bounds of layout.Data");
x = MirrorPoint(x, inside, isRightToLeft());
GridColumnStylesCollection columns = myGridTable.GridColumnStyles;
int columnCount = columns.Count;
int cx = inside.X - negOffset;
int col = firstVisibleCol;
while (cx < inside.Width + inside.X && col < columnCount) {
// if (columns[col].Visible && columns[col].PropertyDescriptor != null)
if (columns[col].PropertyDescriptor != null)
cx += columns[col].Width;
if (cx > x)
return col;
++col;
}
return -1;
}
/// <devdoc>
/// Returns the coordinate of the left edge of the given column
/// Bi-Di: if the grid has the RightToLeft property set to RightToLeft.Yes, this will
/// return what appears as the right edge of the column
/// </devdoc>
internal int GetColBeg(int col) {
Debug.Assert(myGridTable != null, "GetColBeg can't be called when myGridTable == null.");
int offset = layout.Data.X - negOffset;
GridColumnStylesCollection columns = myGridTable.GridColumnStyles;
int lastCol = Math.Min(col, columns.Count);
for (int i = firstVisibleCol; i < lastCol; ++i) {
// if (columns[i].Visible && columns[i].PropertyDescriptor != null)
if (columns[i].PropertyDescriptor != null)
offset += columns[i].Width;
}
return MirrorPoint(offset, layout.Data, isRightToLeft());
}
/// <devdoc>
/// Returns the coordinate of the right edge of the given column
/// Bi-Di: if the grid has the RightToLeft property set to RightToLeft.Yes, this will
/// return what appears as the left edge of the column
/// </devdoc>
internal int GetColEnd(int col) {
// return MirrorPoint(GetColBeg(col) + myGridTable.GridColumnStyles[col].Width, layout.Data, isRightToLeft());
int colBeg = GetColBeg(col);
Debug.Assert(myGridTable.GridColumnStyles[col].PropertyDescriptor != null, "why would we need the coordinate of a column that is not visible?");
int width = myGridTable.GridColumnStyles[col].Width;
return isRightToLeft() ? colBeg - width : colBeg + width;
}
private int GetColumnWidthSum() {
int sum = 0;
if (myGridTable != null && myGridTable.GridColumnStyles != null) {
GridColumnStylesCollection columns = myGridTable.GridColumnStyles;
int columnsCount = columns.Count;
for (int i = 0; i < columnsCount; i++)
// if (columns[i].Visible && columns[i].PropertyDescriptor != null)
if (columns[i].PropertyDescriptor != null)
sum += columns[i].Width;
}
return sum;
}
/// <devdoc>
/// Not all rows in the DataGrid are expandable,
/// this computes which ones are and returns an array
/// of references to them.
/// </devdoc>
private DataGridRelationshipRow[] GetExpandableRows() {
int nExpandableRows = DataGridRowsLength;
DataGridRow[] localGridRows = DataGridRows;
if (policy.AllowAdd)
nExpandableRows = Math.Max(nExpandableRows-1,0);
DataGridRelationshipRow[] expandableRows = new DataGridRelationshipRow[nExpandableRows];
for (int i = 0; i < nExpandableRows; i++)
expandableRows[i] = (DataGridRelationshipRow)localGridRows[i];
return expandableRows;
}
/// <devdoc>
/// Returns the row number underneath the given y coordinate.
/// </devdoc>
/// <internalonly/>
private int GetRowFromY(int y) {
Rectangle inside = layout.Data;
Debug.Assert(y >= inside.Y && y < inside.Bottom, "y must be inside the vertical bounds of the data");
int cy = inside.Y;
int row = firstVisibleRow;
int rowCount = DataGridRowsLength;
DataGridRow[] localGridRows = DataGridRows;
int bottom = inside.Bottom;
while (cy < bottom && row < rowCount) {
cy += localGridRows[row].Height;
if (cy > y) {
return row;
}
++row;
}
return -1;
}
internal Rectangle GetRowHeaderRect() {
return layout.RowHeaders;
}
internal Rectangle GetColumnHeadersRect() {
return layout.ColumnHeaders;
}
/// <devdoc>
/// Determines where on the control's ClientRectangle a given row is
/// painting to.
/// </devdoc>
private Rectangle GetRowRect(int rowNumber) {
Rectangle inside = layout.Data;
int cy = inside.Y;
DataGridRow[] localGridRows = DataGridRows;
for (int row = firstVisibleRow; row <= rowNumber; ++row) {
if (cy > inside.Bottom) {
break;
}
if (row == rowNumber) {
Rectangle rowRect = new Rectangle(inside.X,
cy,
inside.Width,
localGridRows[row].Height);
if (layout.RowHeadersVisible) {
rowRect.Width += layout.RowHeaders.Width;
rowRect.X -= isRightToLeft() ? 0 : layout.RowHeaders.Width;
}
return rowRect;
}
cy += localGridRows[row].Height;
}
return Rectangle.Empty;
}
/// <devdoc>
/// Returns the coordinate of the top edge of the given row
/// </devdoc>
private int GetRowTop(int row) {
DataGridRow[] localGridRows = DataGridRows;
int offset = layout.Data.Y;
int lastRow = Math.Min(row, DataGridRowsLength);
for (int i = firstVisibleRow; i < lastRow; ++i) {
offset += localGridRows[i].Height;
}
for (int i=firstVisibleRow; i > lastRow; i--) {
offset -= localGridRows[i].Height;
}
return offset;
}
/// <devdoc>
/// Returns the coordinate of the bottom edge of the given row
/// </devdoc>
private int GetRowBottom(int row) {
DataGridRow[] localGridRows = DataGridRows;
return GetRowTop(row) + localGridRows[row].Height;
}
/// <devdoc>
/// This method is called on methods that need the grid
/// to be bound to a DataTable to work.
/// </devdoc>
private void EnsureBound() {
if (!Bound) {
throw new InvalidOperationException(SR.GetString(SR.DataGridUnbound));
}
}
private void EnsureVisible(int row, int col) {
if (row < firstVisibleRow
|| row >= firstVisibleRow + numTotallyVisibleRows) {
int dRows = ComputeDeltaRows(row);
ScrollDown(dRows);
}
if (this.firstVisibleCol == 0 && this.numVisibleCols == 0 && this.lastTotallyVisibleCol == -1) {
// no columns are displayed whatsoever
// some sanity checks
Debug.Assert(this.negOffset == 0, " no columns are displayed so the negative offset should be 0");
return;
}
int previousFirstVisibleCol = this.firstVisibleCol;
int previousNegOffset = this.negOffset;
int previousLastTotallyVisibleCol = this.lastTotallyVisibleCol;
while (col < firstVisibleCol
|| col == firstVisibleCol && negOffset != 0
|| lastTotallyVisibleCol == -1 && col > firstVisibleCol
|| lastTotallyVisibleCol > -1 && col > lastTotallyVisibleCol) {
ScrollToColumn(col);
if (previousFirstVisibleCol == this.firstVisibleCol &&
previousNegOffset == this.negOffset &&
previousLastTotallyVisibleCol == this.lastTotallyVisibleCol) {
// nothing changed since the last iteration
// don't get into an infinite loop
break;
}
previousFirstVisibleCol = firstVisibleCol;
previousNegOffset = this.negOffset;
previousLastTotallyVisibleCol = this.lastTotallyVisibleCol;
// continue to scroll to the right until the scrollTo column is the totally last visible column or it is the first visible column
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.GetCurrentCellBounds"]/*' />
/// <devdoc>
/// <para>Gets a <see cref='T:System.Drawing.Rectangle'/>
/// that specifies the four corners of the selected cell.</para>
/// </devdoc>
public Rectangle GetCurrentCellBounds() {
DataGridCell current = this.CurrentCell;
return GetCellBounds(current.RowNumber, current.ColumnNumber);
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.GetCellBounds"]/*' />
/// <devdoc>
/// <para>Gets the <see cref='T:System.Drawing.Rectangle'/> of the cell specified by row and column number.</para>
/// </devdoc>
public Rectangle GetCellBounds(int row, int col) {
DataGridRow[] localGridRows = DataGridRows;
Rectangle cellBounds = localGridRows[row].GetCellBounds(col);
cellBounds.Y += GetRowTop(row);
cellBounds.X += layout.Data.X - negOffset;
cellBounds.X = MirrorRectangle(cellBounds, layout.Data, isRightToLeft());
return cellBounds;
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.GetCellBounds1"]/*' />
/// <devdoc>
/// <para>Gets the <see cref='T:System.Drawing.Rectangle'/> of the cell specified by <see cref='System.Windows.Forms.DataGridCell'/>.</para>
/// </devdoc>
public Rectangle GetCellBounds(DataGridCell dgc) {
return GetCellBounds(dgc.RowNumber, dgc.ColumnNumber);
}
//
internal Rectangle GetRowBounds(DataGridRow row) {
Rectangle rowBounds = new Rectangle();
rowBounds.Y = GetRowTop(row.RowNumber);
rowBounds.X = layout.Data.X;
rowBounds.Height = row.Height;
rowBounds.Width = layout.Data.Width;
return rowBounds;
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.HitTest"]/*' />
/// <devdoc>
/// <para>Gets information, such as row and column number of a
/// clicked point on
/// the grid,
/// using the x
/// and y coordinate passed to the method.</para>
/// </devdoc>
public HitTestInfo HitTest(int x, int y) {
int topOfData = layout.Data.Y;
HitTestInfo ci = new HitTestInfo();
if (layout.CaptionVisible && layout.Caption.Contains(x,y)) {
ci.type = HitTestType.Caption;
return ci;
}
if (layout.ParentRowsVisible && layout.ParentRows.Contains(x,y)) {
ci.type = HitTestType.ParentRows;
return ci;
}
if (!layout.Inside.Contains(x,y))
return ci;
if (layout.TopLeftHeader.Contains(x,y))
return ci;
// check for column resize
if (layout.ColumnHeaders.Contains(x,y)) {
ci.type = HitTestType.ColumnHeader;
ci.col = GetColFromX(x);
if (ci.col < 0)
return HitTestInfo.Nowhere;
int right = GetColBeg(ci.col + 1);
bool rightToLeft = isRightToLeft();
if ((rightToLeft && x - right < 8) || (!rightToLeft && right - x < 8)) {
ci.type = HitTestType.ColumnResize;
}
return(allowColumnResize ? ci : HitTestInfo.Nowhere);
}
//check for RowResize:
if (layout.RowHeaders.Contains(x,y)) {
ci.type = HitTestType.RowHeader;
ci.row = GetRowFromY(y);
if (ci.row < 0)
return HitTestInfo.Nowhere;
// find out if the click was a RowResize click
DataGridRow[] localGridRows = DataGridRows;
int bottomBorder = GetRowTop(ci.row) + localGridRows[ci.row].Height;
if (bottomBorder - y - BorderWidth < 2 && !(localGridRows[ci.row] is DataGridAddNewRow)) {
ci.type = HitTestType.RowResize;
}
return (allowRowResize ? ci : HitTestInfo.Nowhere);
}
if (layout.Data.Contains(x,y)) {
ci.type = HitTestType.Cell;
ci.col = GetColFromX(x);
ci.row = GetRowFromY(y);
if (ci.col < 0 || ci.row < 0) return HitTestInfo.Nowhere;
return ci;
}
return ci;
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.HitTest1"]/*' />
/// <devdoc>
/// <para>Gets information, such as row and column number of a
/// clicked point on the grid, about the
/// grid using a specific
/// <see cref='System.Drawing.Point'/>.</para>
/// </devdoc>
public HitTestInfo HitTest(Point position) {
return HitTest(position.X, position.Y);
}
/// <internalonly/>
/// <devdoc>
/// Initializes the values for column widths in the table.
/// </devdoc>
private void InitializeColumnWidths() {
if (myGridTable == null)
return;
GridColumnStylesCollection columns = myGridTable.GridColumnStyles;
int numCols = columns.Count;
// Resize the columns to a approximation of a best fit.
// We find the best fit width of NumRowsForAutoResize rows
// and use it for each column.
int preferredColumnWidth = this.myGridTable.IsDefault ? this.PreferredColumnWidth : this.myGridTable.PreferredColumnWidth;
// if we set the PreferredColumnWidth to something else than AutoColumnSize
// then use that value
//
for (int col = 0; col < numCols; col++) {
// if the column width is not -1, then this column was initialized already
if (columns[col].width != -1) continue;
columns[col].width = preferredColumnWidth;
}
}
/// <devdoc>
/// Invalidates the scrollable area of the DataGrid.
/// </devdoc>
internal void InvalidateInside() {
Invalidate(layout.Inside);
}
/// <devdoc>
/// Invalidates the caption area of the DataGrid.
/// </devdoc>
internal void InvalidateCaption() {
if (layout.CaptionVisible)
Invalidate(layout.Caption);
}
/// <devdoc>
/// Invalidates a rectangle normalized to the caption's
/// visual bounds.
/// </devdoc>
internal void InvalidateCaptionRect(Rectangle r) {
if (layout.CaptionVisible) {
Invalidate(r);
}
}
/// <devdoc>
/// Invalidates the display region of a given DataGridColumn.
/// </devdoc>
internal void InvalidateColumn(int column) {
GridColumnStylesCollection gridColumns = this.myGridTable.GridColumnStyles;
if (column < 0 || gridColumns == null || gridColumns.Count <= column)
return;
Debug.Assert(gridColumns[column].PropertyDescriptor != null, "how can we invalidate a column that is invisible?");
// bail if the column is not visible.
if (column < firstVisibleCol || column > firstVisibleCol + numVisibleCols - 1)
return;
Rectangle columnArea = new Rectangle();
columnArea.Height = layout.Data.Height;
columnArea.Width = gridColumns[column].Width;
columnArea.Y = layout.Data.Y;
int x = layout.Data.X - negOffset;
int gridColumnsCount = gridColumns.Count;
for (int i = firstVisibleCol; i < gridColumnsCount; ++i) {
if (i == column)
break;
x += gridColumns[i].Width;
}
columnArea.X = x;
columnArea.X = MirrorRectangle(columnArea, layout.Data, isRightToLeft());
Invalidate(columnArea);
}
/// <devdoc>
/// Invalidates the parent rows area of the DataGrid
/// </devdoc>
internal void InvalidateParentRows() {
if (layout.ParentRowsVisible)
Invalidate(layout.ParentRows);
}
/// <devdoc>
/// Invalidates a rectangle normalized to the parent
/// rows area's visual bounds.
/// </devdoc>
internal void InvalidateParentRowsRect(Rectangle r) {
Rectangle parentRowsRect = layout.ParentRows;
Invalidate(r);
if (!parentRowsRect.IsEmpty) {
//Invalidate(new Rectangle(parentRowsRect.X + r.X, parentRowsRect.Y + r.Y,
// r.Width, r.Height));
}
}
/// <devdoc>
/// Invalidate the painting region for the row specified.
/// </devdoc>
internal void InvalidateRow(int rowNumber) {
Rectangle rowRect = GetRowRect(rowNumber);
if (!rowRect.IsEmpty) {
Debug.WriteLineIf(CompModSwitches.DataGridPainting.TraceVerbose, "DataGridPainting: Invalidating row " + rowNumber.ToString(CultureInfo.InvariantCulture));
Invalidate(rowRect);
}
}
private void InvalidateRowHeader(int rowNumber) {
if (rowNumber >= firstVisibleRow && rowNumber < firstVisibleRow + numVisibleRows) {
if (!layout.RowHeadersVisible)
return;
Rectangle invalid = new Rectangle();
invalid.Y = GetRowTop(rowNumber);
invalid.X = layout.RowHeaders.X;
invalid.Width = layout.RowHeaders.Width;
invalid.Height = this.DataGridRows[rowNumber].Height;
Invalidate(invalid);
}
}
// NOTE:
// because of Rtl, we assume that the only place that calls InvalidateRowRect is
// the DataGridRelationshipRow
internal void InvalidateRowRect(int rowNumber, Rectangle r) {
Rectangle rowRect = GetRowRect(rowNumber);
if (!rowRect.IsEmpty) {
Debug.WriteLineIf(CompModSwitches.DataGridPainting.TraceVerbose, "DataGridPainting: Invalidating a rect in row " + rowNumber.ToString(CultureInfo.InvariantCulture));
Rectangle inner = new Rectangle(rowRect.X + r.X, rowRect.Y + r.Y, r.Width, r.Height);
if (vertScrollBar.Visible && isRightToLeft())
inner.X -= vertScrollBar.Width;
Invalidate(inner);
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.IsExpanded"]/*' />
/// <devdoc>
/// <para>Gets a value that indicates whether a specified row's node is expanded or collapsed.</para>
/// </devdoc>
public bool IsExpanded(int rowNumber) {
if (rowNumber < 0 || rowNumber > DataGridRowsLength)
throw new ArgumentOutOfRangeException("rowNumber");
DataGridRow[] localGridRows = DataGridRows;
//
DataGridRow row = localGridRows[rowNumber];
if (row is DataGridRelationshipRow) {
DataGridRelationshipRow relRow = (DataGridRelationshipRow)row;
return relRow.Expanded;
}
else
return false;
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.IsSelected"]/*' />
/// <devdoc>
/// <para>
/// Gets a value indicating whether a
/// specified row is selected.
/// </para>
/// </devdoc>
public bool IsSelected(int row) {
// SECREVIEW: the method does not check the bounds. We can't fix this and throw an exception because this would be a breaking change
DataGridRow[] localGridRows = DataGridRows;
return localGridRows[row].Selected;
}
internal static bool IsTransparentColor(Color color) {
return color.A < 255;
}
/// <devdoc>
/// Determines if Scrollbars should be visible,
/// updates their bounds and the bounds of all
/// other regions in the DataGrid's Layout.
/// </devdoc>
private void LayoutScrollBars() {
// if we set the dataSource to null, then take away the scrollbars.
if (listManager == null || myGridTable == null) {
horizScrollBar.Visible = false;
vertScrollBar.Visible = false;
return;
}
// Scrollbars are a tricky issue.
// We need to see if we can cram our columns and rows
// in without scrollbars and if they don't fit, we make
// scrollbars visible and then fixup our regions for the
// data and headers.
bool needHorizScrollbar = false;
bool needVertScrollbar = false;
bool recountRows = false;
bool alignToRight = isRightToLeft();
int nGridCols = myGridTable.GridColumnStyles.Count;
// if we call LayoutScrollBars before CreateDataGridRows
// then the columns will have their default width ( 100 )
// CreateDataGridRows will possibly change the columns' width
//
// and anyway, ComputeVisibleRows will call the DataGridRows accessor
//
DataGridRow[] gridRows = this.DataGridRows;
// at this stage, the data grid columns may have their width set to -1 ( ie, their width is uninitialized )
// make sure that the totalWidth is at least 0
int totalWidth = Math.Max(0, GetColumnWidthSum());
if (totalWidth > layout.Data.Width && !needHorizScrollbar) {
int horizHeight = horizScrollBar.Height;
layout.Data.Height -= horizHeight;
if (layout.RowHeadersVisible)
layout.RowHeaders.Height -= horizHeight;
needHorizScrollbar = true;
}
int oldFirstVisibleRow = firstVisibleRow;
ComputeVisibleRows();
if (numTotallyVisibleRows != DataGridRowsLength && !needVertScrollbar) {
int vertWidth = vertScrollBar.Width;
layout.Data.Width -= vertWidth;
if (layout.ColumnHeadersVisible)
{
if (alignToRight)
layout.ColumnHeaders.X += vertWidth;
layout.ColumnHeaders.Width -= vertWidth;
}
needVertScrollbar = true;
}
this.firstVisibleCol = ComputeFirstVisibleColumn();
// we compute the number of visible columns only after we set up the vertical scroll bar.
ComputeVisibleColumns();
if (needVertScrollbar && totalWidth > layout.Data.Width && !needHorizScrollbar) {
firstVisibleRow = oldFirstVisibleRow;
int horizHeight = horizScrollBar.Height;
layout.Data.Height -= horizHeight;
if (layout.RowHeadersVisible)
layout.RowHeaders.Height -= horizHeight;
needHorizScrollbar = true;
recountRows = true;
}
if (recountRows) {
ComputeVisibleRows();
if (numTotallyVisibleRows != DataGridRowsLength && !needVertScrollbar) {
int vertWidth = vertScrollBar.Width;
layout.Data.Width -= vertWidth;
if (layout.ColumnHeadersVisible)
{
if (alignToRight)
layout.ColumnHeaders.X += vertWidth;
layout.ColumnHeaders.Width -= vertWidth;
}
needVertScrollbar = true;
}
}
layout.ResizeBoxRect = new Rectangle();
if (needVertScrollbar && needHorizScrollbar) {
Rectangle data = layout.Data;
layout.ResizeBoxRect = new Rectangle(alignToRight ? data.X : data.Right,
data.Bottom,
vertScrollBar.Width,
horizScrollBar.Height);
}
if (needHorizScrollbar && nGridCols > 0) {
//Debug.WriteLineIf(CompModSwitches.DataGridScrolling.TraceVerbose, "DataGridScrolling: foo");
int widthNotVisible = totalWidth - layout.Data.Width;
horizScrollBar.Minimum = 0;
horizScrollBar.Maximum = totalWidth;
horizScrollBar.SmallChange = 1;
horizScrollBar.LargeChange = Math.Max(totalWidth - widthNotVisible, 0);
horizScrollBar.Enabled = this.Enabled;
horizScrollBar.RightToLeft = RightToLeft;
horizScrollBar.Bounds = new Rectangle(alignToRight ? layout.Inside.X + layout.ResizeBoxRect.Width : layout.Inside.X,
layout.Data.Bottom,
layout.Inside.Width - layout.ResizeBoxRect.Width,
horizScrollBar.Height);
horizScrollBar.Visible = true;
}
else {
HorizontalOffset = 0;
horizScrollBar.Visible = false;
}
if (needVertScrollbar) {
int vertScrollBarTop = layout.Data.Y;
if (layout.ColumnHeadersVisible)
vertScrollBarTop = layout.ColumnHeaders.Y;
// if numTotallyVisibleRows == 0 ( the height of the row is bigger than the height of
// the grid ) then scroll in increments of 1.
vertScrollBar.LargeChange = numTotallyVisibleRows != 0 ? numTotallyVisibleRows : 1;
vertScrollBar.Bounds = new Rectangle(alignToRight ? layout.Data.X : layout.Data.Right,
vertScrollBarTop,
vertScrollBar.Width,
layout.Data.Height + layout.ColumnHeaders.Height);
vertScrollBar.Enabled = this.Enabled;
vertScrollBar.Visible = true;
if (alignToRight)
layout.Data.X += vertScrollBar.Width;
}
else {
vertScrollBar.Visible = false;
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.NavigateBack"]/*' />
/// <devdoc>
/// <para>Navigates back to the table previously displayed in the grid.</para>
/// </devdoc>
public void NavigateBack() {
if (!CommitEdit() || parentRows.IsEmpty())
return;
// when navigating back, if the grid is inAddNewRow state, cancel the currentEdit.
// we do not need to recreate the rows cause we are navigating back.
// the grid will catch any exception that happens.
if (gridState[GRIDSTATE_inAddNewRow]) {
gridState[GRIDSTATE_inAddNewRow] = false;
try {
listManager.CancelCurrentEdit();
} catch {
}
} else {
UpdateListManager();
}
DataGridState newState = parentRows.PopTop();
ResetMouseState();
newState.PullState(this, false); // we do not want to create columns when navigating back
// we need to have originalState != null when we process
// Set_ListManager in the NavigateBack/NavigateTo methods.
// otherwise the DataSource_MetaDataChanged event will not get registered
// properly
if (parentRows.GetTopParent() == null)
originalState = null;
DataGridRow[] localGridRows = this.DataGridRows;
// what if the user changed the ReadOnly property
// on the grid while the user was navigating to the child rows?
//
// what if the policy does not allow for allowAdd?
//
if ((this.ReadOnly || !policy.AllowAdd) == (localGridRows[DataGridRowsLength -1] is DataGridAddNewRow)) {
int newDataGridRowsLength = (ReadOnly || !policy.AllowAdd) ? DataGridRowsLength - 1 : DataGridRowsLength + 1;
DataGridRow[] newDataGridRows = new DataGridRow[newDataGridRowsLength];
for (int i = 0; i < Math.Min(newDataGridRowsLength, DataGridRowsLength); i++) {
newDataGridRows[i] = DataGridRows[i];
}
if(!this.ReadOnly && policy.AllowAdd)
newDataGridRows[newDataGridRowsLength - 1] = new DataGridAddNewRow(this, this.myGridTable, newDataGridRowsLength-1);
SetDataGridRows(newDataGridRows, newDataGridRowsLength);
}
// when we navigate back from a child table,
// it may be the case that in between the user added a tableStyle that is different
// from the one that is currently in the grid
// in that case, we need to reset the dataGridTableStyle in the rows
localGridRows = this.DataGridRows;
if (localGridRows != null && localGridRows.Length != 0) {
DataGridTableStyle dgTable = localGridRows[0].DataGridTableStyle;
if (dgTable != this.myGridTable) {
for (int i = 0; i < localGridRows.Length; i ++)
localGridRows[i].DataGridTableStyle = this.myGridTable;
}
}
// if we have the default table, when we navigate back
// we also have the default gridColumns, w/ width = -1
// we need to set the width on the new gridColumns
//
if (this.myGridTable.GridColumnStyles.Count > 0 && this.myGridTable.GridColumnStyles[0].Width == -1) {
#if DEBUG
GridColumnStylesCollection cols = this.myGridTable.GridColumnStyles;
for (int i = 0; i < cols.Count; i++) {
Debug.Assert(cols[i].Width == -1, "Sanity check");
}
Debug.Assert(this.myGridTable.IsDefault, "when we navigate to the parent rows and the columns have widths -1 we are using the default table");
#endif // DEBUG
InitializeColumnWidths();
}
// reset the currentRow to the old position in the listmanager:
currentRow = this.ListManager.Position == -1 ? 0 : this.ListManager.Position;
// if the AllowNavigation changed while the user was navigating the
// child tables, so that the new navigation mode does not allow childNavigation anymore
// then reset the rows
if (!AllowNavigation) {
RecreateDataGridRows();
}
caption.BackButtonActive = (parentRows.GetTopParent() != null) && AllowNavigation;
caption.BackButtonVisible = caption.BackButtonActive;
caption.DownButtonActive = (parentRows.GetTopParent() != null);
PerformLayout();
Invalidate();
// reposition the scroll bar
if (vertScrollBar.Visible)
vertScrollBar.Value = firstVisibleRow;
if (horizScrollBar.Visible)
horizScrollBar.Value = HorizontalOffset + negOffset;
Edit();
OnNavigate(new NavigateEventArgs(false));
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.NavigateTo"]/*' />
/// <devdoc>
/// <para>
/// Navigates to the table specified by row and relation
/// name.
/// </para>
/// </devdoc>
public void NavigateTo(int rowNumber, String relationName) {
// do not navigate if AllowNavigation is set to false
if (!AllowNavigation)
return;
DataGridRow[] localGridRows = DataGridRows;
if (rowNumber < 0 || rowNumber > DataGridRowsLength - (policy.AllowAdd ? 2:1)) {
throw new ArgumentOutOfRangeException("rowNumber");
}
EnsureBound();
DataGridRow source = localGridRows[rowNumber];
NavigateTo(relationName, source, false);
}
internal void NavigateTo(string relationName, DataGridRow source, bool fromRow) {
// do not navigate if AllowNavigation is set to false
if (!AllowNavigation)
return;
// Commit the edit if possible
if (!CommitEdit())
return;
DataGridState childState;
try {
childState = CreateChildState(relationName, source);
}
catch {
// if we get an error when creating the RelatedCurrencyManager
// then navigateBack and ignore the exception.
//
NavigateBack();
return;
}
// call EndCurrentEdit before navigating.
// if we get an exception, we do not navigate.
//
try {
this.listManager.EndCurrentEdit();
}
catch {
return;
}
// Preserve our current state
// we need to do this after the EndCurrentEdit, otherwise the
// DataGridState will get the listChanged event from the EndCurrentEdit
DataGridState dgs = new DataGridState(this);
dgs.LinkingRow = source;
// we need to update the Position in the ListManager
// ( the RelatedListManager uses only the position in the parentManager
// to create the childRows
//
// before the code was calling CurrentCell = this and such
// we should only call EndCurrentEdit ( which the code was doing anyway )
// and then set the position in the listManager to the new row.
//
if (source.RowNumber != CurrentRow)
this.listManager.Position = source.RowNumber;
// We save our state if the parent rows stack is empty.
if (parentRows.GetTopParent() == null) {
originalState = dgs;
}
parentRows.AddParent(dgs);
NavigateTo(childState);
OnNavigate(new NavigateEventArgs(true));
if (fromRow) {
// OnLinkClick(EventArgs.Empty);
}
}
private void NavigateTo(DataGridState childState) {
// we are navigating... better stop editing.
EndEdit();
// also, we are no longer in editOrNavigate mode either
gridState[GRIDSTATE_isNavigating] = false;
// reset hot tracking
ResetMouseState();
// Retrieve the child state
childState.PullState(this, true); // true for creating columns when we navigate to child rows
if (this.listManager.Position != this.currentRow) {
this.currentRow = listManager.Position == -1 ? 0 : listManager.Position;
}
if (parentRows.GetTopParent() != null) {
caption.BackButtonActive = AllowNavigation;
caption.BackButtonVisible = caption.BackButtonActive;
caption.DownButtonActive = true;
}
HorizontalOffset = 0;
PerformLayout();
Invalidate();
}
/// <devdoc>
/// Given a coordinate in the control this method returns
/// the equivalent point for a row.
/// </devdoc>
private Point NormalizeToRow(int x, int y, int row) {
Debug.Assert(row >= firstVisibleRow && row < firstVisibleRow + numVisibleRows,
"Row " + row.ToString(CultureInfo.InvariantCulture) + "is not visible! firstVisibleRow = " +
firstVisibleRow.ToString(CultureInfo.InvariantCulture) + ", numVisibleRows = " +
numVisibleRows.ToString(CultureInfo.InvariantCulture));
Point origin = new Point(0, layout.Data.Y);
DataGridRow[] localGridRows = DataGridRows;
for (int r = firstVisibleRow; r < row; ++r) {
origin.Y += localGridRows[r].Height;
}
// when hittesting for the PlusMinus, the code in the DataGridRelationshipRow
// will use real X coordinate ( the one from layout.RowHeaders ) to paint the glyph
//
return new Point(x, y - origin.Y);
}
internal void OnColumnCollectionChanged(object sender, CollectionChangeEventArgs e) {
DataGridTableStyle table = (DataGridTableStyle)sender;
if (table.Equals(this.myGridTable)) {
// if we changed the column collection, then we need to set the property
// descriptors in the column collection.
// unless the user set the propertyDescriptor in the columnCollection
//
if (!this.myGridTable.IsDefault) {
// if the element in the collectionChangeEventArgs is not null
// and the action is refresh, then it means that the user
// set the propDesc. we do not want to override this.
if (e.Action != CollectionChangeAction.Refresh || e.Element == null)
PairTableStylesAndGridColumns(this.listManager, this.myGridTable, false);
}
Invalidate();
PerformLayout();
}
}
/// <devdoc>
/// Paints column headers.
/// </devdoc>
private void PaintColumnHeaders(Graphics g) {
bool alignToLeft = isRightToLeft();
Rectangle boundingRect = layout.ColumnHeaders;
if (!alignToLeft)
boundingRect.X -= negOffset;
boundingRect.Width += negOffset;
int columnHeaderWidth = PaintColumnHeaderText(g, boundingRect);
if (alignToLeft)
boundingRect.X = boundingRect.Right - columnHeaderWidth;
boundingRect.Width = columnHeaderWidth;
if (!FlatMode) {
ControlPaint.DrawBorder3D(g, boundingRect, Border3DStyle.RaisedInner);
boundingRect.Inflate(-1, -1);
// g.SetPen(OldSystemPens.Control);
// g.OldBrush = (OldSystemBrushes.Hollow);
boundingRect.Width --;
boundingRect.Height--;
g.DrawRectangle(SystemPens.Control, boundingRect);
}
}
private int PaintColumnHeaderText(Graphics g, Rectangle boundingRect) {
int cx = 0;
Rectangle textBounds = boundingRect;
GridColumnStylesCollection gridColumns = this.myGridTable.GridColumnStyles;
bool alignRight = isRightToLeft();
int nGridCols = gridColumns.Count;
// for sorting
PropertyDescriptor sortProperty = null;
sortProperty = this.ListManager.GetSortProperty();
// Now paint the column header text!
for (int col = firstVisibleCol; col < nGridCols; ++col) {
if (gridColumns[col].PropertyDescriptor == null)
continue;
if (cx > boundingRect.Width)
break;
bool columnSorted = sortProperty != null && sortProperty.Equals(gridColumns[col].PropertyDescriptor);
TriangleDirection whichWay = TriangleDirection.Up;
if (columnSorted)
{
ListSortDirection direction = this.ListManager.GetSortDirection();
if (direction == ListSortDirection.Descending)
whichWay = TriangleDirection.Down;
}
if (alignRight)
{
textBounds.Width = gridColumns[col].Width -
(columnSorted ? textBounds.Height : 0);
textBounds.X = boundingRect.Right - cx - textBounds.Width;
}
else
{
textBounds.X = boundingRect.X + cx;
textBounds.Width = gridColumns[col].Width -
(columnSorted ? textBounds.Height : 0);
}
// at the moment we paint some pixels twice.
// we should not call FilLRectangle, once the real GDI+ is there, we will have no need to do that
// if the user set the HeaderBackBrush property on the
// dataGrid, then use that property
Brush headerBrush;
if (this.myGridTable.IsDefault)
headerBrush = HeaderBackBrush;
else
headerBrush = this.myGridTable.HeaderBackBrush;
g.FillRectangle(headerBrush, textBounds);
// granted, the code would be a lot cleaner if we were using a "new Rectangle"
// but like this will be faster
if (alignRight)
{
textBounds.X -= 2; textBounds.Y += 2;
}
else
{
textBounds.X += 2; textBounds.Y += 2;
}
StringFormat format = new StringFormat();
// the columnHeaderText alignment should be the same as
// the alignment in the column
//
HorizontalAlignment colAlignment = gridColumns[col].Alignment;
format.Alignment = colAlignment == HorizontalAlignment.Right ? StringAlignment.Far :
colAlignment == HorizontalAlignment.Center ? StringAlignment.Center :
StringAlignment.Near;
// part 1, section 1: the column headers should not wrap
format.FormatFlags |= StringFormatFlags.NoWrap;
if (alignRight) {
format.FormatFlags |= StringFormatFlags.DirectionRightToLeft;
format.Alignment = StringAlignment.Near;
}
g.DrawString(gridColumns[col].HeaderText,
this.myGridTable.IsDefault ? this.HeaderFont : this.myGridTable.HeaderFont,
this.myGridTable.IsDefault ? this.HeaderForeBrush : this.myGridTable.HeaderForeBrush,
textBounds,
format);
format.Dispose();
if (alignRight)
{
textBounds.X += 2; textBounds.Y -= 2;
}
else
{
textBounds.X -= 2; textBounds.Y -= 2;
}
if (columnSorted) {
//
Rectangle triBounds = new Rectangle(alignRight ? textBounds.X - textBounds.Height : textBounds.Right,
textBounds.Y,
textBounds.Height,
textBounds.Height);
g.FillRectangle(headerBrush, triBounds);
int deflateValue = Math.Max(0,(textBounds.Height - 5) / 2);
triBounds.Inflate(-deflateValue, -deflateValue);
Pen pen1 = new Pen(this.BackgroundBrush);
Pen pen2 = new Pen(this.myGridTable.BackBrush);
Triangle.Paint(g, triBounds, whichWay, headerBrush, pen1, pen2, pen1, true);
pen1.Dispose();
pen2.Dispose();
}
int paintedWidth = textBounds.Width + (columnSorted ? textBounds.Height : 0);
if (!FlatMode) {
if (alignRight && columnSorted)
textBounds.X -= textBounds.Height;
textBounds.Width = paintedWidth;
ControlPaint.DrawBorder3D(g, textBounds, Border3DStyle.RaisedInner);
}
cx += paintedWidth;
}
// paint the possible exposed portion to the right ( or left, as the case may be)
if (cx < boundingRect.Width) {
textBounds = boundingRect;
if (!alignRight)
textBounds.X += cx;
textBounds.Width -= cx;
g.FillRectangle(backgroundBrush, textBounds);
}
return cx;
}
/// <devdoc>
/// Paints a border around the bouding rectangle given
/// </devdoc>
private void PaintBorder(Graphics g, Rectangle bounds)
{
if (BorderStyle == BorderStyle.None)
return;
if (BorderStyle == BorderStyle.Fixed3D) {
Border3DStyle style = Border3DStyle.Sunken;
ControlPaint.DrawBorder3D(g, bounds, style );
}
else if (BorderStyle == BorderStyle.FixedSingle) {
Brush br;
if (this.myGridTable.IsDefault)
br = this.HeaderForeBrush;
else
br = this.myGridTable.HeaderForeBrush;
g.FillRectangle(br, bounds.X, bounds.Y, bounds.Width + 2, 2);
g.FillRectangle(br, bounds.Right - 2, bounds.Y, 2, bounds.Height + 2);
g.FillRectangle(br, bounds.X, bounds.Bottom - 2, bounds.Width + 2, 2);
g.FillRectangle(br, bounds.X, bounds.Y, 2, bounds.Height + 2);
}
else {
Pen pen = SystemPens.WindowFrame;
bounds.Width --;
bounds.Height--;
g.DrawRectangle(pen, bounds);
}
}
/// <devdoc>
/// Paints the grid in the bounding rectangle given.
/// This includes the column headers and each visible row.
/// </devdoc>
private void PaintGrid(Graphics g, Rectangle gridBounds) {
Debug.WriteLineIf(CompModSwitches.DataGridPainting.TraceVerbose, "DataGridPainting: PaintGrid on " + gridBounds.ToString());
Rectangle rc = gridBounds;
if (this.listManager != null) {
if (layout.ColumnHeadersVisible) {
Region r = g.Clip;
g.SetClip(layout.ColumnHeaders);
PaintColumnHeaders(g);
g.Clip = r;
r.Dispose();
int columnHeaderHeight = layout.ColumnHeaders.Height;
rc.Y += columnHeaderHeight;
rc.Height -= columnHeaderHeight;
}
if (layout.TopLeftHeader.Width > 0) {
if (this.myGridTable.IsDefault)
g.FillRectangle(this.HeaderBackBrush, layout.TopLeftHeader);
else
g.FillRectangle(this.myGridTable.HeaderBackBrush, layout.TopLeftHeader);
if (!FlatMode) {
ControlPaint.DrawBorder3D(g, layout.TopLeftHeader, Border3DStyle.RaisedInner);
}
}
PaintRows(g, ref rc) ;
}
// paint the possible exposed portion below
if (rc.Height > 0) {
g.FillRectangle(backgroundBrush, rc);
}
}
private void DeleteDataGridRows(int deletedRows) {
if (deletedRows == 0)
return;
int currentRowCount = DataGridRowsLength;
int newDataGridRowsLength = currentRowCount - deletedRows + (gridState[GRIDSTATE_inAddNewRow] ? 1:0);
DataGridRow[] newDataGridRows = new DataGridRow[newDataGridRowsLength];
DataGridRow[] gridRows = DataGridRows;
// the number of selected entries so far in the array
int selectedEntries = 0;
for (int i = 0; i < currentRowCount; i++) {
if (gridRows[i].Selected) {
selectedEntries ++;
} else {
newDataGridRows[i - selectedEntries] = gridRows[i];
newDataGridRows[i - selectedEntries].number = i - selectedEntries;
}
}
if (gridState[GRIDSTATE_inAddNewRow]) {
newDataGridRows[currentRowCount - selectedEntries] = new DataGridAddNewRow(this, this.myGridTable, currentRowCount - selectedEntries);
gridState[GRIDSTATE_inAddNewRow] = false;
}
Debug.Assert(selectedEntries == deletedRows, "all the rows that would have been deleted should have been selected: selectedGridEntries " + selectedEntries.ToString(CultureInfo.InvariantCulture) + " deletedRows " + deletedRows.ToString(CultureInfo.InvariantCulture));
SetDataGridRows(newDataGridRows, newDataGridRowsLength);
}
/// <devdoc>
/// Paints the visible rows on the grid.
/// </devdoc>
private void PaintRows(Graphics g, ref Rectangle boundingRect) {
int cy = 0;
bool alignRight = isRightToLeft();
Rectangle rowBounds = boundingRect;
Rectangle dataBounds = Rectangle.Empty;
bool paintRowHeaders = layout.RowHeadersVisible;
Rectangle headerBounds = Rectangle.Empty;
int numRows = DataGridRowsLength;
DataGridRow[] localGridRows = DataGridRows;
int numCols = myGridTable.GridColumnStyles.Count - firstVisibleCol;
for (int row = firstVisibleRow; row < numRows; row++) {
if (cy > boundingRect.Height)
break;
rowBounds = boundingRect;
rowBounds.Height = localGridRows[row].Height;
rowBounds.Y = boundingRect.Y + cy;
// will add some errors
#if false
if (forDebug == 0 || forDebug == 1)
{
object dRowView = listManager[row];
DataRow dRow= ((DataRowView) dRowView).Row;
// dRow.RowError = "Error " + forDebug.ToString();
dRow.SetColumnError(forDebug, "another error " + forDebug.ToString());
/*
if (localGridRows[row].DataRow != null)
{
localGridRows[row].DataRow.RowError = "error " + forDebug.ToString();
localGridRows[row].DataRow.SetColumnError(forDebug, "another error " + forDebug.ToString());
}
*/
forDebug ++;
}
#endif // false
if (paintRowHeaders) {
headerBounds = rowBounds;
headerBounds.Width = layout.RowHeaders.Width;
if (alignRight)
{
headerBounds.X = rowBounds.Right - headerBounds.Width;
}
if (g.IsVisible(headerBounds)) {
localGridRows[row].PaintHeader(g, headerBounds, alignRight, gridState[GRIDSTATE_isEditing]);
g.ExcludeClip(headerBounds);
}
if (!alignRight)
rowBounds.X += headerBounds.Width;
rowBounds.Width -= headerBounds.Width;
}
if (g.IsVisible(rowBounds)) {
dataBounds = rowBounds;
if (!alignRight)
dataBounds.X -= negOffset;
dataBounds.Width += negOffset;
localGridRows[row].Paint(g, dataBounds, rowBounds, firstVisibleCol, numCols, alignRight);
}
cy += rowBounds.Height;
}
boundingRect.Y += cy;
boundingRect.Height -= cy;
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ProcessDialogKey"]/*' />
/// <devdoc>
/// <para>
/// Gets or sets a value that indicates whether a key should be processed
/// further.
/// </para>
/// </devdoc>
[UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)]
protected override bool ProcessDialogKey(Keys keyData) {
Debug.WriteLineIf(CompModSwitches.DataGridKeys.TraceVerbose, "DataGridKeys: ProcessDialogKey " + TypeDescriptor.GetConverter(typeof(Keys)).ConvertToString(keyData));
DataGridRow[] localGridRows = DataGridRows;
if (listManager != null && DataGridRowsLength > 0 && localGridRows[currentRow].OnKeyPress(keyData)) {
Debug.WriteLineIf(CompModSwitches.DataGridKeys.TraceVerbose, "DataGridKeys: Current Row ate the keystroke");
return true;
}
switch (keyData & Keys.KeyCode) {
case Keys.Tab:
case Keys.Up:
case Keys.Down:
case Keys.Left:
case Keys.Right:
case Keys.Next:
case Keys.Prior:
case Keys.Enter:
case Keys.Escape:
case Keys.Oemplus:
case Keys.Add:
case Keys.OemMinus:
case Keys.Subtract:
case Keys.Space:
case Keys.Delete:
case Keys.A:
KeyEventArgs ke = new KeyEventArgs(keyData);
if (ProcessGridKey(ke))
return true;
break;
case Keys.C:
if ((keyData & Keys.Control) != 0 && (keyData & Keys.Alt) == 0)
{
// the user pressed Ctrl-C
if (!Bound)
break;
// need to distinguish between selecting a set of rows, and
// selecting just one column.
if (numSelectedRows == 0)
{
// copy the data from one column only
if (currentRow < ListManager.Count)
{
GridColumnStylesCollection columns = myGridTable.GridColumnStyles;
if (currentCol >= 0 && currentCol < columns.Count)
{
DataGridColumnStyle column = columns[currentCol];
string text = column.GetDisplayText(column.GetColumnValueAtRow(ListManager, currentRow));
// copy the data to the clipboard
Clipboard.SetDataObject(text);
return true;
}
}
}
else
{
// the user selected a set of rows to copy the data from
int numRowsOutputted = 0; // the number of rows written to "text"
string text = "";
for (int i = 0; i < DataGridRowsLength; ++i)
{
if (localGridRows[i].Selected)
{
GridColumnStylesCollection columns = myGridTable.GridColumnStyles;
int numCols = columns.Count;
for (int j = 0; j < numCols; j++)
{
DataGridColumnStyle column = columns[j];
text += column.GetDisplayText(column.GetColumnValueAtRow(ListManager, i));
// do not put the delimiter at the end of the last column
if ( j < numCols - 1)
{
text += GetOutputTextDelimiter();
}
}
// put the hard enter "\r\n" only if this is not the last selected row
if (numRowsOutputted < numSelectedRows - 1)
{
text += "\r\n";
}
numRowsOutputted ++;
}
}
// copy the data to the clipboard
Clipboard.SetDataObject(text);
return true;
}
}
break;
}
return base.ProcessDialogKey(keyData);
}
private void DeleteRows(DataGridRow[] localGridRows) {
int rowsDeleted = 0;
int currentRowsCount = this.listManager == null ? 0 : this.listManager.Count;
if (this.Visible) BeginUpdateInternal();
try {
if (this.ListManager != null) {
for (int i = 0; i < this.DataGridRowsLength; i++) {
if (localGridRows[i].Selected) {
if (localGridRows[i] is DataGridAddNewRow) {
Debug.Assert(i == DataGridRowsLength - 1, "the location of addNewRow is " + i.ToString(CultureInfo.InvariantCulture) + " and there are " + DataGridRowsLength.ToString(CultureInfo.InvariantCulture) + " rows ");
localGridRows[i].Selected = false;
}
else {
this.ListManager.RemoveAt(i - rowsDeleted);
rowsDeleted ++;
}
}
}
}
}
catch {
// if we got an exception from the back end
// when deleting the rows then we should reset
// our rows and re-throw the exception
//
RecreateDataGridRows();
gridState[GRIDSTATE_inDeleteRow] = false;
if (this.Visible) EndUpdateInternal();
throw;
}
// keep the copy of the old rows in place
// it may be the case that deleting one row could cause multiple rows to be deleted in the same list
//
if (this.listManager != null && currentRowsCount == this.listManager.Count + rowsDeleted) {
DeleteDataGridRows(rowsDeleted);
} else {
RecreateDataGridRows();
}
gridState[GRIDSTATE_inDeleteRow] = false;
if (this.Visible) EndUpdateInternal();
if (this.listManager != null && currentRowsCount != this.listManager.Count + rowsDeleted) {
Invalidate();
}
}
// convention:
// if we return -1 it means that the user was going left and there were no visible columns to the left of the current one
// if we return cols.Count + 1 it means that the user was going right and there were no visible columns to the right of the currrent
private int MoveLeftRight(GridColumnStylesCollection cols, int startCol, bool goRight) {
int i;
if (goRight) {
for (i = startCol + 1; i < cols.Count; i++) {
// if (cols[i].Visible && cols[i].PropertyDescriptor != null)
if (cols[i].PropertyDescriptor != null)
return i;
}
return i;
} else {
for (i = startCol - 1; i>=0; i--) {
// if (cols[i].Visible && cols[i].PropertyDescriptor != null)
if (cols[i].PropertyDescriptor != null)
return i;
}
return i;
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ProcessGridKey"]/*' />
/// <devdoc>
/// <para>
/// Processes keys for grid navigation.
/// </para>
/// </devdoc>
[
SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)
]
[SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")]
// SECREVIEW: This seems safe, but could anything dangerous occur here?
protected bool ProcessGridKey(KeyEventArgs ke) {
Debug.WriteLineIf(CompModSwitches.DataGridKeys.TraceVerbose, "DataGridKeys: ProcessGridKey "+ TypeDescriptor.GetConverter(typeof(Keys)).ConvertToString(ke.KeyCode));
if (listManager == null || myGridTable == null)
return false;
DataGridRow[] localGridRows = DataGridRows;
KeyEventArgs biDiKe = ke;
// check for Bi-Di
//
if (isRightToLeft())
{
switch(ke.KeyCode) {
case Keys.Left:
biDiKe = new KeyEventArgs((Keys.Right | ke.Modifiers));
break;
case Keys.Right:
biDiKe = new KeyEventArgs((Keys.Left | ke.Modifiers));
break;
default:
break;
}
}
GridColumnStylesCollection cols = this.myGridTable.GridColumnStyles;
int firstColumnMarkedVisible = 0;
int lastColumnMarkedVisible = cols.Count;
for (int i = 0; i < cols.Count; i++) {
if (cols[i].PropertyDescriptor != null) {
firstColumnMarkedVisible = i;
break;
}
}
for (int i = cols.Count - 1 ; i >= 0; i--) {
if (cols[i].PropertyDescriptor != null) {
lastColumnMarkedVisible = i;
break;
}
}
switch (biDiKe.KeyCode) {
case Keys.Tab:
return ProcessTabKey(biDiKe.KeyData);
case Keys.Up:
gridState[GRIDSTATE_childLinkFocused] = false;
if (this.dataGridRowsLength == 0) {
return true;
}
if (biDiKe.Control && !biDiKe.Alt)
{
if (biDiKe.Shift)
{
DataGridRow [] gridRows = DataGridRows;
int savedCurrentRow = currentRow;
CurrentRow = 0;
ResetSelection();
for (int i = 0; i <= savedCurrentRow; i ++)
gridRows[i].Selected = true;
numSelectedRows = savedCurrentRow + 1;
// hide the edit box
//
EndEdit();
return true;
}
// do not make the parentRowsVisible = false;
// ParentRowsVisible = false;
ResetSelection();
CurrentRow = 0;
Debug.Assert(ListManager.Position == CurrentCell.RowNumber || listManager.Count == 0, "current row out of ssync with DataSource");
return true;
}
else if (biDiKe.Shift)
{
DataGridRow [] gridRows = DataGridRows;
// keep a continous selected region
if (gridRows[currentRow].Selected) {
if (currentRow >= 1) {
if (gridRows[currentRow - 1].Selected) {
if (currentRow >= DataGridRowsLength - 1 || !gridRows[currentRow+1].Selected) {
numSelectedRows --;
gridRows[currentRow].Selected = false;
}
} else {
numSelectedRows += gridRows[currentRow -1].Selected ? 0:1;
gridRows[currentRow - 1].Selected = true;
}
CurrentRow --;
}
} else {
numSelectedRows ++;
gridRows[currentRow].Selected = true;
if (currentRow >= 1) {
numSelectedRows += gridRows[currentRow-1].Selected ? 0:1;
gridRows[currentRow-1].Selected = true;
CurrentRow --;
}
}
// hide the edit box:
//
EndEdit();
Debug.Assert(ListManager.Position == CurrentCell.RowNumber || listManager.Count == 0, "current row out of ssync with DataSource");
return true;
}
else if (biDiKe.Alt)
{
// will need to collapse all child table links
// -1 is for all rows, and false is for collapsing the rows
SetRowExpansionState(-1, false);
Debug.Assert(ListManager.Position == CurrentCell.RowNumber || listManager.Count == 0, "current row out of ssync with DataSource");
return true;
}
ResetSelection();
CurrentRow = CurrentRow - 1;
Edit();
Debug.Assert(ListManager.Position == CurrentCell.RowNumber || listManager.Count == 0, "current row out of ssync with DataSource");
break;
case Keys.Down:
gridState[GRIDSTATE_childLinkFocused] = false;
if (this.dataGridRowsLength == 0) {
return true;
}
if (biDiKe.Control && !biDiKe.Alt)
{
if (biDiKe.Shift)
{
int savedCurrentRow = currentRow;
CurrentRow = Math.Max(0, DataGridRowsLength - (policy.AllowAdd ? 2:1));
DataGridRow [] gridRows = DataGridRows;
ResetSelection();
for (int i = savedCurrentRow; i <= currentRow; i++)
gridRows[i].Selected = true;
numSelectedRows = currentRow - savedCurrentRow + 1;
// hide the edit box
//
EndEdit();
return true;
}
// do not make the parentRowsVisible = true;
// ParentRowsVisible = true;
ResetSelection();
CurrentRow = Math.Max(0, DataGridRowsLength - (policy.AllowAdd ? 2 : 1));
Debug.Assert(ListManager.Position == CurrentCell.RowNumber || listManager.Count == 0, "current row out of ssync with DataSource");
return true;
}
else if (biDiKe.Shift)
{
DataGridRow [] gridRows = DataGridRows;
// keep a continous selected region
if (gridRows[currentRow].Selected) {
// -1 because we index from 0
if (currentRow < DataGridRowsLength - (policy.AllowAdd ? 1:0) - 1) {
if (gridRows[currentRow + 1].Selected) {
if (currentRow == 0 || !gridRows[currentRow - 1].Selected) {
numSelectedRows --;
gridRows[currentRow].Selected = false;
}
} else {
numSelectedRows += gridRows[currentRow + 1].Selected ? 0:1;
gridRows[currentRow + 1].Selected = true;
}
CurrentRow ++;
}
} else {
numSelectedRows ++;
gridRows[currentRow].Selected = true;
// -1 because we index from 0, and -1 so this is not the last row
// so it adds to -2
if (currentRow < DataGridRowsLength - (policy.AllowAdd ? 1:0) - 1) {
CurrentRow ++;
numSelectedRows += gridRows[currentRow].Selected ? 0:1;
gridRows[currentRow].Selected = true;
}
}
// hide the edit box:
//
EndEdit();
Debug.Assert(ListManager.Position == CurrentCell.RowNumber || listManager.Count == 0, "current row out of ssync with DataSource");
return true;
}
else if (biDiKe.Alt)
{
// will need to expande all child table links
// -1 is for all rows, and true is for expanding the rows
SetRowExpansionState(-1, true);
return true;
}
ResetSelection();
Edit();
CurrentRow = CurrentRow + 1;
Debug.Assert(ListManager.Position == CurrentCell.RowNumber || listManager.Count == 0, "current row out of ssync with DataSource");
break;
case Keys.OemMinus:
case Keys.Subtract:
gridState[GRIDSTATE_childLinkFocused] = false;
if (biDiKe.Control && !biDiKe.Alt)
{
SetRowExpansionState(-1, false);
return true;
}
return false;
case Keys.Oemplus:
case Keys.Add:
gridState[GRIDSTATE_childLinkFocused] = false;
if (biDiKe.Control)
{
SetRowExpansionState(-1, true);
// hide the edit box
//
EndEdit();
return true;
}
return false;
case Keys.Space:
gridState[GRIDSTATE_childLinkFocused] = false;
if (this.dataGridRowsLength == 0) {
return true;
}
if (biDiKe.Shift)
{
ResetSelection();
EndEdit();
DataGridRow [] gridRows = DataGridRows;
gridRows[currentRow].Selected = true;
numSelectedRows = 1;
return true;
}
return false;
case Keys.Next:
gridState[GRIDSTATE_childLinkFocused] = false;
if (this.dataGridRowsLength == 0) {
return true;
}
if (biDiKe.Shift)
{
int savedCurrentRow = currentRow;
CurrentRow = Math.Min(DataGridRowsLength - (policy.AllowAdd ? 2:1), currentRow + numTotallyVisibleRows);
DataGridRow [] gridRows = DataGridRows;
for (int i = savedCurrentRow; i <= currentRow; i++)
{
if (!gridRows[i].Selected)
{
gridRows[i].Selected = true;
numSelectedRows ++;
}
}
// hide edit box
//
EndEdit();
} else if (biDiKe.Control && !biDiKe.Alt) {
// map ctrl-pageDown to show the parentRows
ParentRowsVisible = true;
}
else
{
ResetSelection();
CurrentRow = Math.Min(DataGridRowsLength - (policy.AllowAdd ? 2:1),
CurrentRow + numTotallyVisibleRows);
}
break;
case Keys.Prior:
if (this.dataGridRowsLength == 0) {
return true;
}
gridState[GRIDSTATE_childLinkFocused] = false;
if (biDiKe.Shift)
{
int savedCurrentRow = currentRow;
CurrentRow = Math.Max(0, CurrentRow - numTotallyVisibleRows);
DataGridRow [] gridRows = DataGridRows;
for (int i = savedCurrentRow; i >= currentRow; i--)
{
if ( !gridRows[i].Selected)
{
gridRows[i].Selected = true;
numSelectedRows ++;
}
}
// hide the edit box
//
EndEdit();
} else if (biDiKe.Control && !biDiKe.Alt) {
// map ctrl-pageUp to hide the parentRows
ParentRowsVisible = false;
}
else
{
ResetSelection();
CurrentRow = Math.Max(0,
CurrentRow - numTotallyVisibleRows);
}
break;
case Keys.Left:
gridState[GRIDSTATE_childLinkFocused] = false;
ResetSelection();
if ((biDiKe.Modifiers & Keys.Modifiers) == Keys.Alt)
{
if (Caption.BackButtonVisible)
NavigateBack();
return true;
}
if ((biDiKe.Modifiers & Keys.Control) == Keys.Control)
{
// we should navigate to the first visible column
CurrentColumn = firstColumnMarkedVisible;
break;
}
if (currentCol == firstColumnMarkedVisible && currentRow != 0)
{
CurrentRow = CurrentRow - 1;
int newCol = MoveLeftRight(this.myGridTable.GridColumnStyles, this.myGridTable.GridColumnStyles.Count, false);
Debug.Assert(newCol != -1, "there should be at least a visible column, right?");
CurrentColumn = newCol;
}
else {
int newCol = MoveLeftRight(this.myGridTable.GridColumnStyles, currentCol, false);
if (newCol == -1) {
if (currentRow == 0)
return true;
else {
// go to the previous row:
CurrentRow = CurrentRow - 1;
CurrentColumn = lastColumnMarkedVisible;
}
} else {
CurrentColumn = newCol;
}
}
break;
case Keys.Right:
gridState[GRIDSTATE_childLinkFocused] = false;
ResetSelection();
if ((biDiKe.Modifiers & Keys.Control) == Keys.Control && !biDiKe.Alt)
{
// we should navigate to the last column that is marked as Visible
CurrentColumn = lastColumnMarkedVisible;
break;
}
if (currentCol == lastColumnMarkedVisible && currentRow != DataGridRowsLength - 1)
{
CurrentRow = CurrentRow + 1;
// navigate to the first visible column
CurrentColumn = firstColumnMarkedVisible;
}
else {
int newCol = MoveLeftRight(this.myGridTable.GridColumnStyles, this.currentCol, true);
if (newCol == cols.Count + 1) {
// navigate to the first visible column
// and the next row
//
CurrentColumn = firstColumnMarkedVisible;
CurrentRow ++;
}
else
CurrentColumn = newCol;
}
break;
case Keys.F2:
gridState[GRIDSTATE_childLinkFocused] = false;
ResetSelection();
Edit();
break;
#if DEBUG
case Keys.F12:
gridState[GRIDSTATE_childLinkFocused] = false;
AddNewRow();
break;
#endif
case Keys.Home:
gridState[GRIDSTATE_childLinkFocused] = false;
if (this.dataGridRowsLength == 0) {
return true;
}
ResetSelection();
CurrentColumn = 0;
if (biDiKe.Control && !biDiKe.Alt) {
int currentRowSaved = currentRow;
CurrentRow = 0;
if (biDiKe.Shift)
{
// Ctrl-Shift-Home will select all the rows up to the first one
DataGridRow[] gridRows = DataGridRows;
for (int i = 0; i <= currentRowSaved; i++)
{
gridRows[i].Selected = true;
numSelectedRows ++;
}
// hide the edit box:
EndEdit();
}
Debug.Assert(ListManager.Position == CurrentCell.RowNumber || listManager.Count == 0, "current row out of ssync with DataSource");
return true;
}
Debug.Assert(ListManager.Position == CurrentCell.RowNumber || listManager.Count == 0, "current row out of ssync with DataSource");
break;
case Keys.Delete:
gridState[GRIDSTATE_childLinkFocused] = false;
if (policy.AllowRemove && numSelectedRows > 0) {
#if DEBUG
// when the list is empty, then the position
// in the listManager is -1, and the currentPosition in the grid is 0
if (ListManager != null && ListManager.Count > 0) {
Debug.Assert(ListManager.Position == this.currentRow,
"Current row out of sync with DataSource",
"The DataSource's Position property should be mirrored by the CurrentCell.RowNumber of the DataGrid.");
}
#endif // DEBUG
gridState[GRIDSTATE_inDeleteRow] = true;
DeleteRows(localGridRows);
// set the currentRow to the position in the list
this.currentRow = this.listManager.Count == 0 ? 0 : this.listManager.Position;
numSelectedRows = 0;
} else {
// if we did not use the the Delete key, let the dataGridTextBox use it
return false;
}
break;
case Keys.End:
gridState[GRIDSTATE_childLinkFocused] = false;
if (this.dataGridRowsLength == 0) {
return true;
}
ResetSelection();
// go the the last visible column
CurrentColumn = lastColumnMarkedVisible;
if (biDiKe.Control && !biDiKe.Alt) {
int savedCurrentRow = currentRow;
CurrentRow = Math.Max(0, DataGridRowsLength - (policy.AllowAdd ? 2:1));
if (biDiKe.Shift)
{
// Ctrl-Shift-Home will select all the rows up to the first one
DataGridRow[] gridRows = DataGridRows;
for (int i = savedCurrentRow; i <= currentRow; i++)
{
gridRows[i].Selected = true;
}
numSelectedRows = currentRow - savedCurrentRow + 1;
// hide the edit box
//
EndEdit();
}
Debug.Assert(ListManager.Position == CurrentCell.RowNumber || listManager.Count == 0, "current row out of ssync with DataSource");
return true;
}
Debug.Assert(ListManager.Position == CurrentCell.RowNumber || listManager.Count == 0, "current row out of ssync with DataSource");
break;
case Keys.Enter:
gridState[GRIDSTATE_childLinkFocused] = false;
ResetSelection();
// yield the return key if there is no editing
if (!gridState[GRIDSTATE_isEditing])
return false;
// Ctrl-Enter will call EndCurrentEdit
if ((biDiKe.Modifiers & Keys.Control) != 0 && !biDiKe.Alt)
{
EndEdit();
HandleEndCurrentEdit();
Edit(); // put the edit box on the screen
}
else
{
// Do not commit the edit, cause reseting the
// current cell will do that
//
// CommitEdit();
CurrentRow = currentRow + 1;
}
break;
case Keys.A:
gridState[GRIDSTATE_childLinkFocused] = false;
if (biDiKe.Control && !biDiKe.Alt)
{
DataGridRow [] gridRows = DataGridRows;
for (int i = 0; i < DataGridRowsLength; i++)
if (gridRows[i] is DataGridRelationshipRow) gridRows[i].Selected = true;
numSelectedRows = DataGridRowsLength - (policy.AllowAdd ? 1 : 0);
// hide the edit box
//
EndEdit();
return true;
}
return false;
case Keys.Escape:
gridState[GRIDSTATE_childLinkFocused] = false;
ResetSelection();
if (gridState[GRIDSTATE_isEditing])
{
// rollback
AbortEdit();
// we have to invalidate the row header ( make it display the row selector instead of the pencil )
if (layout.RowHeadersVisible && this.currentRow > -1) {
Rectangle rowHdrRect = GetRowRect(this.currentRow);
rowHdrRect.Width = layout.RowHeaders.Width;
Invalidate(rowHdrRect);
}
// now put the edit column back on the screen
Edit();
}
else {
// add this protected virtual method for the XML designer team
CancelEditing();
Edit();
return false;
}
break;
}
return true;
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ProcessKeyPreview"]/*' />
/// <devdoc>
/// <para>Previews a keyboard message and returns a value indicating if the key was
/// consumed.</para>
/// </devdoc>
[
SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)
]
protected override bool ProcessKeyPreview(ref Message m) {
if (m.Msg == NativeMethods.WM_KEYDOWN) {
KeyEventArgs ke = new KeyEventArgs((Keys)(unchecked((int)(long)m.WParam)) | ModifierKeys);
switch (ke.KeyCode) {
case Keys.Up:
case Keys.Down:
case Keys.Prior:
case Keys.Next:
case Keys.Right:
case Keys.Left:
case Keys.Tab:
case Keys.Escape:
case Keys.Enter:
case Keys.OemMinus:
case Keys.Subtract:
case Keys.Oemplus:
case Keys.Add:
case Keys.Space:
case Keys.Home:
case Keys.End:
case Keys.F2:
case Keys.Delete:
case Keys.A:
return ProcessGridKey(ke);
}
// Ctrl-Tab will be sent as a tab paired w/ a control on the KeyUp message
//
} else if (m.Msg == NativeMethods.WM_KEYUP) {
KeyEventArgs ke = new KeyEventArgs((Keys)(unchecked((int)(long)m.WParam)) | ModifierKeys);
if (ke.KeyCode == Keys.Tab)
return ProcessGridKey(ke);
}
return base.ProcessKeyPreview(ref m);
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ProcessTabKey"]/*' />
/// <devdoc>
/// <para>
/// Gets a value indicating whether the Tab key should be processed.
/// </para>
/// </devdoc>
[UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)]
protected bool ProcessTabKey(Keys keyData) {
if (this.listManager == null || myGridTable == null)
return false;
bool wasEditing = false;
int columnCount = myGridTable.GridColumnStyles.Count;
bool biDi = isRightToLeft();
ResetSelection();
// Try to commit changes to cell if we were editing
if (gridState[GRIDSTATE_isEditing]) {
wasEditing = true;
if (!CommitEdit()) {
//MessageBox.Show("Could not commit changes! Press Escape to abort edit");
Edit(); // if we can't commit the value put the edit box so that the user sees where the focus is
return true;
}
}
if ((keyData & Keys.Control) == Keys.Control)
{
// when the user hits ctrl-alt-tab just ignore it.
if ((keyData & Keys.Alt) == Keys.Alt)
return true;
// navigate to the next control in the form
Keys ke = keyData & ~(Keys.Control);
EndEdit();
gridState[GRIDSTATE_editControlChanging] = true;
try {
FocusInternal();
} finally {
gridState[GRIDSTATE_editControlChanging] = false;
}
bool ret = false;
// SECREVIEW : Taking focus and activating a control in response
// : to a user gesture (WM_SETFOCUS) is OK.
//
IntSecurity.ModifyFocus.Assert();
try {
ret = base.ProcessDialogKey(ke);
}
finally {
CodeAccessPermission.RevertAssert();
}
return ret;
}
// see if the child relationships can use this TAB key
DataGridRow[] localRows = DataGridRows;
GridColumnStylesCollection cols = this.myGridTable.GridColumnStyles;
int lastColumnMarkedVisible = 0;
int firstColumnMarkedVisible = cols.Count-1;
// bug 70492: if we do not have any rows, then tab should move focus to the next control
//
if (localRows.Length == 0) {
EndEdit();
bool ret = false;
// SECREVIEW : ProcessDialogKey can generate a call to ContainerControl.SetActiveControl which demands ModifyFocus permission,
// we need to assert it here; the assert is safe, setting the active control does not expose any sec vulnerability
// indirectly.
//
IntSecurity.ModifyFocus.Assert();
try {
ret = base.ProcessDialogKey(keyData);
}
finally {
CodeAccessPermission.RevertAssert();
}
return ret;
}
for (int i = 0; i < cols.Count; i ++) {
// if (cols[i].Visible && cols[i].PropertyDescriptor != null) {
if (cols[i].PropertyDescriptor != null) {
firstColumnMarkedVisible = i;
break;
}
}
for (int i = cols.Count - 1; i >= 0; i --) {
// if (cols[i].Visible && cols[i].PropertyDescriptor != null) {
if (cols[i].PropertyDescriptor != null) {
lastColumnMarkedVisible = i;
break;
}
}
if (CurrentColumn == lastColumnMarkedVisible)
{
if (gridState[GRIDSTATE_childLinkFocused] || (!gridState[GRIDSTATE_childLinkFocused] && (keyData & Keys.Shift) != Keys.Shift)) {
if (localRows[CurrentRow].ProcessTabKey(keyData, layout.RowHeaders, isRightToLeft()))
{
if (cols.Count > 0)
cols[CurrentColumn].ConcedeFocus();
gridState[GRIDSTATE_childLinkFocused] = true;
// let the grid regain focus
// introduced because of that BeginInvoke thing in the OnLeave method....
if (gridState[GRIDSTATE_canFocus] && CanFocus && !Focused)
this.FocusInternal();
return true;
}
}
// actually, it turns out that we should leave the
// control if we are in the last row
if ((this.currentRow == this.DataGridRowsLength -1) && ((keyData & Keys.Shift) == 0)) {
EndEdit();
bool ret = false;
// SECREVIEW : ProcessDialogKey can generate a call to ContainerControl.SetActiveControl which demands ModifyFocus permission,
// we need to assert it here; the assert is safe, setting the active control does not expose any sec vulnerability
// indirectly.
//
IntSecurity.ModifyFocus.Assert();
try {
ret = base.ProcessDialogKey(keyData);
}
finally {
CodeAccessPermission.RevertAssert();
}
return ret;
}
}
if (CurrentColumn == firstColumnMarkedVisible)
{
// if the childLink is focused, then navigate within the relations
// in the row, otherwise expand the relations list for the row above
if (!gridState[GRIDSTATE_childLinkFocused]) {
if (CurrentRow != 0 && (keyData & Keys.Shift) == Keys.Shift)
{
if (localRows[CurrentRow - 1].ProcessTabKey(keyData, layout.RowHeaders, isRightToLeft()))
{
CurrentRow --;
if (cols.Count > 0)
cols[CurrentColumn].ConcedeFocus();
gridState[GRIDSTATE_childLinkFocused] = true;
// let the grid regain focus
// introduced because of that BeginInvoke thing in the OnLeave method....
if (gridState[GRIDSTATE_canFocus] && CanFocus && !Focused)
this.FocusInternal();
return true;
}
}
} else {
if (localRows[CurrentRow].ProcessTabKey(keyData, layout.RowHeaders, isRightToLeft())) {
return true;
} else {
// we were on the firstColumn, previously the link was focused
// we have to navigate to the last column
gridState[GRIDSTATE_childLinkFocused] = false;
CurrentColumn = lastColumnMarkedVisible;
return true;
}
}
// if we are on the first cell ( not on the addNewRow )
// then shift - tab should move to the next control on the form
if (this.currentRow == 0 && ((keyData & Keys.Shift) == Keys.Shift)) {
EndEdit();
bool ret = false;
// SECREVIEW : ProcessDialogKey can generate a call to ContainerControl.SetActiveControl which demands ModifyFocus permission,
// we need to assert it here; the assert is safe, setting the active control does not expose any sec vulnerability
// indirectly.
//
IntSecurity.ModifyFocus.Assert();
try {
ret = base.ProcessDialogKey(keyData);
}
finally {
CodeAccessPermission.RevertAssert();
}
return ret;
}
}
// move
if ((keyData & Keys.Shift) != Keys.Shift) {
// forward
if (CurrentColumn == lastColumnMarkedVisible) {
if (CurrentRow != DataGridRowsLength - 1)
CurrentColumn = firstColumnMarkedVisible;
CurrentRow = CurrentRow + 1;
}
else {
int nextCol = MoveLeftRight(cols, currentCol, true); // true for going right;
Debug.Assert(nextCol < cols.Count, "we already checked that we are not at the lastColumnMarkedVisible");
CurrentColumn = nextCol;
}
}
else {
// backward
if (CurrentColumn == firstColumnMarkedVisible) {
if (CurrentRow != 0)
{
CurrentColumn = lastColumnMarkedVisible;
}
if (!gridState[GRIDSTATE_childLinkFocused]) // bug 86803
CurrentRow --;
} else if (gridState[GRIDSTATE_childLinkFocused] && CurrentColumn == lastColumnMarkedVisible) {
// part deux: when we hilite the childLink and then press shift-tab, we
// don't want to navigate at the second to last column
InvalidateRow(this.currentRow);
Edit();
} else {
int prevCol = MoveLeftRight(cols, currentCol, false); // false for going left
Debug.Assert(prevCol != -1, "we already checked that we are not at the first columnMarked visible");
CurrentColumn = prevCol;
}
}
// if we got here, then invalidate childLinkFocused
//
gridState[GRIDSTATE_childLinkFocused] = false;
// Begin another edit if we were editing before
if (wasEditing) {
ResetSelection();
Edit();
}
return true;
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.CancelEditing"]/*' />
virtual protected void CancelEditing() {
CancelCursorUpdate();
// yield the escape key if there is no editing
// make the last row a DataGridAddNewRow
if (gridState[GRIDSTATE_inAddNewRow]) {
gridState[GRIDSTATE_inAddNewRow] = false;
DataGridRow[] localGridRows = this.DataGridRows;
localGridRows[DataGridRowsLength-1] = new DataGridAddNewRow(this, this.myGridTable, DataGridRowsLength -1);
SetDataGridRows(localGridRows, DataGridRowsLength);
}
}
internal void RecalculateFonts() {
try {
linkFont = new Font(Font, FontStyle.Underline);
}
catch {
}
fontHeight = Font.Height;
linkFontHeight = LinkFont.Height;
captionFontHeight = CaptionFont.Height;
if (this.myGridTable == null || this.myGridTable.IsDefault)
headerFontHeight = this.HeaderFont.Height;
else
headerFontHeight = myGridTable.HeaderFont.Height;
}
// the BackButtonClicked event:
//
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.BackButtonClick"]/*' />
/// <devdoc>
/// <para>Occurs when the BackButton is clicked.</para>
/// </devdoc>
[
SRCategory(SR.CatAction),
SRDescription(SR.DataGridBackButtonClickDescr)
]
public event EventHandler BackButtonClick {
add {
Events.AddHandler(EVENT_BACKBUTTONCLICK, value);
}
remove {
Events.RemoveHandler(EVENT_BACKBUTTONCLICK, value);
}
}
// the DownButtonClick event
//
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ShowParentDetailsButtonClick"]/*' />
/// <devdoc>
/// Occurs when the Down button is clicked.
/// </devdoc>
[
SRCategory(SR.CatAction),
SRDescription(SR.DataGridDownButtonClickDescr)
]
public event EventHandler ShowParentDetailsButtonClick {
add {
Events.AddHandler(EVENT_DOWNBUTTONCLICK, value);
}
remove {
Events.RemoveHandler(EVENT_DOWNBUTTONCLICK, value);
}
}
private void ResetMouseState() {
oldRow = -1;
gridState[GRIDSTATE_overCaption] = true;
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.ResetSelection"]/*' />
/// <devdoc>
/// Turns off selection for all rows that are selected.
/// </devdoc>
protected void ResetSelection() {
if (numSelectedRows > 0) {
DataGridRow[] localGridRows = DataGridRows;
for (int i = 0; i < DataGridRowsLength; ++i)
if (localGridRows[i].Selected)
localGridRows[i].Selected = false;
}
numSelectedRows = 0;
lastRowSelected = -1;
}
private void ResetParentRows() {
parentRows.Clear();
originalState = null;
caption.BackButtonActive = caption.DownButtonActive = caption.BackButtonVisible = false;
caption.SetDownButtonDirection(!layout.ParentRowsVisible);
}
/// <devdoc>
/// Re-initializes all UI related state.
/// </devdoc>
private void ResetUIState() {
gridState[GRIDSTATE_childLinkFocused] = false;
ResetSelection();
ResetMouseState();
PerformLayout();
Invalidate(); // we want to invalidate after we set up the scrollbars
// invalidate the horizontalscrollbar and the vertical scrollbar
//
if (horizScrollBar.Visible)
horizScrollBar.Invalidate();
if (vertScrollBar.Visible)
vertScrollBar.Invalidate();
}
/// <devdoc>
/// Scrolls the datagrid down an arbritrary number of rows.
/// </devdoc>
private void ScrollDown(int rows) {
//Debug.WriteLineIf(CompModSwitches.DataGridScrolling.TraceVerbose, "DataGridScrolling: ScrollDown, rows = " + rows.ToString());
if (rows != 0) {
ClearRegionCache();
// we should put "dataGridRowsLength -1"
int newFirstRow = Math.Max(0, Math.Min(firstVisibleRow + rows, this.DataGridRowsLength -1));
int oldFirstRow = firstVisibleRow;
firstVisibleRow = newFirstRow;
vertScrollBar.Value = newFirstRow;
bool wasEditing = this.gridState[GRIDSTATE_isEditing];
ComputeVisibleRows();
if (gridState[GRIDSTATE_isScrolling]) {
Edit();
// isScrolling is set to TRUE when the user scrolls.
// once we move the edit box, we finished processing the scroll event, so set isScrolling to FALSE
// to set isScrolling to TRUE, we need another scroll event.
gridState[GRIDSTATE_isScrolling] = false;
} else {
EndEdit();
}
int deltaY = ComputeRowDelta(oldFirstRow, newFirstRow);
Rectangle rowsRect = layout.Data;
if (layout.RowHeadersVisible)
rowsRect = Rectangle.Union(rowsRect, layout.RowHeaders);
NativeMethods.RECT scrollArea = NativeMethods.RECT.FromXYWH(rowsRect.X, rowsRect.Y, rowsRect.Width, rowsRect.Height);
SafeNativeMethods.ScrollWindow(new HandleRef(this, Handle), 0, deltaY, ref scrollArea, ref scrollArea);
OnScroll(EventArgs.Empty);
if (wasEditing) {
// invalidate the rowHeader for the
InvalidateRowHeader(currentRow);
}
}
}
/// <devdoc>
/// Scrolls the datagrid right an arbritrary number of columns.
/// </devdoc>
private void ScrollRight(int columns) {
Debug.WriteLineIf(CompModSwitches.DataGridScrolling.TraceVerbose, "DataGridScrolling: ScrollRight, columns = " + columns.ToString(CultureInfo.InvariantCulture));
int newCol = firstVisibleCol + columns;
GridColumnStylesCollection gridColumns = myGridTable.GridColumnStyles;
int newColOffset = 0;
int nGridCols = gridColumns.Count;
int nVisibleCols = 0;
// if we try to scroll past the last totally visible column,
// then the toolTips will dissapear
if (this.myGridTable.IsDefault)
nVisibleCols = nGridCols;
else
for (int i = 0; i < nGridCols; i++)
if (gridColumns[i].PropertyDescriptor != null)
nVisibleCols ++;
if (this.lastTotallyVisibleCol == nVisibleCols - 1 && columns > 0 ||
this.firstVisibleCol == 0 && columns < 0 && negOffset == 0)
return;
newCol = Math.Min(newCol, nGridCols - 1);
for (int i = 0; i < newCol; i++)
// if (gridColumns[i].Visible && gridColumns[i].PropertyDescriptor != null)
if (gridColumns[i].PropertyDescriptor != null)
newColOffset += gridColumns[i].Width;
HorizontalOffset = newColOffset;
}
/// <devdoc>
/// Scrolls a given column into visibility.
/// </devdoc>
private void ScrollToColumn(int targetCol) {
// do not flush the columns to the left
// so, scroll only as many columns as is necessary.
//
int dCols = targetCol - firstVisibleCol;
if (targetCol > lastTotallyVisibleCol && lastTotallyVisibleCol != -1)
dCols = targetCol - lastTotallyVisibleCol;
// if only part of the currentCol is visible
// then we should still scroll
if (dCols != 0 || negOffset != 0)
ScrollRight(dCols);
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.Select"]/*' />
/// <devdoc>
/// Selects a given row
/// </devdoc>
public void Select(int row) {
// SECREVIEW: the method does not check the bounds. We can't fix this and throw an exception because this would be a breaking change
Debug.WriteLineIf(CompModSwitches.DataGridSelection.TraceVerbose, "Selecting row " + row.ToString(CultureInfo.InvariantCulture));
DataGridRow[] localGridRows = DataGridRows;
if (!localGridRows[row].Selected) {
localGridRows[row].Selected = true;
numSelectedRows++;
}
// when selecting a row, hide the edit box
//
EndEdit();
}
// this function will pair the listManager w/ a table from the TableStylesCollection.
// and for each column in the TableStylesCollection will pair them w/ a propertyDescriptor
// from the listManager
//
// prerequisite: the current table is either the default table, or has the same name as the
// list in the listManager.
//
private void PairTableStylesAndGridColumns(CurrencyManager lm, DataGridTableStyle gridTable, bool forceColumnCreation) {
PropertyDescriptorCollection props = lm.GetItemProperties();
GridColumnStylesCollection gridCols = gridTable.GridColumnStyles;
// ]it is possible to have a dataTable w/ an empty string for a name.
if (!gridTable.IsDefault && String.Compare(lm.GetListName(), gridTable.MappingName, true, CultureInfo.InvariantCulture) == 0) {
// we will force column creation only at runtime
if (gridTable.GridColumnStyles.Count == 0 && !DesignMode) {
// we have to create some default columns for each of the propertyDescriptors
//
if (forceColumnCreation)
gridTable.SetGridColumnStylesCollection(lm);
else
gridTable.SetRelationsList(lm);
} else {
// it may the case that the user will have two lists w/ the same name.
// When switching binding between those different lists, we need to invalidate
// the propertyDescriptors from the current gridColumns
//
for (int i = 0; i < gridCols.Count; i ++)
gridCols[i].PropertyDescriptor = null;
// pair the propertyDescriptor from each column to the actual property descriptor
// from the listManager
//
for (int i = 0; i < props.Count; i ++) {
DataGridColumnStyle col = gridCols.MapColumnStyleToPropertyName(props[i].Name);
if (col != null) {
col.PropertyDescriptor = props[i];
}
}
// TableStyle::SetGridColumnStylesCollection will also set the
// relations list in the tableStyle.
gridTable.SetRelationsList(lm);
}
} else {
// we should put an assert, that this is the default Table Style
#if DEBUG
Debug.Assert(gridTable.IsDefault, "if we don't have a match, then the dataGRid should have the default table");
#endif // DEBUG
gridTable.SetGridColumnStylesCollection(lm);
if (gridTable.GridColumnStyles.Count > 0 && gridTable.GridColumnStyles[0].Width == -1) {
#if DEBUG
GridColumnStylesCollection cols = gridTable.GridColumnStyles;
for (int i = 0; i < cols.Count; i++) {
Debug.Assert(cols[i].Width == -1, "if one column's width is not initialized, the same should be happening for the rest of the columns");
}
#endif // DEBUG
InitializeColumnWidths();
}
}
}
/// <devdoc>
/// Sets the current GridTable for the DataGrid.
/// This GridTable is the table which is currently
/// being displayed on the grid.
/// </devdoc>
internal void SetDataGridTable(DataGridTableStyle newTable, bool forceColumnCreation)
{
// we have to listen to the dataGridTable for the propertyChangedEvent
if(this.myGridTable != null) {
// unwire the propertyChanged event
UnWireTableStylePropChanged(myGridTable);
if (myGridTable.IsDefault) {
// reset the propertyDescriptors on the default table.
myGridTable.GridColumnStyles.ResetPropertyDescriptors();
// reset the relationship list from the default table
myGridTable.ResetRelationsList();
}
}
myGridTable = newTable;
WireTableStylePropChanged(myGridTable);
this.layout.RowHeadersVisible = newTable.IsDefault ? this.RowHeadersVisible : newTable.RowHeadersVisible;
// we need to force the grid into the dataGridTableStyle
// this way the controls in the columns will be parented
// consider this scenario: when the user finished InitializeComponent, it added
// a bunch of tables. all of those tables will have the DataGrid property set to this
// grid. however, in InitializeComponent the tables will not have parented the
// edit controls w/ the grid.
//
// the code in DataGridTextBoxColumn already checks to see if the edits are parented
// before parenting them.
//
if (newTable != null)
newTable.DataGrid = this;
// pair the tableStyles and GridColumns
//
if (this.listManager != null)
PairTableStylesAndGridColumns(this.listManager, this.myGridTable, forceColumnCreation);
// reset the relations UI on the newTable
if (newTable != null)
newTable.ResetRelationsUI();
// set the isNavigating to false
gridState[GRIDSTATE_isNavigating] = false;
horizScrollBar.Value = 0;
firstVisibleRow = 0;
currentCol = 0;
// if we add a tableStyle that mapps to the
// current listName, then we should set the currentRow to the
// position in the listManager
if (this.listManager == null)
currentRow = 0;
else
currentRow = this.listManager.Position == -1 ? 0 : listManager.Position;
ResetHorizontalOffset();
negOffset = 0;
ResetUIState();
// check the hierarchy
checkHierarchy = true;
}
/// <devdoc>
/// Scrolls the data area down to make room for the parent rows
/// and lays out the different regions of the DataGrid.
/// </devdoc>
internal void SetParentRowsVisibility(bool visible) {
Rectangle parentRowsRect = layout.ParentRows;
Rectangle underParentRows = layout.Data;
if (layout.RowHeadersVisible) {
underParentRows.X -= isRightToLeft() ? 0 : layout.RowHeaders.Width;
underParentRows.Width += layout.RowHeaders.Width;
}
if (layout.ColumnHeadersVisible) {
underParentRows.Y -= layout.ColumnHeaders.Height;
underParentRows.Height += layout.ColumnHeaders.Height;
}
// hide the Edit Box
EndEdit();
if (visible) {
/*
RECT scrollArea = RECT.FromXYWH(underParentRows.X, underParentRows.Y, underParentRows.Width, underParentRows.Height);
Debug.WriteLineIf(CompModSwitches.DataGridParents.TraceVerbose, "DataGridParents: Making parent rows visible.");
SafeNativeMethods.ScrollWindow(this.Handle, 0, parentRowsRect.Height,
ref scrollArea, ref scrollArea);
*/
layout.ParentRowsVisible = true;
PerformLayout();
Invalidate();
}
else {
// Rectangle scrollArea = Rectangle.Union(layout.ParentRows, underParentRows);
// RECT scrollRECT = RECT.FromXYWH(scrollArea.X, scrollArea.Y, scrollArea.Width, scrollArea.Height);
NativeMethods.RECT scrollRECT = NativeMethods.RECT.FromXYWH(underParentRows.X, underParentRows.Y - layout.ParentRows.Height, underParentRows.Width, underParentRows.Height + layout.ParentRows.Height);
SafeNativeMethods.ScrollWindow(new HandleRef(this, Handle), 0, -parentRowsRect.Height, ref scrollRECT, ref scrollRECT);
// If the vertical scrollbar was visible before and not after
// the ScrollWindow call, then we will not get invalidated
// completely. We need to translate the visual bounds of
// the scrollbar's old location up and invalidate.
//
if (vertScrollBar.Visible) {
Rectangle fixupRect = vertScrollBar.Bounds;
fixupRect.Y -= parentRowsRect.Height;
fixupRect.Height += parentRowsRect.Height;
Invalidate(fixupRect);
}
Debug.WriteLineIf(CompModSwitches.DataGridParents.TraceVerbose, "DataGridParents: Making parent rows invisible.");
layout.ParentRowsVisible = false;
PerformLayout();
}
}
/// <devdoc>
/// Sets whether a row is expanded or not.
/// </devdoc>
private void SetRowExpansionState(int row, bool expanded) {
if (row < -1 || row > DataGridRowsLength - (policy.AllowAdd ? 2:1)) {
throw new ArgumentOutOfRangeException("row");
}
DataGridRow[] localGridRows = DataGridRows;
if (row == -1) {
DataGridRelationshipRow[] expandableRows = GetExpandableRows();
bool repositionEditControl = false;
for (int r = 0; r < expandableRows.Length; ++r) {
if (expandableRows[r].Expanded != expanded)
{
expandableRows[r].Expanded = expanded;
repositionEditControl = true;
}
}
if (repositionEditControl)
{
// we need to reposition the edit control
if (gridState[GRIDSTATE_isNavigating] || gridState[GRIDSTATE_isEditing])
{
ResetSelection();
Edit();
}
}
}
else if (localGridRows[row] is DataGridRelationshipRow) {
DataGridRelationshipRow expandableRow = (DataGridRelationshipRow)localGridRows[row];
if (expandableRow.Expanded != expanded)
{
// we need to reposition the edit control
if (gridState[GRIDSTATE_isNavigating] || gridState[GRIDSTATE_isEditing])
{
ResetSelection();
Edit();
}
expandableRow.Expanded = expanded;
}
}
}
private void ObjectSiteChange(IContainer container, IComponent component, bool site) {
if (site) {
if (component.Site == null) {
container.Add(component);
}
}
else {
if (component.Site != null && component.Site.Container == container) {
container.Remove(component);
}
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.SubObjectsSiteChange"]/*' />
/// <internalonly/>
public void SubObjectsSiteChange(bool site) {
DataGrid dgrid = this;
if (dgrid.DesignMode && dgrid.Site != null) {
IDesignerHost host = (IDesignerHost)dgrid.Site.GetService(typeof(IDesignerHost));
if (host != null) {
DesignerTransaction trans = host.CreateTransaction();
try {
IContainer container = dgrid.Site.Container;
DataGridTableStyle[] tables = new DataGridTableStyle[dgrid.TableStyles.Count];
dgrid.TableStyles.CopyTo(tables, 0);
for (int i = 0; i < tables.Length; i++) {
DataGridTableStyle table = tables[i];
ObjectSiteChange(container, table, site);
DataGridColumnStyle[] columns = new DataGridColumnStyle[table.GridColumnStyles.Count];
table.GridColumnStyles.CopyTo(columns, 0);
for (int j = 0; j < columns.Length; j++) {
DataGridColumnStyle column = columns[j];
ObjectSiteChange(container, column, site);
}
}
}
finally {
trans.Commit();
}
}
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.UnSelect"]/*' />
/// <devdoc>
/// Unselects a given row
/// </devdoc>
public void UnSelect(int row) {
// SECREVIEW: the method does not check the bounds. We can't fix this and throw an exception because this would be a breaking change
Debug.WriteLineIf(CompModSwitches.DataGridSelection.TraceVerbose, "DataGridSelection: Unselecting row " + row.ToString(CultureInfo.InvariantCulture));
DataGridRow[] localGridRows = DataGridRows;
if (localGridRows[row].Selected) {
localGridRows[row].Selected = false;
numSelectedRows--;
}
}
/// <devdoc>
/// Asks the cursor to update.
/// </devdoc>
private void UpdateListManager() {
Debug.WriteLineIf(CompModSwitches.DataGridCursor.TraceVerbose, "DataGridCursor: Requesting EndEdit()");
try {
if (this.listManager != null) {
EndEdit();
this.listManager.EndCurrentEdit();
}
}
catch {
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.GetOutputTextDelimiter"]/*' />
/// <internalonly/>
/// <devdoc>
/// Will return the string that will be used as a delimiter between columns
/// when copying rows contents to the Clipboard.
/// At the moment, return "\t"
/// </devdoc>
protected virtual string GetOutputTextDelimiter() {
return "\t";
}
/// <devdoc>
/// The accessible object class for a DataGrid. The child accessible objects
/// are accessible objects corresponding to the propertygrid entries.
/// </devdoc>
[System.Runtime.InteropServices.ComVisible(true)]
internal class DataGridAccessibleObject : ControlAccessibleObject {
/// <devdoc>
/// Construct a PropertyGridViewAccessibleObject
/// </devdoc>
public DataGridAccessibleObject(DataGrid owner) : base(owner) {
}
internal DataGrid DataGrid {
get {
return (DataGrid)Owner;
}
}
private int ColumnCountPrivate {
get {
return ((DataGrid)Owner).myGridTable.GridColumnStyles.Count;
}
}
private int RowCountPrivate {
get {
return ((DataGrid)Owner).dataGridRows.Length;
}
}
public override string Name {
get {
// Special case: If an explicit name has been set in the AccessibleName property, use that.
// Note: Any non-null value in AccessibleName overrides the default accessible name logic,
// even an empty string (this is the only way to *force* the accessible name to be blank).
string name = Owner.AccessibleName;
if (name != null) {
return name;
}
// Otherwise just return the default label string, minus any mnemonics
return "DataGrid";
}
set {
// If anyone tries to set the accessible name, just cache the value in the control's
// AccessibleName property. This value will then end up overriding the normal accessible
// name logic, until such time as AccessibleName is set back to null.
Owner.AccessibleName = value;
}
}
public override AccessibleRole Role {
get {
AccessibleRole role = Owner.AccessibleRole;
if (role != AccessibleRole.Default) {
return role;
}
return AccessibleRole.Table;
}
}
public override AccessibleObject GetChild(int index) {
DataGrid dataGrid = (DataGrid)Owner;
int cols = ColumnCountPrivate;
int rows = RowCountPrivate;
if (dataGrid.dataGridRows == null) {
dataGrid.CreateDataGridRows();
}
if (index < 1) {
return dataGrid.ParentRowsAccessibleObject;
}
else {
index -= 1;
if (index < cols) {
return dataGrid.myGridTable.GridColumnStyles[index].HeaderAccessibleObject;
}
else {
index -= cols;
if (index < rows) {
Debug.Assert(dataGrid.dataGridRows[index].RowNumber == index, "Row number is wrong!");
return dataGrid.dataGridRows[index].AccessibleObject;
}
else {
index -= rows;
if (dataGrid.horizScrollBar.Visible) {
if (index == 0) {
return dataGrid.horizScrollBar.AccessibilityObject;
}
index --;
}
if (dataGrid.vertScrollBar.Visible) {
if (index == 0) {
return dataGrid.vertScrollBar.AccessibilityObject;
}
index --;
}
int colCount = dataGrid.myGridTable.GridColumnStyles.Count;
int rowCount = dataGrid.dataGridRows.Length;
int currentRow = index / colCount;
int currentCol = index % colCount;
if (currentRow < dataGrid.dataGridRows.Length && currentCol < dataGrid.myGridTable.GridColumnStyles.Count) {
return dataGrid.dataGridRows[currentRow].AccessibleObject.GetChild(currentCol);
}
}
}
}
return null;
}
public override int GetChildCount() {
int n = 1 + ColumnCountPrivate + ((DataGrid)Owner).DataGridRowsLength;
if (DataGrid.horizScrollBar.Visible) {
n++;
}
if (DataGrid.vertScrollBar.Visible) {
n++;
}
n += this.DataGrid.DataGridRows.Length * this.DataGrid.myGridTable.GridColumnStyles.Count;
return n;
}
public override AccessibleObject GetFocused() {
if (DataGrid.Focused) {
return GetSelected();
}
return null;
}
public override AccessibleObject GetSelected() {
if (this.DataGrid.DataGridRows.Length == 0 || this.DataGrid.myGridTable.GridColumnStyles.Count == 0) {
return null;
}
DataGridCell cell = DataGrid.CurrentCell;
return GetChild(1 + ColumnCountPrivate + cell.RowNumber).GetChild(cell.ColumnNumber);
}
public override AccessibleObject HitTest(int x, int y) {
Point client = DataGrid.PointToClient(new Point(x, y));
HitTestInfo hti = DataGrid.HitTest(client.X, client.Y);
switch (hti.Type) {
case HitTestType.RowHeader:
return GetChild(1 + ColumnCountPrivate + hti.Row);
case HitTestType.Cell:
return GetChild(1 + ColumnCountPrivate + hti.Row).GetChild(hti.Column);
case HitTestType.ColumnHeader:
return GetChild(1 + hti.Column);
case HitTestType.ParentRows:
return DataGrid.ParentRowsAccessibleObject;
case HitTestType.None:
case HitTestType.ColumnResize:
case HitTestType.RowResize:
case HitTestType.Caption:
break;
}
return null;
}
[SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
public override AccessibleObject Navigate(AccessibleNavigation navdir) {
// We're only handling FirstChild and LastChild here
if (GetChildCount() > 0) {
switch(navdir) {
case AccessibleNavigation.FirstChild:
return GetChild(0);
case AccessibleNavigation.LastChild:
return GetChild(GetChildCount() - 1);
}
}
return null; // Perform default behavior
}
}
// <summary>
// This simple data structure holds all of the layout information
// for the DataGrid.
// </summary>
internal class LayoutData {
internal bool dirty = true;
// region inside the Control's borders.
public Rectangle Inside = Rectangle.Empty;
public Rectangle RowHeaders = Rectangle.Empty;
public Rectangle TopLeftHeader = Rectangle.Empty;
public Rectangle ColumnHeaders = Rectangle.Empty;
public Rectangle Data = Rectangle.Empty;
public Rectangle Caption = Rectangle.Empty;
public Rectangle ParentRows = Rectangle.Empty;
public Rectangle ResizeBoxRect = Rectangle.Empty;
public bool ColumnHeadersVisible;
public bool RowHeadersVisible;
public bool CaptionVisible;
public bool ParentRowsVisible;
// used for resizing.
public Rectangle ClientRectangle = Rectangle.Empty;
public LayoutData() {
}
public LayoutData(LayoutData src) {
GrabLayout(src);
}
private void GrabLayout(LayoutData src) {
this.Inside = src.Inside;
this.TopLeftHeader = src.TopLeftHeader;
this.ColumnHeaders = src.ColumnHeaders;
this.RowHeaders = src.RowHeaders;
this.Data = src.Data;
this.Caption = src.Caption;
this.ParentRows = src.ParentRows;
this.ResizeBoxRect = src.ResizeBoxRect;
this.ColumnHeadersVisible = src.ColumnHeadersVisible;
this.RowHeadersVisible = src.RowHeadersVisible;
this.CaptionVisible = src.CaptionVisible;
this.ParentRowsVisible = src.ParentRowsVisible;
this.ClientRectangle = src.ClientRectangle;
}
public override string ToString() {
StringBuilder sb = new StringBuilder(200);
sb.Append(base.ToString());
sb.Append(" { \n");
sb.Append("Inside = ");
sb.Append(Inside.ToString());
sb.Append('\n');
sb.Append("TopLeftHeader = ");
sb.Append(TopLeftHeader.ToString());
sb.Append('\n');
sb.Append("ColumnHeaders = ");
sb.Append(ColumnHeaders.ToString());
sb.Append('\n');
sb.Append("RowHeaders = ");
sb.Append(RowHeaders.ToString());
sb.Append('\n');
sb.Append("Data = ");
sb.Append(Data.ToString());
sb.Append('\n');
sb.Append("Caption = ");
sb.Append(Caption.ToString());
sb.Append('\n');
sb.Append("ParentRows = ");
sb.Append(ParentRows.ToString());
sb.Append('\n');
sb.Append("ResizeBoxRect = ");
sb.Append(ResizeBoxRect.ToString());
sb.Append('\n');
sb.Append("ColumnHeadersVisible = ");
sb.Append(ColumnHeadersVisible.ToString());
sb.Append('\n');
sb.Append("RowHeadersVisible = ");
sb.Append(RowHeadersVisible.ToString());
sb.Append('\n');
sb.Append("CaptionVisible = ");
sb.Append(CaptionVisible.ToString());
sb.Append('\n');
sb.Append("ParentRowsVisible = ");
sb.Append(ParentRowsVisible.ToString());
sb.Append('\n');
sb.Append("ClientRectangle = ");
sb.Append(ClientRectangle.ToString());
sb.Append(" } ");
return sb.ToString();
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.HitTestInfo"]/*' />
/// <devdoc>
/// <para>Contains information
/// about the part of the <see cref='System.Windows.Forms.DataGrid'/> control the user
/// has clicked. This class cannot be inherited.</para>
/// </devdoc>
public sealed class HitTestInfo {
internal HitTestType type = HitTestType.None;
internal int row;
internal int col;
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.HitTestInfo.Nowhere"]/*' />
/// <devdoc>
/// <para>Allows the <see cref='System.Windows.Forms.DataGrid.HitTestInfo'/> object to inform you the
/// extent of the grid.</para>
/// </devdoc>
[
SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes") // DataGrid.HitTestInfo type is
// actually immutable.
]
public static readonly HitTestInfo Nowhere = new HitTestInfo();
internal HitTestInfo() {
type = (HitTestType)0;
row = col = -1;
}
internal HitTestInfo(HitTestType type) {
this.type = type;
row = col = -1;
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.HitTestInfo.Column"]/*' />
/// <devdoc>
/// <para>Gets the number of the clicked column.</para>
/// </devdoc>
public int Column {
get {
return col;
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.HitTestInfo.Row"]/*' />
/// <devdoc>
/// <para>Gets the
/// number of the clicked row.</para>
/// </devdoc>
public int Row {
get {
return row;
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.HitTestInfo.Type"]/*' />
/// <devdoc>
/// <para>Gets the part of the <see cref='System.Windows.Forms.DataGrid'/> control, other than the row or column, that was
/// clicked.</para>
/// </devdoc>
public HitTestType Type {
get {
return type;
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.HitTestInfo.Equals"]/*' />
/// <devdoc>
/// <para>Indicates whether two objects are identical.</para>
/// </devdoc>
public override bool Equals(object value) {
if (value is HitTestInfo) {
HitTestInfo ci =(HitTestInfo) value;
return(type == ci.type &&
row == ci.row &&
col == ci.col);
}
return false;
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.HitTestInfo.GetHashCode"]/*' />
/// <devdoc>
/// <para>Gets the hash code for the <see cref='System.Windows.Forms.DataGrid.HitTestInfo'/> instance.</para>
/// </devdoc>
public override int GetHashCode() {
return(int)type +(row << 8) +(col << 16);
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.HitTestInfo.ToString"]/*' />
/// <devdoc>
/// <para>Gets the type, row number, and column number.</para>
/// </devdoc>
public override string ToString() {
return "{ " + ((type).ToString()) + "," + row.ToString(CultureInfo.InvariantCulture) + "," + col.ToString(CultureInfo.InvariantCulture) + "}";
}
}
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.HitTestType"]/*' />
/// <devdoc>
/// <para>Specifies the part of the <see cref='System.Windows.Forms.DataGrid'/> control the user has clicked.</para>
/// </devdoc>
[Flags]
public enum HitTestType {
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.HitTestType.None"]/*' />
None = 0x00000000,
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.HitTestType.Cell"]/*' />
Cell = 0x00000001,
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.HitTestType.ColumnHeader"]/*' />
ColumnHeader = 0x00000002,
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.HitTestType.RowHeader"]/*' />
RowHeader = 0x00000004,
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.HitTestType.ColumnResize"]/*' />
ColumnResize = 0x00000008,
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.HitTestType.RowResize"]/*' />
RowResize = 0x00000010,
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.HitTestType.Caption"]/*' />
Caption = 0x00000020,
/// <include file='doc\DataGrid.uex' path='docs/doc[@for="DataGrid.HitTestType.ParentRows"]/*' />
ParentRows = 0x00000040
}
/// <devdoc>
/// Holds policy information for what the grid can and cannot do.
/// </devdoc>
private class Policy {
private bool allowAdd = true;
private bool allowEdit = true;
private bool allowRemove = true;
public Policy() {
}
public bool AllowAdd {
get {
return allowAdd;
}
set {
if (allowAdd != value) {
allowAdd = value;
}
}
}
public bool AllowEdit {
get {
return allowEdit;
}
set {
if (allowEdit != value) {
allowEdit = value;
}
}
}
public bool AllowRemove {
get {
return allowRemove;
}
set {
if (allowRemove != value) {
allowRemove = value;
}
}
}
// returns true if the UI needs to be updated (here because addnew has changed)
public bool UpdatePolicy(CurrencyManager listManager, bool gridReadOnly)
{
bool change = false;
// only IBindingList can have an AddNewRow
IBindingList bl = listManager == null ? null : listManager.List as IBindingList;
if (listManager == null) {
if (!allowAdd)
change = true;
allowAdd = allowEdit = allowRemove = true;
}
else {
if (AllowAdd != listManager.AllowAdd && !gridReadOnly)
change = true;
AllowAdd= listManager.AllowAdd && !gridReadOnly && bl != null && bl.SupportsChangeNotification;
AllowEdit= listManager.AllowEdit && !gridReadOnly;
AllowRemove = listManager.AllowRemove && !gridReadOnly && bl != null && bl.SupportsChangeNotification; // bug 86061
}
return change;
}
}
//
// Given the x coordinate and the Width of rectangle R1 inside rectangle rect,
// this function returns the x coordinate of the rectangle that
// corresponds to the Bi-Di transformation
//
private int MirrorRectangle(Rectangle R1, Rectangle rect, bool rightToLeft)
{
if (rightToLeft)
return rect.Right + rect.X - R1.Right;
else
return R1.X;
}
//
// Given the x coordinate of a point inside rectangle rect,
// this function returns the x coordinate of the point that
// corresponds to the Bi-Di transformation
//
private int MirrorPoint(int x, Rectangle rect, bool rightToLeft)
{
if (rightToLeft)
return rect.Right + rect.X - x;
else
return x;
}
// This function will return true if the RightToLeft property of the dataGrid is
// set to YES
private bool isRightToLeft()
{
return ( RightToLeft == RightToLeft.Yes);
}
}
}
|