File: winforms\Managed\System\WinForms\DataGridRow.cs
Project: ndp\fx\src\System.Windows.Forms.csproj (System.Windows.Forms)
//------------------------------------------------------------------------------
// <copyright file="DataGridRow.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------
 
namespace System.Windows.Forms {
    using System.Runtime.Remoting;
    using System.Runtime.Versioning;
    using System.Diagnostics;
    using System.Diagnostics.CodeAnalysis;
 
    using System;
    using System.Runtime.InteropServices;
    
    using System.Windows.Forms;
    using System.ComponentModel;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System.Security.Permissions;
    using Microsoft.Win32;
    using System.Collections;
 
    /// <include file='doc\DataGridRow.uex' path='docs/doc[@for="DataGridRow"]/*' />
    /// <devdoc>
    ///    <para>Encapsulates the painting logic for a new row added to a 
    ///    <see cref='System.Windows.Forms.DataGrid'/> 
    ///    control.</para>
    /// </devdoc>
    internal abstract class DataGridRow : MarshalByRefObject {
        internal protected int       number;             // row number
        private bool      selected;
        private int       height;
        // protected DataRow   dataRow;
        private IntPtr       tooltipID = new IntPtr(-1);
        private string    tooltip = String.Empty;
        AccessibleObject  accessibleObject;
 
        // for accessibility...
        //
        // internal DataGrid  dataGrid;
 
        // will need this for the painting information ( row header color )
        // 
        protected DataGridTableStyle dgTable;
 
        // we will be mapping only the black color to 
        // the HeaderForeColor
        //
        private static ColorMap[] colorMap = new ColorMap[] {new ColorMap()};
 
        // bitmaps
        //
        private static Bitmap rightArrow = null;
        private static Bitmap leftArrow = null;
        private static Bitmap errorBmp = null;
        private static Bitmap pencilBmp = null;
        private static Bitmap starBmp = null;
        protected const int xOffset = 3;
        protected const int yOffset = 2;
 
 
        /// <include file='doc\DataGridRow.uex' path='docs/doc[@for="DataGridRow.DataGridRow"]/*' />
        /// <devdoc>
        /// <para>Initializes a new instance of a <see cref='System.Windows.Forms.DataGridRow'/> . </para>
        /// </devdoc>
        [
            SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")  // This class and its derived classes are internal.
                                                                                                    // So this is not a security back door.
        ]
        public DataGridRow(DataGrid dataGrid, DataGridTableStyle dgTable, int rowNumber) {
            if (dataGrid == null || dgTable.DataGrid == null)
                throw new ArgumentNullException("dataGrid");
            if (rowNumber < 0)
                throw new ArgumentException(SR.GetString(SR.DataGridRowRowNumber), "rowNumber");
            // this.dataGrid = dataGrid;
            this.number = rowNumber;
 
            // map the black color in the pictures to the DataGrid's HeaderForeColor
            //
            colorMap[0].OldColor = Color.Black;
            colorMap[0].NewColor = dgTable.HeaderForeColor;
 
            this.dgTable = dgTable;
            height = MinimumRowHeight(dgTable);
        }
 
        public AccessibleObject AccessibleObject {
            get {
                if (accessibleObject == null) {
                    accessibleObject = CreateAccessibleObject();
                }
                return accessibleObject;
            }
        }
 
        protected virtual AccessibleObject CreateAccessibleObject() {
            return new DataGridRowAccessibleObject(this);
        }
 
        internal protected virtual int MinimumRowHeight(DataGridTableStyle dgTable) {
            return MinimumRowHeight(dgTable.GridColumnStyles);
        }
 
        internal protected virtual int MinimumRowHeight(GridColumnStylesCollection columns) {
            int h = dgTable.IsDefault ? this.DataGrid.PreferredRowHeight : dgTable.PreferredRowHeight;
 
            try {
                if (this.dgTable.DataGrid.DataSource != null) {
                    int nCols = columns.Count;
                    for (int i = 0; i < nCols; ++i) {
                        // if (columns[i].Visible && columns[i].PropertyDescriptor != null)
                        if (columns[i].PropertyDescriptor != null)
                            h = Math.Max(h, columns[i].GetMinimumHeight());
                    }
                }
            }
            catch {
            }
            return h;
        }
 
        // =------------------------------------------------------------------
        // =        Properties
        // =------------------------------------------------------------------
 
        /// <include file='doc\DataGridRow.uex' path='docs/doc[@for="DataGridRow.DataGrid"]/*' />
        /// <devdoc>
        /// <para>Gets the <see cref='System.Windows.Forms.DataGrid'/> control the row belongs to.</para>
        /// </devdoc>
        public DataGrid DataGrid {
            get {
                return this.dgTable.DataGrid;
            }
        }
 
        internal DataGridTableStyle DataGridTableStyle {
            get {
                return this.dgTable;
            }
            set {
                dgTable = value;
            }
        }
 
        /*
        public DataGridTable DataGridTable {
            get {
                return dgTable;
            }
        }
        */
 
        /*
        public DataRow DataRow {
            get {
                return dataRow;
            }
        }
        */
 
        /// <include file='doc\DataGridRow.uex' path='docs/doc[@for="DataGridRow.Height"]/*' />
        /// <devdoc>
        ///    <para>Gets or sets the height of the row.</para>
        /// </devdoc>
        public virtual int Height {
            get {
                return height;
            }
            set {
                // the height of the row should be at least 0.
                // this way, if the row has a relationship list and the user resizes the row such that
                // the new height does not accomodate the height of the relationship list
                // the row will at least show the relationship list ( and not paint on the portion of the row above this one )
                height = Math.Max(0, value);
                // when we resize the row, or when we set the PreferredRowHeigth on the
                // DataGridTableStyle, we change the height of the Row, which will cause to invalidate,
                // then the grid itself will do another invalidate call.
                this.dgTable.DataGrid.OnRowHeightChanged(this);
            }
        }
 
        /// <include file='doc\DataGridRow.uex' path='docs/doc[@for="DataGridRow.RowNumber"]/*' />
        /// <devdoc>
        ///    <para>Gets the row's number.</para>
        /// </devdoc>
        public int RowNumber {
            get {
                return this.number;
            }
        }
 
        /// <include file='doc\DataGridRow.uex' path='docs/doc[@for="DataGridRow.Selected"]/*' />
        /// <devdoc>
        ///    <para>Gets or sets a value indicating whether the row is selected.</para>
        /// </devdoc>
        public virtual bool Selected {
            get {
                return selected;
            }
            set {
                selected = value;
                InvalidateRow();
            }
        }
 
        // =------------------------------------------------------------------
        // =        Methods
        // =------------------------------------------------------------------
 
        /// <include file='doc\DataGridRow.uex' path='docs/doc[@for="DataGridRow.GetBitmap"]/*' />
        /// <devdoc>
        ///    <para>Gets the bitmap associated with the row.</para>
        /// </devdoc>
        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)]
        protected Bitmap GetBitmap(string bitmapName) {
            Bitmap b = null;
            try {
                b = new Bitmap(typeof(DataGridCaption), bitmapName);
                b.MakeTransparent();
            }
            catch (Exception e) {
                Debug.Fail("Failed to load bitmap: " + bitmapName, e.ToString());
                throw e;
            }
            return b;
        }
 
        /// <include file='doc\DataGridRow.uex' path='docs/doc[@for="DataGridRow.GetCellBounds"]/*' />
        /// <devdoc>
        /// <para>When overridden in a derived class, gets the <see cref='System.Drawing.Rectangle'/> 
        /// where a cell's contents gets painted.</para>
        /// </devdoc>
        public virtual Rectangle GetCellBounds(int col) {
            int firstVisibleCol = this.dgTable.DataGrid.FirstVisibleColumn;
            int cx = 0;
            Rectangle cellBounds = new Rectangle();
            GridColumnStylesCollection columns = this.dgTable.GridColumnStyles;
            if (columns != null) {
                for (int i = firstVisibleCol; i < col; i++)
                    if (columns[i].PropertyDescriptor != null)
                        cx += columns[i].Width;
 
                int borderWidth = this.dgTable.GridLineWidth;
                cellBounds = new Rectangle(cx,
                                     0,
                                     columns[col].Width - borderWidth,
                                     Height - borderWidth);
            }
            return cellBounds;
        }
 
        /// <include file='doc\DataGridRow.uex' path='docs/doc[@for="DataGridRow.GetNonScrollableArea"]/*' />
        /// <devdoc>
        /// <para>When overridden in a derived class, gets the <see cref='System.Drawing.Rectangle'/> of the non-scrollable area of 
        ///    the row.</para>
        /// </devdoc>
        public virtual Rectangle GetNonScrollableArea() {
            return Rectangle.Empty;
        }
 
        /// <include file='doc\DataGridRow.uex' path='docs/doc[@for="DataGridRow.GetStarBitmap"]/*' />
        /// <devdoc>
        ///    <para>Gets or sets the bitmap displayed in the row header of a new row.</para>
        /// </devdoc>
        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)]
        protected Bitmap GetStarBitmap() {
            if (starBmp == null)
                starBmp = GetBitmap("DataGridRow.star.bmp");
            return starBmp;
        }
 
        /// <include file='doc\DataGridRow.uex' path='docs/doc[@for="DataGridRow.GetPencilBitmap"]/*' />
        /// <devdoc>
        ///    <para>Gets or sets the bitmap displayed in the row header that indicates a row can 
        ///       be edited.</para>
        /// </devdoc>
        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)]
        protected Bitmap GetPencilBitmap() {
            if (pencilBmp == null)
                pencilBmp = GetBitmap("DataGridRow.pencil.bmp");
            return pencilBmp;
        }
 
        /// <include file='doc\DataGridRow.uex' path='docs/doc[@for="DataGridRow.GetErrorBitmap"]/*' />
        /// <devdoc>
        ///    <para>Gets or sets the bitmap displayed on a row with an error.</para>
        /// </devdoc>
        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)]
        protected Bitmap GetErrorBitmap() {
            if (errorBmp == null)
                errorBmp = GetBitmap("DataGridRow.error.bmp");
            errorBmp.MakeTransparent();
            return errorBmp;
        }
 
        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)]
        protected Bitmap GetLeftArrowBitmap() {
            if (leftArrow == null)
                leftArrow = GetBitmap("DataGridRow.left.bmp");
            return leftArrow;
        }
 
        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)]
        protected Bitmap GetRightArrowBitmap() {
            if (rightArrow == null)
                rightArrow = GetBitmap("DataGridRow.right.bmp");
            return rightArrow;
        }
 
        public virtual void InvalidateRow() {
            this.dgTable.DataGrid.InvalidateRow(number);
        }
 
        public virtual void InvalidateRowRect(Rectangle r) {
            this.dgTable.DataGrid.InvalidateRowRect(number, r);
        }
 
        /// <include file='doc\DataGridRow.uex' path='docs/doc[@for="DataGridRow.OnEdit"]/*' />
        /// <devdoc>
        ///    <para>When overridden in a derived class, notifies the grid that an edit will 
        ///       occur.</para>
        /// </devdoc>
        public virtual void OnEdit() {
        }
 
        /// <include file='doc\DataGridRow.uex' path='docs/doc[@for="DataGridRow.OnKeyPress"]/*' />
        /// <devdoc>
        /// <para>When overridden in a derived class, called by the <see cref='System.Windows.Forms.DataGrid'/> control when a key press occurs on a row with focus.</para>
        /// </devdoc>
        public virtual bool OnKeyPress(Keys keyData) {
            int currentColIndex = this.dgTable.DataGrid.CurrentCell.ColumnNumber;
            GridColumnStylesCollection columns = this.dgTable.GridColumnStyles;
            if (columns != null && currentColIndex >= 0 && currentColIndex < columns.Count) {
                DataGridColumnStyle currentColumn = columns[currentColIndex];
                if (currentColumn.KeyPress(this.RowNumber, keyData)) {
                    return true;
                }
            }
            return false;
        }
 
        /// <include file='doc\DataGridRow.uex' path='docs/doc[@for="DataGridRow.OnMouseDown"]/*' />
        /// <devdoc>
        /// <para> Called by the <see cref='System.Windows.Forms.DataGrid'/> when a click occurs in the row's client area 
        ///    specifed by the x and y coordinates and the specified <see cref='System.Drawing.Rectangle'/>
        ///    .</para>
        /// </devdoc>
        public virtual bool OnMouseDown(int x, int y, Rectangle rowHeaders)
        {
            return OnMouseDown(x,y,rowHeaders, false);
        }
 
        /// <include file='doc\DataGridRow.uex' path='docs/doc[@for="DataGridRow.OnMouseDown1"]/*' />
        /// <devdoc>
        /// <para>When overridden in a derived class, is called by the <see cref='System.Windows.Forms.DataGrid'/> when a click occurs 
        ///    in the row's
        ///    client area, specified by x and y coordinates.</para>
        /// </devdoc>
        public virtual bool OnMouseDown(int x, int y, Rectangle rowHeaders, bool alignToRight) {
            // if we call base.OnMouseDown, then the row could not use this 
            // mouse click at all. in that case LoseChildFocus, so the edit control 
            // will become visible
            LoseChildFocus(rowHeaders, alignToRight);
 
            // we did not use this click at all.
            return false;
        }
 
        /// <include file='doc\DataGridRow.uex' path='docs/doc[@for="DataGridRow.OnMouseMove"]/*' />
        /// <devdoc>
        /// </devdoc>
        public virtual bool OnMouseMove(int x, int y, Rectangle rowHeaders) {
            return false;
        }
        
        /// <include file='doc\DataGridRow.uex' path='docs/doc[@for="DataGridRow.OnMouseMove1"]/*' />
        /// <devdoc>
        /// <para>When overridden in a derived class, is called by the <see cref='System.Windows.Forms.DataGrid'/> when 
        ///    the mouse moves within the row's client area.</para>
        /// </devdoc>
        public virtual bool OnMouseMove(int x, int y, Rectangle rowHeaders, bool alignToRight) {
            return false;
        }
 
        /// <include file='doc\DataGridRow.uex' path='docs/doc[@for="DataGridRow.OnMouseLeft"]/*' />
        /// <devdoc>
        /// </devdoc>
        public virtual void OnMouseLeft(Rectangle rowHeaders, bool alignToRight) {
        }
 
        public virtual void OnMouseLeft() {
        }
 
        /// <include file='doc\DataGridRow.uex' path='docs/doc[@for="DataGridRow.OnRowEnter"]/*' />
        /// <devdoc>
        ///    <para>When overridden in a derived class, causes the RowEnter event to occur.</para>
        /// </devdoc>
        public virtual void OnRowEnter() {}
        public virtual void OnRowLeave() {}
 
        // processes the Tab Key
        // returns TRUE if the TAB key is processed
        internal abstract bool ProcessTabKey(Keys keyData, Rectangle rowHeaders, bool alignToRight);
 
        // tells the dataGridRow that it lost the focus
        internal abstract void LoseChildFocus(Rectangle rowHeaders, bool alignToRight);
 
        /// <include file='doc\DataGridRow.uex' path='docs/doc[@for="DataGridRow.Paint"]/*' />
        /// <devdoc>
        ///      Paints the row.
        /// </devdoc>
        public abstract int Paint(Graphics g,
                                 Rectangle dataBounds,
                                 Rectangle rowBounds,
                                 int firstVisibleColumn,
                                 int numVisibleColumns);
 
        public abstract int Paint(Graphics g,
                                  Rectangle dataBounds,
                                  Rectangle rowBounds,
                                  int firstVisibleColumn,
                                  int numVisibleColumns,
                                  bool alignToRight);
 
        /// <include file='doc\DataGridRow.uex' path='docs/doc[@for="DataGridRow.PaintBottomBorder"]/*' />
        /// <devdoc>
        ///      Draws a border on the bottom DataGrid.GridLineWidth pixels
        ///      of the bounding rectangle passed in.
        /// </devdoc>
        protected virtual void PaintBottomBorder(Graphics g, Rectangle bounds, int dataWidth)
        {
            PaintBottomBorder(g, bounds, dataWidth, this.dgTable.GridLineWidth, false);
        }
 
        protected virtual void PaintBottomBorder(Graphics g, Rectangle bounds, int dataWidth, int borderWidth, bool alignToRight) {
            // paint bottom border
            Rectangle bottomBorder = new Rectangle(alignToRight ? bounds.Right - dataWidth : bounds.X,
                                                   bounds.Bottom - borderWidth,
                                                   dataWidth,
                                                   borderWidth);
 
            g.FillRectangle(this.dgTable.IsDefault ? this.DataGrid.GridLineBrush : this.dgTable.GridLineBrush, bottomBorder);
 
            // paint any exposed region to the right
            if (dataWidth < bounds.Width) {
                g.FillRectangle(this.dgTable.DataGrid.BackgroundBrush,
                                alignToRight ? bounds.X: bottomBorder.Right,
                                bottomBorder.Y,
                                bounds.Width - bottomBorder.Width,
                                borderWidth);
            }
        }
 
        /// <include file='doc\DataGridRow.uex' path='docs/doc[@for="DataGridRow.PaintData"]/*' />
        /// <devdoc>
        ///      Paints the row.
        /// </devdoc>
        public virtual int PaintData(Graphics g,
                                     Rectangle bounds,
                                     int firstVisibleColumn,
                                     int columnCount)
        {
            return PaintData(g, bounds, firstVisibleColumn, columnCount, false);
        }
 
        public virtual int PaintData(Graphics g,
                                     Rectangle bounds,
                                     int firstVisibleColumn,
                                     int columnCount,
                                     bool alignToRight) {
            Debug.WriteLineIf(CompModSwitches.DGRowPaint.TraceVerbose, "Painting DataGridAddNewRow: bounds = " + bounds.ToString());
 
            Rectangle cellBounds   = bounds;
            int       bWidth       = this.dgTable.IsDefault ? this.DataGrid.GridLineWidth : this.dgTable.GridLineWidth;
            int       cx           = 0;
 
            DataGridCell current = this.dgTable.DataGrid.CurrentCell;
 
            GridColumnStylesCollection columns = dgTable.GridColumnStyles;
            int nCols = columns.Count;
            for (int col = firstVisibleColumn; col < nCols; ++col) {
                if (cx > bounds.Width)
                    break;
 
                // if (!columns[col].Visible || columns[col].PropertyDescriptor == null)
                if (columns[col].PropertyDescriptor == null || columns[col].Width <= 0)
                    continue;
 
                cellBounds.Width = columns[col].Width - bWidth;
 
                if (alignToRight)
                    cellBounds.X = bounds.Right - cx - cellBounds.Width;
                else
                    cellBounds.X = bounds.X + cx;
 
                // Paint the data with the the DataGridColumn
                Brush backBr = BackBrushForDataPaint(ref current, columns[col], col);
                Brush foreBrush = ForeBrushForDataPaint(ref current, columns[col], col);
 
                PaintCellContents(g,
                                  cellBounds,
                                  columns[col],
                                  backBr,
                                  foreBrush,
                                  alignToRight);
 
                // Paint the border to the right of each cell
                if (bWidth > 0) {
                    g.FillRectangle(this.dgTable.IsDefault ? this.DataGrid.GridLineBrush : this.dgTable.GridLineBrush,
                                    alignToRight ? cellBounds.X - bWidth : cellBounds.Right,
                                    cellBounds.Y,
                                    bWidth,
                                    cellBounds.Height);
                }
                cx += cellBounds.Width + bWidth;
            }
 
            // Paint any exposed area to the right ( or left ) of the data cell area
            if (cx < bounds.Width) {
                g.FillRectangle(this.dgTable.DataGrid.BackgroundBrush,
                                alignToRight ? bounds.X : bounds.X + cx,
                                bounds.Y,
                                bounds.Width - cx,
                                bounds.Height);
            }
            return cx;
        }
 
        protected virtual void PaintCellContents(Graphics g, Rectangle cellBounds, DataGridColumnStyle column,
                                                 Brush backBr, Brush foreBrush)
        {
            PaintCellContents(g, cellBounds, column, backBr, foreBrush, false);
        }
 
        protected virtual void PaintCellContents(Graphics g, Rectangle cellBounds, DataGridColumnStyle column,
                                                 Brush backBr, Brush foreBrush, bool alignToRight) {
            g.FillRectangle(backBr, cellBounds);
        }
 
        //
        // This function will do the following: if paintIcon is set to true, then
        // will draw the image on the RowHeader. if paintIcon is set to false, 
        // then this function will fill the rectangle on which otherwise will
        // have been drawn the image
        //
        // will return the rectangle that includes the Icon
        // 
        protected Rectangle PaintIcon(Graphics g, Rectangle visualBounds, bool paintIcon, bool alignToRight, Bitmap bmp) {
            return PaintIcon(g, visualBounds, paintIcon, alignToRight, bmp, 
                             this.dgTable.IsDefault ? this.DataGrid.HeaderBackBrush : this.dgTable.HeaderBackBrush);
        }
        protected Rectangle PaintIcon(Graphics g, Rectangle visualBounds, bool paintIcon, bool alignToRight, Bitmap bmp, Brush backBrush) {
            Size bmpSize = bmp.Size;
            Rectangle bmpRect = new Rectangle(alignToRight ? visualBounds.Right - xOffset - bmpSize.Width : visualBounds.X + xOffset,
                                              visualBounds.Y + yOffset,
                                              bmpSize.Width,
                                              bmpSize.Height);
            g.FillRectangle(backBrush, visualBounds);
            if (paintIcon)
            {
                colorMap[0].NewColor = this.dgTable.IsDefault ? this.DataGrid.HeaderForeColor : this.dgTable.HeaderForeColor;
                colorMap[0].OldColor = Color.Black;
                ImageAttributes attr = new ImageAttributes();
                attr.SetRemapTable(colorMap, ColorAdjustType.Bitmap);
                g.DrawImage(bmp, bmpRect, 0, 0, bmpRect.Width, bmpRect.Height,GraphicsUnit.Pixel, attr);
                // g.DrawImage(bmp, bmpRect);
                attr.Dispose();
            }
 
            return bmpRect;
        }
 
        // assume that the row is not aligned to right, and that the row is not dirty
        public virtual void PaintHeader(Graphics g, Rectangle visualBounds) {
            PaintHeader(g, visualBounds, false);
        }
 
        // assume that the row is not dirty
        public virtual void PaintHeader(Graphics g, Rectangle visualBounds, bool alignToRight) {
            PaintHeader(g,visualBounds, alignToRight, false);
        }
 
        public virtual void PaintHeader(Graphics g, Rectangle visualBounds, bool alignToRight, bool rowIsDirty) {
            Rectangle bounds = visualBounds;
 
            // paint the first part of the row header: the Arror or Pencil/Star
            Bitmap bmp;
            if (this is DataGridAddNewRow)
            {
                bmp = GetStarBitmap();
                lock (bmp) {
                    bounds.X += PaintIcon(g, bounds, true, alignToRight, bmp).Width + xOffset;
                }
                return;
            }
            else if (rowIsDirty)
            {
                bmp = GetPencilBitmap();
                lock (bmp) {
                    bounds.X += PaintIcon(g, bounds, RowNumber == this.DataGrid.CurrentCell.RowNumber, alignToRight, bmp).Width + xOffset;
                }
            }
            else
            {
                bmp = alignToRight ? GetLeftArrowBitmap() : GetRightArrowBitmap();
                lock (bmp) {
                    bounds.X += PaintIcon(g, bounds, RowNumber == this.DataGrid.CurrentCell.RowNumber, alignToRight, bmp).Width + xOffset;
                }
            }
 
            // Paint the error icon
            //
            object errorInfo = DataGrid.ListManager[this.number];
            if (!(errorInfo is IDataErrorInfo))
                return;
 
            string errString = ((IDataErrorInfo) errorInfo).Error;
            if (errString == null)
                errString = String.Empty;
 
            if (tooltip != errString) {
                if (!String.IsNullOrEmpty(tooltip)) {
                    DataGrid.ToolTipProvider.RemoveToolTip(tooltipID);
                    tooltip = String.Empty;
                    tooltipID = new IntPtr(-1);
                }
            }
 
            if (String.IsNullOrEmpty(errString))
                return;
 
            // we now have an error string: paint the errorIcon and add the tooltip
            Rectangle errRect;
            bmp = GetErrorBitmap();
            lock (bmp) {
                errRect = PaintIcon(g, bounds, true, alignToRight, bmp);
            }
            bounds.X += errRect.Width + xOffset;
 
            tooltip = errString;
            tooltipID = (IntPtr)((int)DataGrid.ToolTipId++);
            DataGrid.ToolTipProvider.AddToolTip(tooltip, tooltipID, errRect);
        }
 
        protected Brush GetBackBrush() {
            Brush br = this.dgTable.IsDefault ? DataGrid.BackBrush : this.dgTable.BackBrush;
            if (DataGrid.LedgerStyle && (RowNumber % 2 == 1)) {
                br = this.dgTable.IsDefault ? this.DataGrid.AlternatingBackBrush : this.dgTable.AlternatingBackBrush;
            }
            return br;
        }
 
        /// <include file='doc\DataGridRow.uex' path='docs/doc[@for="DataGridRow.BackBrushForDataPaint"]/*' />
        /// <devdoc>
        ///      Returns the BackColor and TextColor  that the Graphics object should use
        ///      for the appropriate values for a given row and column when painting the data.
        ///
        /// </devdoc>
        protected Brush BackBrushForDataPaint(ref DataGridCell current, DataGridColumnStyle gridColumn, int column) {
            Brush backBr = this.GetBackBrush();
 
            if (Selected) {
                backBr = this.dgTable.IsDefault ? this.DataGrid.SelectionBackBrush : this.dgTable.SelectionBackBrush;
            }            
            /*
            if (RowNumber == current.RowNumber && column == current.ColumnNumber) {
                backBr = grid.CurrentCellBackBrush;
            }
            */
            return backBr;
        }
 
        protected Brush ForeBrushForDataPaint(ref DataGridCell current, DataGridColumnStyle gridColumn, int column) {
            // Brush foreBrush = gridColumn.ForeBrush;
            Brush foreBrush = this.dgTable.IsDefault ? this.DataGrid.ForeBrush : this.dgTable.ForeBrush;
 
            if (Selected) {
                foreBrush = this.dgTable.IsDefault ? this.DataGrid.SelectionForeBrush : this.dgTable.SelectionForeBrush;
            }
            /*
            if (RowNumber == current.RowNumber && column == current.ColumnNumber) {
                foreColor = grid.CurrentCellForeColor;
            }
            */
            return foreBrush;
        }
 
        [ComVisible(true)]
        protected class DataGridRowAccessibleObject : AccessibleObject {
            ArrayList cells;
 
            DataGridRow owner = null;
 
            internal static string CellToDisplayString(DataGrid grid, int row, int column) {
                if (column < grid.myGridTable.GridColumnStyles.Count) {
                    return grid.myGridTable.GridColumnStyles[column].PropertyDescriptor.Converter.ConvertToString(grid[row, column]);
                }
                else {
                    return "";
                }
            }
 
            internal static object DisplayStringToCell(DataGrid grid, int row, int column, string value) {
                if (column < grid.myGridTable.GridColumnStyles.Count) {
                    return grid.myGridTable.GridColumnStyles[column].PropertyDescriptor.Converter.ConvertFromString(value);
                }
                // ignore...
                //
                return null;
            }
 
            [
                SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")  // This class and its derived classes are internal.
                                                                                                        // So this is not a security back door.
            ]
            public DataGridRowAccessibleObject(DataGridRow owner) : base() {
                Debug.Assert(owner != null, "DataGridRowAccessibleObject must have a valid owner DataGridRow");
                this.owner = owner;
                DataGrid grid = DataGrid;
                Debug.WriteLineIf(DataGrid.DataGridAcc.TraceVerbose, "Create row accessible object");
 
                EnsureChildren();
            }
 
            private void EnsureChildren() {
                if (cells == null) {
                    // default size... little extra for relationships...
                    //
                    cells = new ArrayList(DataGrid.myGridTable.GridColumnStyles.Count + 2);
                    AddChildAccessibleObjects(cells);
                }
            }
 
            protected virtual void AddChildAccessibleObjects(IList children) {
                Debug.WriteLineIf(DataGrid.DataGridAcc.TraceVerbose, "Create row's accessible children");
                Debug.Indent();
                GridColumnStylesCollection cols = DataGrid.myGridTable.GridColumnStyles;
                int len = cols.Count;
                Debug.WriteLineIf(DataGrid.DataGridAcc.TraceVerbose, len + " columns present");
                for (int i=0; i<len; i++) {
                    children.Add(CreateCellAccessibleObject(i));
                }
                Debug.Unindent();
            }
 
            protected virtual AccessibleObject CreateCellAccessibleObject(int column) {
                return new DataGridCellAccessibleObject(owner, column);
            }
 
            public override Rectangle Bounds {
                get {
                    return DataGrid.RectangleToScreen(DataGrid.GetRowBounds(owner));
                }
            }
 
            public override string Name {
                get {
                    if (owner is DataGridAddNewRow) {
                        return SR.GetString(SR.AccDGNewRow);
                    }
                    else {
                        return DataGridRowAccessibleObject.CellToDisplayString(DataGrid, owner.RowNumber, 0);
                    }
                }
            }
 
            protected DataGridRow Owner {
                get {
                    return owner;
                }
            }
 
            public override AccessibleObject Parent {
                [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
                get {
                    return DataGrid.AccessibilityObject;
                }
            }
 
            private DataGrid DataGrid {
                get {
                    return owner.DataGrid;
                }
            }
 
            public override AccessibleRole Role {
                get {
                    return AccessibleRole.Row;
                }
            }
 
            public override AccessibleStates State {
                get {
                    AccessibleStates state = AccessibleStates.Selectable | AccessibleStates.Focusable;
 
                    // Determine focus
                    //
                    if (DataGrid.CurrentCell.RowNumber == owner.RowNumber) {
                        state |= AccessibleStates.Focused;
                    }
 
                    // Determine selected
                    //
                    if (DataGrid.CurrentRowIndex == owner.RowNumber) {
                        state |= AccessibleStates.Selected;
                    }
 
                    return state;
                }
            }
 
            public override string Value {
                [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
                get {
                    return Name;
                }
            }
            
            public override AccessibleObject GetChild(int index) {
                if (index < cells.Count) {
                    return (AccessibleObject)cells[index];
                }
 
                return null;
            }
 
            public override int GetChildCount() {
                return cells.Count;
            }
 
            /// <include file='doc\DataGridRow.uex' path='docs/doc[@for="DataGridRow.DataGridRowAccessibleObject.GetFocused"]/*' />
            /// <devdoc>
            ///      Returns the currently focused child, if any.
            ///      Returns this if the object itself is focused.
            /// </devdoc>
            public override AccessibleObject GetFocused() {
                if (DataGrid.Focused) {
                    DataGridCell cell = DataGrid.CurrentCell;
                    if (cell.RowNumber == owner.RowNumber) {
                        return (AccessibleObject)cells[cell.ColumnNumber];
                    }
                }
 
                return null;
            }
 
 
            /// <include file='doc\DataGridRow.uex' path='docs/doc[@for="DataGridRow.DataGridRowAccessibleObject.Navigate"]/*' />
            /// <devdoc>
            ///      Navigate to the next or previous grid entry.entry.
            /// </devdoc>
            [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
            public override AccessibleObject Navigate(AccessibleNavigation navdir) {
                switch (navdir) {
                    case AccessibleNavigation.Down:
                    case AccessibleNavigation.Right:
                    case AccessibleNavigation.Next:
                        return DataGrid.AccessibilityObject.GetChild(1 + owner.dgTable.GridColumnStyles.Count + owner.RowNumber + 1);
 
                    case AccessibleNavigation.Up:
                    case AccessibleNavigation.Left:
                    case AccessibleNavigation.Previous:
                        return DataGrid.AccessibilityObject.GetChild(1 + owner.dgTable.GridColumnStyles.Count + owner.RowNumber - 1);
 
                    case AccessibleNavigation.FirstChild:
                        if (GetChildCount() > 0) {
                            return GetChild(0);
                        }
                        break;
                    case AccessibleNavigation.LastChild:
                        if (GetChildCount() > 0) {
                            return GetChild(GetChildCount() - 1);
                        }
                        break;
                }
 
                return null;
 
            }
 
            [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
            public override void Select(AccessibleSelection flags) {
                // Focus the PropertyGridView window
                //
                if ( (flags & AccessibleSelection.TakeFocus) == AccessibleSelection.TakeFocus) {
                    DataGrid.Focus();
                }
 
                // Select the grid entry
                //
                if ( (flags & AccessibleSelection.TakeSelection) == AccessibleSelection.TakeSelection) {
                    DataGrid.CurrentRowIndex = owner.RowNumber;
                }
            }
 
        }
 
        [ComVisible(true)]
        protected class DataGridCellAccessibleObject : AccessibleObject {
            DataGridRow owner = null;
            int column;
 
            public DataGridCellAccessibleObject(DataGridRow owner, int column) : base() {
                Debug.Assert(owner != null, "DataGridColumnAccessibleObject must have a valid owner DataGridRow");
                this.owner = owner;
                this.column = column;
                Debug.WriteLineIf(DataGrid.DataGridAcc.TraceVerbose, "Create cell accessible object");
            }
 
            public override Rectangle Bounds {
                get {
                    return DataGrid.RectangleToScreen(DataGrid.GetCellBounds(new DataGridCell(owner.RowNumber, column)));
                }
            }
 
            public override string Name {
                get {
                    return DataGrid.myGridTable.GridColumnStyles[column].HeaderText;
                }
            }
 
            public override AccessibleObject Parent {
                [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
                get {
                    return owner.AccessibleObject;
                }
            }
 
            protected DataGrid DataGrid {
                get {
                    return owner.DataGrid;
                }
            }
 
            public override string DefaultAction {
                get {
                    return SR.GetString(SR.AccDGEdit);
                }
            }
 
            public override AccessibleRole Role {
                get {
                    return AccessibleRole.Cell;
                }
            }
 
            public override AccessibleStates State {
                get {
                    AccessibleStates state = AccessibleStates.Selectable | AccessibleStates.Focusable;
 
                    // Determine focus
                    //
                    if (DataGrid.CurrentCell.RowNumber == owner.RowNumber
                        && DataGrid.CurrentCell.ColumnNumber == column) {
                        if (DataGrid.Focused) {
                            state |= AccessibleStates.Focused;
                        }
                        state |= AccessibleStates.Selected;
                    }
 
                    return state;
                }
            }
 
            public override string Value {
                [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
                get {
                    if (owner is DataGridAddNewRow) {
                        return null;
                    }
                    else {
                        return DataGridRowAccessibleObject.CellToDisplayString(DataGrid, owner.RowNumber, column);
                    }
                }
 
                [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
                set {
                    if (!(owner is DataGridAddNewRow)) {
                        object realValue =  DataGridRowAccessibleObject.DisplayStringToCell(DataGrid, owner.RowNumber, column, value);
                        DataGrid[owner.RowNumber, column] = realValue;
                    }
                }
            }
 
            [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
            public override void DoDefaultAction() {
                Select(AccessibleSelection.TakeFocus | AccessibleSelection.TakeSelection);
            }
 
            /// <include file='doc\DataGridRow.uex' path='docs/doc[@for="DataGridRow.DataGridCellAccessibleObject.GetFocused"]/*' />
            /// <devdoc>
            ///      Returns the currently focused child, if any.
            ///      Returns this if the object itself is focused.
            /// </devdoc>
            public override AccessibleObject GetFocused() {
                // Datagrid always returns the cell as the focused thing... so do we!
                //
                return DataGrid.AccessibilityObject.GetFocused();
            }
 
 
            /// <include file='doc\DataGridRow.uex' path='docs/doc[@for="DataGridRow.DataGridCellAccessibleObject.Navigate"]/*' />
            /// <devdoc>
            ///      Navigate to the next or previous grid entry.
            /// </devdoc>
            [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
            public override AccessibleObject Navigate(AccessibleNavigation navdir) {
                switch (navdir) {
                    case AccessibleNavigation.Right:
                    case AccessibleNavigation.Next:
                        if (column < owner.AccessibleObject.GetChildCount() - 1) {
                            return owner.AccessibleObject.GetChild(column + 1);
                        }
                        else {
                            AccessibleObject o = DataGrid.AccessibilityObject.GetChild(1 + owner.dgTable.GridColumnStyles.Count + owner.RowNumber + 1);
                            if (o != null) {
                                return o.Navigate(AccessibleNavigation.FirstChild);
                            }
                        }
                        break;
                    case AccessibleNavigation.Down:
                        return DataGrid.AccessibilityObject.GetChild(1 + owner.dgTable.GridColumnStyles.Count + owner.RowNumber + 1).Navigate(AccessibleNavigation.FirstChild);
                    case AccessibleNavigation.Up:
                        return DataGrid.AccessibilityObject.GetChild(1 + owner.dgTable.GridColumnStyles.Count + owner.RowNumber - 1).Navigate(AccessibleNavigation.FirstChild);
                    case AccessibleNavigation.Left:
                    case AccessibleNavigation.Previous:
                        if (column > 0) {
                            return owner.AccessibleObject.GetChild(column - 1);
                        }
                        else {
                            AccessibleObject o = DataGrid.AccessibilityObject.GetChild(1 + owner.dgTable.GridColumnStyles.Count + owner.RowNumber - 1);
                            if (o != null) {
                                return o.Navigate(AccessibleNavigation.LastChild);
                            }
                        }
                        break;
 
                    case AccessibleNavigation.FirstChild:
                    case AccessibleNavigation.LastChild:
 
                        break;
                }
 
                return null;
 
            }
 
            [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
            public override void Select(AccessibleSelection flags) {
                // Focus the PropertyGridView window
                //
                if ( (flags & AccessibleSelection.TakeFocus) == AccessibleSelection.TakeFocus) {
                    DataGrid.Focus();
                }
 
                // Select the grid entry
                //
                if ( (flags & AccessibleSelection.TakeSelection) == AccessibleSelection.TakeSelection) {
                    DataGrid.CurrentCell = new DataGridCell(owner.RowNumber, column);
                }
            }
 
        }
 
    }
}