Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / WinForms / Managed / System / WinForms / DataGridParentRows.cs / 1305376 / DataGridParentRows.cs
//------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- namespace System.Windows.Forms { using System.Text; using System.Runtime.Remoting; using System; using System.Collections; using System.Windows.Forms; using System.Security.Permissions; using System.ComponentModel; using System.Drawing; using System.Drawing.Imaging; using Microsoft.Win32; using System.Runtime.InteropServices; using System.Diagnostics; using System.Runtime.Versioning; internal class DataGridParentRows { // siting // private DataGrid dataGrid; // ui // // private Color backColor = DataGrid.defaultParentRowsBackColor; // private Color foreColor = DataGrid.defaultParentRowsForeColor; private SolidBrush backBrush = DataGrid.DefaultParentRowsBackBrush; private SolidBrush foreBrush = DataGrid.DefaultParentRowsForeBrush; private int borderWidth = 1; // private Color borderColor = SystemColors.WindowFrame; private Brush borderBrush = new SolidBrush(SystemColors.WindowFrame); private static Bitmap rightArrow = null; private static Bitmap leftArrow = null; private ColorMap[] colorMap = new ColorMap[] {new ColorMap()}; // private bool gridLineDots = false; // private Color gridLineColor = SystemColors.Control; // private Brush gridLineBrush = SystemBrushes.Control; private Pen gridLinePen = SystemPens.Control; private int totalHeight = 0; private int textRegionHeight = 0; // now that we have left and right arrows, we also have layout private Layout layout = new Layout(); // mouse info // // private bool overLeftArrow = false; // private bool overRightArrow = false; private bool downLeftArrow = false; private bool downRightArrow = false; // a horizOffset of 0 means that the layout for the parent // rows is left aligned. // a horizOffset of 1 means that the leftmost unit of information ( let it be a // table name, a column name or a column value ) is not visible. // a horizOffset of 2 means that the leftmost 2 units of information are not visible, and so on // private int horizOffset = 0; // storage for parent row states // private ArrayList parents = new ArrayList(); private int parentsCount = 0; private ArrayList rowHeights = new ArrayList(); AccessibleObject accessibleObject; internal DataGridParentRows(DataGrid dataGrid) { this.colorMap[0].OldColor = Color.Black; this.dataGrid = dataGrid; // UpdateGridLinePen(); } // =----------------------------------------------------------------- // = Properties // =----------------------------------------------------------------- public AccessibleObject AccessibleObject { get { if (accessibleObject == null) { accessibleObject = new DataGridParentRowsAccessibleObject(this); } return accessibleObject; } } internal Color BackColor { get { return backBrush.Color; } set { if (value.IsEmpty) throw new ArgumentException(SR.GetString(SR.DataGridEmptyColor, "Parent Rows BackColor")); if (value != backBrush.Color) { backBrush = new SolidBrush(value); Invalidate(); } } } internal SolidBrush BackBrush { get { return backBrush; } set { if (value != backBrush) { CheckNull(value, "BackBrush"); backBrush = value; Invalidate(); } } } internal SolidBrush ForeBrush { get { return foreBrush; } set { if (value != foreBrush) { CheckNull(value, "BackBrush"); foreBrush = value; Invalidate(); } } } // since the layout of the parentRows is computed on every paint message, // we can actually return true ClientRectangle coordinates internal Rectangle GetBoundsForDataGridStateAccesibility(DataGridState dgs) { Rectangle ret = Rectangle.Empty; int rectY = 0; for (int i = 0; i < parentsCount; i++) { int height = (int) rowHeights[i]; if (parents[i] == dgs) { ret.X = layout.leftArrow.IsEmpty ? layout.data.X : layout.leftArrow.Right; ret.Height = height; ret.Y = rectY; ret.Width = layout.data.Width; return ret; } rectY += height; } return ret; } /* internal Color BorderColor { get { return borderColor; } set { if (value != borderColor) { borderColor = value; Invalidate(); } } } */ internal Brush BorderBrush { get { return borderBrush; } set { if (value != borderBrush) { borderBrush = value; Invalidate(); } } } /* internal Brush GridLineBrush { get { return gridLineBrush; } set { if (value != gridLineBrush) { gridLineBrush = value; UpdateGridLinePen(); Invalidate(); } } } internal bool GridLineDots { get { return gridLineDots; } set { if (gridLineDots != value) { gridLineDots = value; UpdateGridLinePen(); Invalidate(); } } } */ internal int Height { get { return totalHeight; } } internal Color ForeColor { get { return foreBrush.Color; } set { if (value.IsEmpty) throw new ArgumentException(SR.GetString(SR.DataGridEmptyColor, "Parent Rows ForeColor")); if (value != foreBrush.Color) { foreBrush = new SolidBrush(value); Invalidate(); } } } internal bool Visible { get { return dataGrid.ParentRowsVisible; } set { dataGrid.ParentRowsVisible = value; } } // =------------------------------------------------------------------ // = Methods // =----------------------------------------------------------------- ////// /// Adds a DataGridState object to the top of the list of parents. /// internal void AddParent(DataGridState dgs) { CurrencyManager childDataSource = (CurrencyManager) dataGrid.BindingContext[dgs.DataSource, dgs.DataMember]; parents.Add(dgs); SetParentCount(parentsCount + 1); Debug.Assert(GetTopParent() != null, "we should have a parent at least"); } internal void Clear() { for (int i = 0; i < parents.Count; i++) { DataGridState dgs = parents[i] as DataGridState; dgs.RemoveChangeNotification(); } parents.Clear(); rowHeights.Clear(); totalHeight = 0; SetParentCount(0); } internal void SetParentCount(int count) { parentsCount = count; dataGrid.Caption.BackButtonVisible = (parentsCount > 0) && (dataGrid.AllowNavigation); } internal void CheckNull(object value, string propName) { if (value == null) throw new ArgumentNullException("propName"); } internal void Dispose() { gridLinePen.Dispose(); } ////// /// Retrieves the top most parent in the list of parents. /// internal DataGridState GetTopParent() { if (parentsCount < 1) { return null; } return(DataGridState)(((ICloneable)(parents[parentsCount-1])).Clone()); } ////// /// Determines if there are any parent rows contained in this object. /// internal bool IsEmpty() { return parentsCount == 0; } ////// /// Similar to GetTopParent() but also removes it. /// internal DataGridState PopTop() { if (parentsCount < 1) { return null; } SetParentCount(parentsCount - 1); DataGridState ret = (DataGridState)parents[parentsCount]; ret.RemoveChangeNotification(); parents.RemoveAt(parentsCount); return ret; } internal void Invalidate() { if (dataGrid != null) dataGrid.InvalidateParentRows(); } internal void InvalidateRect(Rectangle rect) { if (dataGrid != null) { Rectangle r = new Rectangle(rect.X, rect.Y, rect.Width + borderWidth, rect.Height + borderWidth); dataGrid.InvalidateParentRowsRect(r); } } // called from DataGrid::OnLayout internal void OnLayout() { if (parentsCount == rowHeights.Count) return; int height = 0; if (totalHeight == 0) { totalHeight += 2 * borderWidth; } // figure out how tall each row's text will be // textRegionHeight = (int)dataGrid.Font.Height + 2; // make the height of the Column.Font count for the height // of the parentRows; // // if the user wants to programatically // navigate to a relation in the constructor of the form // ( ie, when the form does not process PerformLayout ) // the grid will receive an OnLayout message when there is more // than one parent in the grid if (parentsCount > rowHeights.Count) { Debug.Assert(parentsCount == rowHeights.Count + 1 || rowHeights.Count == 0, "see bug 82808 for more info, or the comment above"); int rowHeightsCount = this.rowHeights.Count; for (int i = rowHeightsCount; i < parentsCount; i++) { DataGridState dgs = (DataGridState) parents[i]; GridColumnStylesCollection cols = dgs.GridColumnStyles; int colsHeight = 0; for (int j=0; j0) { ResetMouseInfo(); horizOffset -= 1; Invalidate(); } else { ResetMouseInfo(); InvalidateRect(layout.leftArrow); } } private void RightArrowClick(int cellCount) { if (horizOffset < cellCount - 1) { ResetMouseInfo(); horizOffset += 1; Invalidate(); } else { ResetMouseInfo(); InvalidateRect(layout.rightArrow); } } // the only mouse clicks that are handled are // the mouse clicks on the LeftArrow and RightArrow // internal void OnMouseDown(int x, int y, bool alignToRight) { if (layout.rightArrow.IsEmpty) { Debug.Assert(layout.leftArrow.IsEmpty, "we can't have the leftArrow w/o the rightArrow"); return; } int cellCount = CellCount(); if (layout.rightArrow.Contains(x,y)) { // draw a nice sunken border around the right arrow area // we want to keep a cell on the screen downRightArrow = true; if (alignToRight) LeftArrowClick(cellCount); else RightArrowClick(cellCount); } else if (layout.leftArrow.Contains(x,y)) { downLeftArrow = true; if (alignToRight) RightArrowClick(cellCount); else LeftArrowClick(cellCount); } else { if (downLeftArrow) { downLeftArrow = false; InvalidateRect(layout.leftArrow); } if (downRightArrow) { downRightArrow = false; InvalidateRect(layout.rightArrow); } } } internal void OnMouseLeave() { if (downLeftArrow) { downLeftArrow = false; InvalidateRect(layout.leftArrow); } if (downRightArrow) { downRightArrow = false; InvalidateRect(layout.rightArrow); } } internal void OnMouseMove(int x, int y) { /* if (!layout.leftArrow.IsEmpty && layout.leftArrow.Contains(x,y)) { ResetMouseInfo(); overLeftArrow = true; InvalidateRect(layout.leftArrow); return; } if (!layout.rightArrow.IsEmpty && layout.rightArrow.Contains(x,y)) { ResetMouseInfo(); overRightArrow = true; InvalidateRect(layout.rightArrow); return; } */ if (downLeftArrow) { downLeftArrow = false; InvalidateRect(layout.leftArrow); } if (downRightArrow) { downRightArrow = false; InvalidateRect(layout.rightArrow); } } internal void OnMouseUp(int x, int y) { ResetMouseInfo(); if (!layout.rightArrow.IsEmpty && layout.rightArrow.Contains(x,y)) { InvalidateRect(layout.rightArrow); return; } if (!layout.leftArrow.IsEmpty && layout.leftArrow.Contains(x,y)) { InvalidateRect(layout.leftArrow); return; } } internal void OnResize(Rectangle oldBounds) { Invalidate(); } /// /// /// Paints the parent rows /// internal void Paint(Graphics g, Rectangle visualbounds, bool alignRight) { Rectangle bounds = visualbounds; // Paint the border around our bounds if (borderWidth > 0) { PaintBorder(g, bounds); bounds.Inflate(-borderWidth, -borderWidth); } PaintParentRows(g, bounds, alignRight); } private void PaintBorder(Graphics g, Rectangle bounds) { Rectangle border = bounds; // top border.Height = borderWidth; g.FillRectangle(borderBrush, border); // bottom border.Y = bounds.Bottom - borderWidth; g.FillRectangle(borderBrush, border); // left border = new Rectangle(bounds.X, bounds.Y + borderWidth, borderWidth, bounds.Height - 2*borderWidth); g.FillRectangle(borderBrush, border); // right border.X = bounds.Right - borderWidth; g.FillRectangle(borderBrush, border); } // will return the width of the text box that will fit all the // tables names private int GetTableBoxWidth(Graphics g, Font font) { // try to make the font BOLD Font textFont = font; try { textFont = new Font(font, FontStyle.Bold); } catch { } int width = 0; for (int row = 0; row < parentsCount; row ++) { DataGridState dgs = (DataGridState) parents[row]; // Graphics.MeasureString(...) returns different results for ": " than for " :" // string displayTableName = dgs.ListManager.GetListName() + " :"; int size = (int) g.MeasureString(displayTableName, textFont).Width; width = Math.Max(size, width); } return width; } // will return the width of the text box that will // fit all the column names private int GetColBoxWidth(Graphics g, Font font, int colNum) { int width = 0; for (int row = 0; row < parentsCount; row ++) { DataGridState dgs = (DataGridState) parents[row]; GridColumnStylesCollection columns = dgs.GridColumnStyles; if (colNum < columns.Count) { // Graphics.MeasureString(...) returns different results for ": " than for " :" // string colName = columns[colNum].HeaderText + " :"; int size = (int) g.MeasureString(colName, font).Width; width = Math.Max(size, width); } } return width; } // will return the width of the best fit for the column // private int GetColDataBoxWidth(Graphics g, int colNum) { int width = 0; for (int row = 0; row < parentsCount; row ++) { DataGridState dgs = (DataGridState) parents[row]; GridColumnStylesCollection columns = dgs.GridColumnStyles; if (colNum < columns.Count) { object value = columns[colNum].GetColumnValueAtRow((CurrencyManager) dataGrid.BindingContext[dgs.DataSource, dgs.DataMember], dgs.LinkingRow.RowNumber); int size = columns[colNum].GetPreferredSize(g, value).Width; width = Math.Max(size, width); } } return width; } // will return the count of the table with the largest number of columns private int ColsCount() { int colNum = 0; for (int row = 0; row < parentsCount; row ++) { DataGridState dgs = (DataGridState) parents[row]; colNum = Math.Max(colNum, dgs.GridColumnStyles.Count); } return colNum; } // will return the total width required to paint the parentRows private int TotalWidth(int tableNameBoxWidth, int[] colsNameWidths, int[] colsDataWidths) { int totalWidth = 0; totalWidth += tableNameBoxWidth; Debug.Assert(colsNameWidths.Length == colsDataWidths.Length, "both arrays are as long as the largest column count in dgs"); for (int i = 0; i < colsNameWidths.Length; i ++) { totalWidth += colsNameWidths[i]; totalWidth += colsDataWidths[i]; } // let 3 pixels in between datacolumns // see DonnaWa totalWidth += 3 * (colsNameWidths.Length - 1); return totalWidth; } // computes the layout for the parent rows // private void ComputeLayout(Rectangle bounds, int tableNameBoxWidth, int[] colsNameWidths, int[] colsDataWidths) { int totalWidth = TotalWidth(tableNameBoxWidth, colsNameWidths, colsDataWidths); if (totalWidth > bounds.Width) { layout.leftArrow = new Rectangle(bounds.X, bounds.Y, 15, bounds.Height); layout.data = new Rectangle(layout.leftArrow.Right, bounds.Y, bounds.Width - 30, bounds.Height); layout.rightArrow = new Rectangle(layout.data.Right, bounds.Y, 15, bounds.Height); } else { layout.data = bounds; layout.leftArrow = Rectangle.Empty; layout.rightArrow = Rectangle.Empty; } } private void PaintParentRows(Graphics g, Rectangle bounds, bool alignToRight) { // variables needed for aligning the table and column names int tableNameBoxWidth = 0; int numCols = ColsCount(); int[] colsNameWidths = new int[numCols]; int[] colsDataWidths = new int[numCols]; // compute the size of the box that will contain the tableName // if (dataGrid.ParentRowsLabelStyle == DataGridParentRowsLabelStyle.TableName || dataGrid.ParentRowsLabelStyle == DataGridParentRowsLabelStyle.Both) { tableNameBoxWidth = GetTableBoxWidth(g, dataGrid.Font); } // initialiaze the arrays that contain the column names and the column size // for (int i = 0; i < numCols; i++) { if (dataGrid.ParentRowsLabelStyle == DataGridParentRowsLabelStyle.ColumnName || dataGrid.ParentRowsLabelStyle == DataGridParentRowsLabelStyle.Both) { colsNameWidths[i] = GetColBoxWidth(g, dataGrid.Font, i); } else { colsNameWidths[i] = 0; } colsDataWidths[i] = GetColDataBoxWidth(g, i); } // compute the layout // ComputeLayout(bounds, tableNameBoxWidth, colsNameWidths, colsDataWidths); // paint the navigation arrows, if necessary // if (!layout.leftArrow.IsEmpty) { g.FillRectangle(BackBrush, layout.leftArrow); PaintLeftArrow(g, layout.leftArrow, alignToRight); } // paint the parent rows: // Rectangle rowBounds = layout.data; for (int row = 0; row < parentsCount; ++row) { rowBounds.Height = (int) rowHeights[row]; if (rowBounds.Y > bounds.Bottom) break; int paintedWidth = PaintRow(g, rowBounds, row, dataGrid.Font, alignToRight, tableNameBoxWidth, colsNameWidths, colsDataWidths); if (row == parentsCount-1) break; // draw the grid line below g.DrawLine(gridLinePen, rowBounds.X, rowBounds.Bottom, rowBounds.X + paintedWidth, rowBounds.Bottom); rowBounds.Y += rowBounds.Height; } if (!layout.rightArrow.IsEmpty) { g.FillRectangle(BackBrush, layout.rightArrow); PaintRightArrow(g, layout.rightArrow, alignToRight); } } [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] private Bitmap GetBitmap(string bitmapName, Color transparentColor) { Bitmap b = null; try { b = new Bitmap(typeof(DataGridParentRows), bitmapName); b.MakeTransparent(transparentColor); } catch (Exception e) { Debug.Fail("Failed to load bitmap: " + bitmapName, e.ToString()); } return b; } [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] private Bitmap GetRightArrowBitmap() { if (rightArrow == null) rightArrow = GetBitmap("DataGridParentRows.RightArrow.bmp", Color.White); return rightArrow; } [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] private Bitmap GetLeftArrowBitmap() { if (leftArrow == null) leftArrow = GetBitmap("DataGridParentRows.LeftArrow.bmp", Color.White); return leftArrow; } private void PaintBitmap(Graphics g, Bitmap b, Rectangle bounds) { // center the bitmap in the bounds: int bmpX = bounds.X + (bounds.Width - b.Width) / 2; int bmpY = bounds.Y + (bounds.Height - b.Height) / 2; Rectangle bmpRect = new Rectangle(bmpX, bmpY, b.Width, b.Height); g.FillRectangle(BackBrush, bmpRect); // now draw the bitmap ImageAttributes attr = new ImageAttributes(); this.colorMap[0].NewColor = this.ForeColor; attr.SetRemapTable(colorMap, ColorAdjustType.Bitmap); g.DrawImage(b, bmpRect, 0, 0, bmpRect.Width, bmpRect.Height,GraphicsUnit.Pixel, attr); attr.Dispose(); } /* private void PaintOverButton(Graphics g, Rectangle bounds) { } */ private void PaintDownButton(Graphics g, Rectangle bounds) { g.DrawLine(Pens.Black, bounds.X, bounds.Y, bounds.X + bounds.Width, bounds.Y); // the top g.DrawLine(Pens.White, bounds.X + bounds.Width, bounds.Y, bounds.X + bounds.Width, bounds.Y + bounds.Height); // the right side g.DrawLine(Pens.White, bounds.X + bounds.Width, bounds.Y + bounds.Height, bounds.X, bounds.Y + bounds.Height); // the right side g.DrawLine(Pens.Black, bounds.X, bounds.Y + bounds.Height, bounds.X, bounds.Y); // the left side } private void PaintLeftArrow(Graphics g, Rectangle bounds, bool alignToRight) { Bitmap bmp = GetLeftArrowBitmap(); // paint the border around this bitmap if this is the case // /* if (overLeftArrow) { Debug.Assert(!downLeftArrow, "can both of those happen?"); PaintOverButton(g, bounds); layout.leftArrow.Inflate(-1,-1); } */ if (downLeftArrow) { PaintDownButton(g, bounds); layout.leftArrow.Inflate(-1,-1); lock (bmp) { PaintBitmap(g, bmp, bounds); } layout.leftArrow.Inflate(1,1); } else { lock (bmp) { PaintBitmap(g, bmp, bounds); } } } private void PaintRightArrow(Graphics g, Rectangle bounds, bool alignToRight) { Bitmap bmp = GetRightArrowBitmap(); // paint the border around this bitmap if this is the case // /* if (overRightArrow) { Debug.Assert(!downRightArrow, "can both of those happen?"); PaintOverButton(g, bounds); layout.rightArrow.Inflate(-1,-1); } */ if (downRightArrow) { PaintDownButton(g, bounds); layout.rightArrow.Inflate(-1,-1); lock (bmp) { PaintBitmap(g, bmp, bounds); } layout.rightArrow.Inflate(1,1); } else { lock (bmp) { PaintBitmap(g, bmp, bounds); } } } private int PaintRow(Graphics g, Rectangle bounds, int row, Font font, bool alignToRight, int tableNameBoxWidth, int[] colsNameWidths, int[] colsDataWidths) { DataGridState dgs = (DataGridState) parents[row]; Rectangle paintBounds = bounds; Rectangle rowBounds = bounds; paintBounds.Height = (int) rowHeights[row]; rowBounds.Height = (int) rowHeights[row]; int paintedWidth = 0; // used for scrolling: when paiting, we will skip horizOffset cells in the dataGrid ParentRows int skippedCells = 0; // paint the table name if ( dataGrid.ParentRowsLabelStyle == DataGridParentRowsLabelStyle.TableName || dataGrid.ParentRowsLabelStyle == DataGridParentRowsLabelStyle.Both) { if (skippedCells < horizOffset) { // skip this skippedCells ++; } else { paintBounds.Width = Math.Min(paintBounds.Width, tableNameBoxWidth); paintBounds.X = MirrorRect(bounds, paintBounds, alignToRight); string displayTableName = dgs.ListManager.GetListName() + ": "; PaintText(g, paintBounds, displayTableName, font, true, alignToRight); // true is for painting bold paintedWidth += paintBounds.Width; } } if (paintedWidth >= bounds.Width) return bounds.Width; // we painted everything rowBounds.Width -= paintedWidth; rowBounds.X += alignToRight ? 0 : paintedWidth; paintedWidth += PaintColumns(g, rowBounds, dgs, font, alignToRight, colsNameWidths, colsDataWidths, skippedCells); // paint the possible space left after columns if (paintedWidth < bounds.Width) { paintBounds.X = bounds.X + paintedWidth; paintBounds.Width = bounds.Width - paintedWidth; paintBounds.X = MirrorRect(bounds, paintBounds, alignToRight); g.FillRectangle(BackBrush, paintBounds); } return paintedWidth; } private int PaintColumns(Graphics g, Rectangle bounds, DataGridState dgs, Font font, bool alignToRight, int[] colsNameWidths, int[] colsDataWidths, int skippedCells) { Rectangle paintBounds = bounds; Rectangle rowBounds = bounds; GridColumnStylesCollection cols = dgs.GridColumnStyles; int cx = 0; for (int i = 0; i < cols.Count; i ++) { if (cx >= bounds.Width) break; // paint the column name, if we have to if (dataGrid.ParentRowsLabelStyle == DataGridParentRowsLabelStyle.ColumnName || dataGrid.ParentRowsLabelStyle == DataGridParentRowsLabelStyle.Both) { if (skippedCells < horizOffset) { // skip this column } else { paintBounds.X = bounds.X + cx; paintBounds.Width = Math.Min(bounds.Width - cx, colsNameWidths[i]); paintBounds.X = MirrorRect(bounds, paintBounds, alignToRight); string colName = cols[i].HeaderText + ": "; PaintText(g, paintBounds, colName, font, false, alignToRight); // false is for not painting bold cx += paintBounds.Width; } } if (cx >= bounds.Width) break; if (skippedCells < horizOffset) { // skip this cell skippedCells ++; } else { // paint the cell contents paintBounds.X = bounds.X + cx; paintBounds.Width = Math.Min(bounds.Width - cx, colsDataWidths[i]); paintBounds.X = MirrorRect(bounds, paintBounds, alignToRight); // when we paint the data grid parent rows, we want to paint the data at the position // stored in the currency manager. cols[i].Paint(g, paintBounds, (CurrencyManager) dataGrid.BindingContext[dgs.DataSource, dgs.DataMember], dataGrid.BindingContext[dgs.DataSource, dgs.DataMember].Position, BackBrush, ForeBrush, alignToRight); cx += paintBounds.Width; // draw the line to the right (or left, according to alignRight) // g.DrawLine(new Pen(SystemColors.ControlDark), alignToRight ? paintBounds.X : paintBounds.Right, paintBounds.Y, alignToRight ? paintBounds.X : paintBounds.Right, paintBounds.Bottom); // this is how wide the line is.... cx++; // put 3 pixels in between columns // see DonnaWa // if ( i < cols.Count - 1) { paintBounds.X = bounds.X + cx; paintBounds.Width = Math.Min(bounds.Width - cx, 3); paintBounds.X = MirrorRect(bounds, paintBounds, alignToRight); g.FillRectangle(BackBrush, paintBounds); cx += 3; } } } return cx; } ////// /// Draws on the screen the text. It is used only to paint the Table Name and the column Names /// Returns the width of bounding rectangle that was passed in /// private int PaintText(Graphics g, Rectangle textBounds, string text, Font font, bool bold, bool alignToRight) { Font textFont = font; if (bold) try { textFont = new Font(font, FontStyle.Bold); } catch {} else textFont = font; // right now, we paint the entire box, cause it will be used anyway g.FillRectangle(BackBrush, textBounds); StringFormat format = new StringFormat(); if (alignToRight) { format.FormatFlags |= StringFormatFlags.DirectionRightToLeft; format.Alignment = StringAlignment.Far; } format.FormatFlags |= StringFormatFlags.NoWrap; // part 1, section 3: put the table and the column name in the // parent rows at the same height as the dataGridTextBoxColumn draws the string // textBounds.Offset(0, 2); textBounds.Height -= 2; g.DrawString(text, textFont, ForeBrush, textBounds, format); format.Dispose(); return textBounds.Width; } // will return the X coordinate of the containedRect mirrored within the surroundingRect // according to the value of alignToRight private int MirrorRect(Rectangle surroundingRect, Rectangle containedRect, bool alignToRight) { Debug.Assert(containedRect.X >= surroundingRect.X && containedRect.Right <= surroundingRect.Right, "containedRect is not contained in surroundingRect"); if (alignToRight) return surroundingRect.Right - containedRect.Right + surroundingRect.X; else return containedRect.X; } private class Layout { public Rectangle data; public Rectangle leftArrow; public Rectangle rightArrow; public Layout() { data = Rectangle.Empty; leftArrow = Rectangle.Empty; rightArrow = Rectangle.Empty; } public override string ToString() { StringBuilder sb = new StringBuilder(200); sb.Append("ParentRows Layout: \n"); sb.Append("data = "); sb.Append(data.ToString()); sb.Append("\n leftArrow = "); sb.Append(leftArrow.ToString()); sb.Append("\n rightArrow = "); sb.Append(rightArrow.ToString()); sb.Append("\n"); return sb.ToString(); } } [ComVisible(true)] protected internal class DataGridParentRowsAccessibleObject : AccessibleObject { DataGridParentRows owner = null; public DataGridParentRowsAccessibleObject(DataGridParentRows owner) : base() { Debug.Assert(owner != null, "DataGridParentRowsAccessibleObject must have a valid owner"); this.owner = owner; } internal DataGridParentRows Owner { get { return owner; } } public override Rectangle Bounds { get { return owner.dataGrid.RectangleToScreen(owner.dataGrid.ParentRowsBounds); } } public override string DefaultAction { get { return SR.GetString(SR.AccDGNavigateBack); } } public override string Name { get { return SR.GetString(SR.AccDGParentRows); } } public override AccessibleObject Parent { [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] get { return owner.dataGrid.AccessibilityObject; } } public override AccessibleRole Role { get { return AccessibleRole.List; } } public override AccessibleStates State { get { AccessibleStates state = AccessibleStates.ReadOnly; if (owner.parentsCount == 0) { state |= AccessibleStates.Invisible; } if (owner.dataGrid.ParentRowsVisible) { state |= AccessibleStates.Expanded; } else { state |= AccessibleStates.Collapsed; } return state; } } public override string Value { [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] get { return null; } } [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] public override void DoDefaultAction() { owner.dataGrid.NavigateBack(); } public override AccessibleObject GetChild(int index) { return ((DataGridState)owner.parents[index]).ParentRowAccessibleObject; } public override int GetChildCount() { return owner.parentsCount; } ////// /// Returns the currently focused child, if any. /// Returns this if the object itself is focused. /// public override AccessibleObject GetFocused() { return null; } internal AccessibleObject GetNext(AccessibleObject child) { int children = GetChildCount(); bool hit = false; for (int i=0; i=0; i--) { if (hit) { return GetChild(i); } if (GetChild(i) == child) { hit = true; } } return null; } /// /// /// Navigate to the next or previous grid entry. /// [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] public override AccessibleObject Navigate(AccessibleNavigation navdir) { switch (navdir) { case AccessibleNavigation.Right: case AccessibleNavigation.Next: case AccessibleNavigation.Down: return Parent.GetChild(1); case AccessibleNavigation.Up: case AccessibleNavigation.Left: case AccessibleNavigation.Previous: return Parent.GetChild(GetChildCount() - 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) { } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- namespace System.Windows.Forms { using System.Text; using System.Runtime.Remoting; using System; using System.Collections; using System.Windows.Forms; using System.Security.Permissions; using System.ComponentModel; using System.Drawing; using System.Drawing.Imaging; using Microsoft.Win32; using System.Runtime.InteropServices; using System.Diagnostics; using System.Runtime.Versioning; internal class DataGridParentRows { // siting // private DataGrid dataGrid; // ui // // private Color backColor = DataGrid.defaultParentRowsBackColor; // private Color foreColor = DataGrid.defaultParentRowsForeColor; private SolidBrush backBrush = DataGrid.DefaultParentRowsBackBrush; private SolidBrush foreBrush = DataGrid.DefaultParentRowsForeBrush; private int borderWidth = 1; // private Color borderColor = SystemColors.WindowFrame; private Brush borderBrush = new SolidBrush(SystemColors.WindowFrame); private static Bitmap rightArrow = null; private static Bitmap leftArrow = null; private ColorMap[] colorMap = new ColorMap[] {new ColorMap()}; // private bool gridLineDots = false; // private Color gridLineColor = SystemColors.Control; // private Brush gridLineBrush = SystemBrushes.Control; private Pen gridLinePen = SystemPens.Control; private int totalHeight = 0; private int textRegionHeight = 0; // now that we have left and right arrows, we also have layout private Layout layout = new Layout(); // mouse info // // private bool overLeftArrow = false; // private bool overRightArrow = false; private bool downLeftArrow = false; private bool downRightArrow = false; // a horizOffset of 0 means that the layout for the parent // rows is left aligned. // a horizOffset of 1 means that the leftmost unit of information ( let it be a // table name, a column name or a column value ) is not visible. // a horizOffset of 2 means that the leftmost 2 units of information are not visible, and so on // private int horizOffset = 0; // storage for parent row states // private ArrayList parents = new ArrayList(); private int parentsCount = 0; private ArrayList rowHeights = new ArrayList(); AccessibleObject accessibleObject; internal DataGridParentRows(DataGrid dataGrid) { this.colorMap[0].OldColor = Color.Black; this.dataGrid = dataGrid; // UpdateGridLinePen(); } // =----------------------------------------------------------------- // = Properties // =----------------------------------------------------------------- public AccessibleObject AccessibleObject { get { if (accessibleObject == null) { accessibleObject = new DataGridParentRowsAccessibleObject(this); } return accessibleObject; } } internal Color BackColor { get { return backBrush.Color; } set { if (value.IsEmpty) throw new ArgumentException(SR.GetString(SR.DataGridEmptyColor, "Parent Rows BackColor")); if (value != backBrush.Color) { backBrush = new SolidBrush(value); Invalidate(); } } } internal SolidBrush BackBrush { get { return backBrush; } set { if (value != backBrush) { CheckNull(value, "BackBrush"); backBrush = value; Invalidate(); } } } internal SolidBrush ForeBrush { get { return foreBrush; } set { if (value != foreBrush) { CheckNull(value, "BackBrush"); foreBrush = value; Invalidate(); } } } // since the layout of the parentRows is computed on every paint message, // we can actually return true ClientRectangle coordinates internal Rectangle GetBoundsForDataGridStateAccesibility(DataGridState dgs) { Rectangle ret = Rectangle.Empty; int rectY = 0; for (int i = 0; i < parentsCount; i++) { int height = (int) rowHeights[i]; if (parents[i] == dgs) { ret.X = layout.leftArrow.IsEmpty ? layout.data.X : layout.leftArrow.Right; ret.Height = height; ret.Y = rectY; ret.Width = layout.data.Width; return ret; } rectY += height; } return ret; } /* internal Color BorderColor { get { return borderColor; } set { if (value != borderColor) { borderColor = value; Invalidate(); } } } */ internal Brush BorderBrush { get { return borderBrush; } set { if (value != borderBrush) { borderBrush = value; Invalidate(); } } } /* internal Brush GridLineBrush { get { return gridLineBrush; } set { if (value != gridLineBrush) { gridLineBrush = value; UpdateGridLinePen(); Invalidate(); } } } internal bool GridLineDots { get { return gridLineDots; } set { if (gridLineDots != value) { gridLineDots = value; UpdateGridLinePen(); Invalidate(); } } } */ internal int Height { get { return totalHeight; } } internal Color ForeColor { get { return foreBrush.Color; } set { if (value.IsEmpty) throw new ArgumentException(SR.GetString(SR.DataGridEmptyColor, "Parent Rows ForeColor")); if (value != foreBrush.Color) { foreBrush = new SolidBrush(value); Invalidate(); } } } internal bool Visible { get { return dataGrid.ParentRowsVisible; } set { dataGrid.ParentRowsVisible = value; } } // =------------------------------------------------------------------ // = Methods // =----------------------------------------------------------------- ////// /// Adds a DataGridState object to the top of the list of parents. /// internal void AddParent(DataGridState dgs) { CurrencyManager childDataSource = (CurrencyManager) dataGrid.BindingContext[dgs.DataSource, dgs.DataMember]; parents.Add(dgs); SetParentCount(parentsCount + 1); Debug.Assert(GetTopParent() != null, "we should have a parent at least"); } internal void Clear() { for (int i = 0; i < parents.Count; i++) { DataGridState dgs = parents[i] as DataGridState; dgs.RemoveChangeNotification(); } parents.Clear(); rowHeights.Clear(); totalHeight = 0; SetParentCount(0); } internal void SetParentCount(int count) { parentsCount = count; dataGrid.Caption.BackButtonVisible = (parentsCount > 0) && (dataGrid.AllowNavigation); } internal void CheckNull(object value, string propName) { if (value == null) throw new ArgumentNullException("propName"); } internal void Dispose() { gridLinePen.Dispose(); } ////// /// Retrieves the top most parent in the list of parents. /// internal DataGridState GetTopParent() { if (parentsCount < 1) { return null; } return(DataGridState)(((ICloneable)(parents[parentsCount-1])).Clone()); } ////// /// Determines if there are any parent rows contained in this object. /// internal bool IsEmpty() { return parentsCount == 0; } ////// /// Similar to GetTopParent() but also removes it. /// internal DataGridState PopTop() { if (parentsCount < 1) { return null; } SetParentCount(parentsCount - 1); DataGridState ret = (DataGridState)parents[parentsCount]; ret.RemoveChangeNotification(); parents.RemoveAt(parentsCount); return ret; } internal void Invalidate() { if (dataGrid != null) dataGrid.InvalidateParentRows(); } internal void InvalidateRect(Rectangle rect) { if (dataGrid != null) { Rectangle r = new Rectangle(rect.X, rect.Y, rect.Width + borderWidth, rect.Height + borderWidth); dataGrid.InvalidateParentRowsRect(r); } } // called from DataGrid::OnLayout internal void OnLayout() { if (parentsCount == rowHeights.Count) return; int height = 0; if (totalHeight == 0) { totalHeight += 2 * borderWidth; } // figure out how tall each row's text will be // textRegionHeight = (int)dataGrid.Font.Height + 2; // make the height of the Column.Font count for the height // of the parentRows; // // if the user wants to programatically // navigate to a relation in the constructor of the form // ( ie, when the form does not process PerformLayout ) // the grid will receive an OnLayout message when there is more // than one parent in the grid if (parentsCount > rowHeights.Count) { Debug.Assert(parentsCount == rowHeights.Count + 1 || rowHeights.Count == 0, "see bug 82808 for more info, or the comment above"); int rowHeightsCount = this.rowHeights.Count; for (int i = rowHeightsCount; i < parentsCount; i++) { DataGridState dgs = (DataGridState) parents[i]; GridColumnStylesCollection cols = dgs.GridColumnStyles; int colsHeight = 0; for (int j=0; j0) { ResetMouseInfo(); horizOffset -= 1; Invalidate(); } else { ResetMouseInfo(); InvalidateRect(layout.leftArrow); } } private void RightArrowClick(int cellCount) { if (horizOffset < cellCount - 1) { ResetMouseInfo(); horizOffset += 1; Invalidate(); } else { ResetMouseInfo(); InvalidateRect(layout.rightArrow); } } // the only mouse clicks that are handled are // the mouse clicks on the LeftArrow and RightArrow // internal void OnMouseDown(int x, int y, bool alignToRight) { if (layout.rightArrow.IsEmpty) { Debug.Assert(layout.leftArrow.IsEmpty, "we can't have the leftArrow w/o the rightArrow"); return; } int cellCount = CellCount(); if (layout.rightArrow.Contains(x,y)) { // draw a nice sunken border around the right arrow area // we want to keep a cell on the screen downRightArrow = true; if (alignToRight) LeftArrowClick(cellCount); else RightArrowClick(cellCount); } else if (layout.leftArrow.Contains(x,y)) { downLeftArrow = true; if (alignToRight) RightArrowClick(cellCount); else LeftArrowClick(cellCount); } else { if (downLeftArrow) { downLeftArrow = false; InvalidateRect(layout.leftArrow); } if (downRightArrow) { downRightArrow = false; InvalidateRect(layout.rightArrow); } } } internal void OnMouseLeave() { if (downLeftArrow) { downLeftArrow = false; InvalidateRect(layout.leftArrow); } if (downRightArrow) { downRightArrow = false; InvalidateRect(layout.rightArrow); } } internal void OnMouseMove(int x, int y) { /* if (!layout.leftArrow.IsEmpty && layout.leftArrow.Contains(x,y)) { ResetMouseInfo(); overLeftArrow = true; InvalidateRect(layout.leftArrow); return; } if (!layout.rightArrow.IsEmpty && layout.rightArrow.Contains(x,y)) { ResetMouseInfo(); overRightArrow = true; InvalidateRect(layout.rightArrow); return; } */ if (downLeftArrow) { downLeftArrow = false; InvalidateRect(layout.leftArrow); } if (downRightArrow) { downRightArrow = false; InvalidateRect(layout.rightArrow); } } internal void OnMouseUp(int x, int y) { ResetMouseInfo(); if (!layout.rightArrow.IsEmpty && layout.rightArrow.Contains(x,y)) { InvalidateRect(layout.rightArrow); return; } if (!layout.leftArrow.IsEmpty && layout.leftArrow.Contains(x,y)) { InvalidateRect(layout.leftArrow); return; } } internal void OnResize(Rectangle oldBounds) { Invalidate(); } /// /// /// Paints the parent rows /// internal void Paint(Graphics g, Rectangle visualbounds, bool alignRight) { Rectangle bounds = visualbounds; // Paint the border around our bounds if (borderWidth > 0) { PaintBorder(g, bounds); bounds.Inflate(-borderWidth, -borderWidth); } PaintParentRows(g, bounds, alignRight); } private void PaintBorder(Graphics g, Rectangle bounds) { Rectangle border = bounds; // top border.Height = borderWidth; g.FillRectangle(borderBrush, border); // bottom border.Y = bounds.Bottom - borderWidth; g.FillRectangle(borderBrush, border); // left border = new Rectangle(bounds.X, bounds.Y + borderWidth, borderWidth, bounds.Height - 2*borderWidth); g.FillRectangle(borderBrush, border); // right border.X = bounds.Right - borderWidth; g.FillRectangle(borderBrush, border); } // will return the width of the text box that will fit all the // tables names private int GetTableBoxWidth(Graphics g, Font font) { // try to make the font BOLD Font textFont = font; try { textFont = new Font(font, FontStyle.Bold); } catch { } int width = 0; for (int row = 0; row < parentsCount; row ++) { DataGridState dgs = (DataGridState) parents[row]; // Graphics.MeasureString(...) returns different results for ": " than for " :" // string displayTableName = dgs.ListManager.GetListName() + " :"; int size = (int) g.MeasureString(displayTableName, textFont).Width; width = Math.Max(size, width); } return width; } // will return the width of the text box that will // fit all the column names private int GetColBoxWidth(Graphics g, Font font, int colNum) { int width = 0; for (int row = 0; row < parentsCount; row ++) { DataGridState dgs = (DataGridState) parents[row]; GridColumnStylesCollection columns = dgs.GridColumnStyles; if (colNum < columns.Count) { // Graphics.MeasureString(...) returns different results for ": " than for " :" // string colName = columns[colNum].HeaderText + " :"; int size = (int) g.MeasureString(colName, font).Width; width = Math.Max(size, width); } } return width; } // will return the width of the best fit for the column // private int GetColDataBoxWidth(Graphics g, int colNum) { int width = 0; for (int row = 0; row < parentsCount; row ++) { DataGridState dgs = (DataGridState) parents[row]; GridColumnStylesCollection columns = dgs.GridColumnStyles; if (colNum < columns.Count) { object value = columns[colNum].GetColumnValueAtRow((CurrencyManager) dataGrid.BindingContext[dgs.DataSource, dgs.DataMember], dgs.LinkingRow.RowNumber); int size = columns[colNum].GetPreferredSize(g, value).Width; width = Math.Max(size, width); } } return width; } // will return the count of the table with the largest number of columns private int ColsCount() { int colNum = 0; for (int row = 0; row < parentsCount; row ++) { DataGridState dgs = (DataGridState) parents[row]; colNum = Math.Max(colNum, dgs.GridColumnStyles.Count); } return colNum; } // will return the total width required to paint the parentRows private int TotalWidth(int tableNameBoxWidth, int[] colsNameWidths, int[] colsDataWidths) { int totalWidth = 0; totalWidth += tableNameBoxWidth; Debug.Assert(colsNameWidths.Length == colsDataWidths.Length, "both arrays are as long as the largest column count in dgs"); for (int i = 0; i < colsNameWidths.Length; i ++) { totalWidth += colsNameWidths[i]; totalWidth += colsDataWidths[i]; } // let 3 pixels in between datacolumns // see DonnaWa totalWidth += 3 * (colsNameWidths.Length - 1); return totalWidth; } // computes the layout for the parent rows // private void ComputeLayout(Rectangle bounds, int tableNameBoxWidth, int[] colsNameWidths, int[] colsDataWidths) { int totalWidth = TotalWidth(tableNameBoxWidth, colsNameWidths, colsDataWidths); if (totalWidth > bounds.Width) { layout.leftArrow = new Rectangle(bounds.X, bounds.Y, 15, bounds.Height); layout.data = new Rectangle(layout.leftArrow.Right, bounds.Y, bounds.Width - 30, bounds.Height); layout.rightArrow = new Rectangle(layout.data.Right, bounds.Y, 15, bounds.Height); } else { layout.data = bounds; layout.leftArrow = Rectangle.Empty; layout.rightArrow = Rectangle.Empty; } } private void PaintParentRows(Graphics g, Rectangle bounds, bool alignToRight) { // variables needed for aligning the table and column names int tableNameBoxWidth = 0; int numCols = ColsCount(); int[] colsNameWidths = new int[numCols]; int[] colsDataWidths = new int[numCols]; // compute the size of the box that will contain the tableName // if (dataGrid.ParentRowsLabelStyle == DataGridParentRowsLabelStyle.TableName || dataGrid.ParentRowsLabelStyle == DataGridParentRowsLabelStyle.Both) { tableNameBoxWidth = GetTableBoxWidth(g, dataGrid.Font); } // initialiaze the arrays that contain the column names and the column size // for (int i = 0; i < numCols; i++) { if (dataGrid.ParentRowsLabelStyle == DataGridParentRowsLabelStyle.ColumnName || dataGrid.ParentRowsLabelStyle == DataGridParentRowsLabelStyle.Both) { colsNameWidths[i] = GetColBoxWidth(g, dataGrid.Font, i); } else { colsNameWidths[i] = 0; } colsDataWidths[i] = GetColDataBoxWidth(g, i); } // compute the layout // ComputeLayout(bounds, tableNameBoxWidth, colsNameWidths, colsDataWidths); // paint the navigation arrows, if necessary // if (!layout.leftArrow.IsEmpty) { g.FillRectangle(BackBrush, layout.leftArrow); PaintLeftArrow(g, layout.leftArrow, alignToRight); } // paint the parent rows: // Rectangle rowBounds = layout.data; for (int row = 0; row < parentsCount; ++row) { rowBounds.Height = (int) rowHeights[row]; if (rowBounds.Y > bounds.Bottom) break; int paintedWidth = PaintRow(g, rowBounds, row, dataGrid.Font, alignToRight, tableNameBoxWidth, colsNameWidths, colsDataWidths); if (row == parentsCount-1) break; // draw the grid line below g.DrawLine(gridLinePen, rowBounds.X, rowBounds.Bottom, rowBounds.X + paintedWidth, rowBounds.Bottom); rowBounds.Y += rowBounds.Height; } if (!layout.rightArrow.IsEmpty) { g.FillRectangle(BackBrush, layout.rightArrow); PaintRightArrow(g, layout.rightArrow, alignToRight); } } [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] private Bitmap GetBitmap(string bitmapName, Color transparentColor) { Bitmap b = null; try { b = new Bitmap(typeof(DataGridParentRows), bitmapName); b.MakeTransparent(transparentColor); } catch (Exception e) { Debug.Fail("Failed to load bitmap: " + bitmapName, e.ToString()); } return b; } [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] private Bitmap GetRightArrowBitmap() { if (rightArrow == null) rightArrow = GetBitmap("DataGridParentRows.RightArrow.bmp", Color.White); return rightArrow; } [ResourceExposure(ResourceScope.Machine)] [ResourceConsumption(ResourceScope.Machine)] private Bitmap GetLeftArrowBitmap() { if (leftArrow == null) leftArrow = GetBitmap("DataGridParentRows.LeftArrow.bmp", Color.White); return leftArrow; } private void PaintBitmap(Graphics g, Bitmap b, Rectangle bounds) { // center the bitmap in the bounds: int bmpX = bounds.X + (bounds.Width - b.Width) / 2; int bmpY = bounds.Y + (bounds.Height - b.Height) / 2; Rectangle bmpRect = new Rectangle(bmpX, bmpY, b.Width, b.Height); g.FillRectangle(BackBrush, bmpRect); // now draw the bitmap ImageAttributes attr = new ImageAttributes(); this.colorMap[0].NewColor = this.ForeColor; attr.SetRemapTable(colorMap, ColorAdjustType.Bitmap); g.DrawImage(b, bmpRect, 0, 0, bmpRect.Width, bmpRect.Height,GraphicsUnit.Pixel, attr); attr.Dispose(); } /* private void PaintOverButton(Graphics g, Rectangle bounds) { } */ private void PaintDownButton(Graphics g, Rectangle bounds) { g.DrawLine(Pens.Black, bounds.X, bounds.Y, bounds.X + bounds.Width, bounds.Y); // the top g.DrawLine(Pens.White, bounds.X + bounds.Width, bounds.Y, bounds.X + bounds.Width, bounds.Y + bounds.Height); // the right side g.DrawLine(Pens.White, bounds.X + bounds.Width, bounds.Y + bounds.Height, bounds.X, bounds.Y + bounds.Height); // the right side g.DrawLine(Pens.Black, bounds.X, bounds.Y + bounds.Height, bounds.X, bounds.Y); // the left side } private void PaintLeftArrow(Graphics g, Rectangle bounds, bool alignToRight) { Bitmap bmp = GetLeftArrowBitmap(); // paint the border around this bitmap if this is the case // /* if (overLeftArrow) { Debug.Assert(!downLeftArrow, "can both of those happen?"); PaintOverButton(g, bounds); layout.leftArrow.Inflate(-1,-1); } */ if (downLeftArrow) { PaintDownButton(g, bounds); layout.leftArrow.Inflate(-1,-1); lock (bmp) { PaintBitmap(g, bmp, bounds); } layout.leftArrow.Inflate(1,1); } else { lock (bmp) { PaintBitmap(g, bmp, bounds); } } } private void PaintRightArrow(Graphics g, Rectangle bounds, bool alignToRight) { Bitmap bmp = GetRightArrowBitmap(); // paint the border around this bitmap if this is the case // /* if (overRightArrow) { Debug.Assert(!downRightArrow, "can both of those happen?"); PaintOverButton(g, bounds); layout.rightArrow.Inflate(-1,-1); } */ if (downRightArrow) { PaintDownButton(g, bounds); layout.rightArrow.Inflate(-1,-1); lock (bmp) { PaintBitmap(g, bmp, bounds); } layout.rightArrow.Inflate(1,1); } else { lock (bmp) { PaintBitmap(g, bmp, bounds); } } } private int PaintRow(Graphics g, Rectangle bounds, int row, Font font, bool alignToRight, int tableNameBoxWidth, int[] colsNameWidths, int[] colsDataWidths) { DataGridState dgs = (DataGridState) parents[row]; Rectangle paintBounds = bounds; Rectangle rowBounds = bounds; paintBounds.Height = (int) rowHeights[row]; rowBounds.Height = (int) rowHeights[row]; int paintedWidth = 0; // used for scrolling: when paiting, we will skip horizOffset cells in the dataGrid ParentRows int skippedCells = 0; // paint the table name if ( dataGrid.ParentRowsLabelStyle == DataGridParentRowsLabelStyle.TableName || dataGrid.ParentRowsLabelStyle == DataGridParentRowsLabelStyle.Both) { if (skippedCells < horizOffset) { // skip this skippedCells ++; } else { paintBounds.Width = Math.Min(paintBounds.Width, tableNameBoxWidth); paintBounds.X = MirrorRect(bounds, paintBounds, alignToRight); string displayTableName = dgs.ListManager.GetListName() + ": "; PaintText(g, paintBounds, displayTableName, font, true, alignToRight); // true is for painting bold paintedWidth += paintBounds.Width; } } if (paintedWidth >= bounds.Width) return bounds.Width; // we painted everything rowBounds.Width -= paintedWidth; rowBounds.X += alignToRight ? 0 : paintedWidth; paintedWidth += PaintColumns(g, rowBounds, dgs, font, alignToRight, colsNameWidths, colsDataWidths, skippedCells); // paint the possible space left after columns if (paintedWidth < bounds.Width) { paintBounds.X = bounds.X + paintedWidth; paintBounds.Width = bounds.Width - paintedWidth; paintBounds.X = MirrorRect(bounds, paintBounds, alignToRight); g.FillRectangle(BackBrush, paintBounds); } return paintedWidth; } private int PaintColumns(Graphics g, Rectangle bounds, DataGridState dgs, Font font, bool alignToRight, int[] colsNameWidths, int[] colsDataWidths, int skippedCells) { Rectangle paintBounds = bounds; Rectangle rowBounds = bounds; GridColumnStylesCollection cols = dgs.GridColumnStyles; int cx = 0; for (int i = 0; i < cols.Count; i ++) { if (cx >= bounds.Width) break; // paint the column name, if we have to if (dataGrid.ParentRowsLabelStyle == DataGridParentRowsLabelStyle.ColumnName || dataGrid.ParentRowsLabelStyle == DataGridParentRowsLabelStyle.Both) { if (skippedCells < horizOffset) { // skip this column } else { paintBounds.X = bounds.X + cx; paintBounds.Width = Math.Min(bounds.Width - cx, colsNameWidths[i]); paintBounds.X = MirrorRect(bounds, paintBounds, alignToRight); string colName = cols[i].HeaderText + ": "; PaintText(g, paintBounds, colName, font, false, alignToRight); // false is for not painting bold cx += paintBounds.Width; } } if (cx >= bounds.Width) break; if (skippedCells < horizOffset) { // skip this cell skippedCells ++; } else { // paint the cell contents paintBounds.X = bounds.X + cx; paintBounds.Width = Math.Min(bounds.Width - cx, colsDataWidths[i]); paintBounds.X = MirrorRect(bounds, paintBounds, alignToRight); // when we paint the data grid parent rows, we want to paint the data at the position // stored in the currency manager. cols[i].Paint(g, paintBounds, (CurrencyManager) dataGrid.BindingContext[dgs.DataSource, dgs.DataMember], dataGrid.BindingContext[dgs.DataSource, dgs.DataMember].Position, BackBrush, ForeBrush, alignToRight); cx += paintBounds.Width; // draw the line to the right (or left, according to alignRight) // g.DrawLine(new Pen(SystemColors.ControlDark), alignToRight ? paintBounds.X : paintBounds.Right, paintBounds.Y, alignToRight ? paintBounds.X : paintBounds.Right, paintBounds.Bottom); // this is how wide the line is.... cx++; // put 3 pixels in between columns // see DonnaWa // if ( i < cols.Count - 1) { paintBounds.X = bounds.X + cx; paintBounds.Width = Math.Min(bounds.Width - cx, 3); paintBounds.X = MirrorRect(bounds, paintBounds, alignToRight); g.FillRectangle(BackBrush, paintBounds); cx += 3; } } } return cx; } ////// /// Draws on the screen the text. It is used only to paint the Table Name and the column Names /// Returns the width of bounding rectangle that was passed in /// private int PaintText(Graphics g, Rectangle textBounds, string text, Font font, bool bold, bool alignToRight) { Font textFont = font; if (bold) try { textFont = new Font(font, FontStyle.Bold); } catch {} else textFont = font; // right now, we paint the entire box, cause it will be used anyway g.FillRectangle(BackBrush, textBounds); StringFormat format = new StringFormat(); if (alignToRight) { format.FormatFlags |= StringFormatFlags.DirectionRightToLeft; format.Alignment = StringAlignment.Far; } format.FormatFlags |= StringFormatFlags.NoWrap; // part 1, section 3: put the table and the column name in the // parent rows at the same height as the dataGridTextBoxColumn draws the string // textBounds.Offset(0, 2); textBounds.Height -= 2; g.DrawString(text, textFont, ForeBrush, textBounds, format); format.Dispose(); return textBounds.Width; } // will return the X coordinate of the containedRect mirrored within the surroundingRect // according to the value of alignToRight private int MirrorRect(Rectangle surroundingRect, Rectangle containedRect, bool alignToRight) { Debug.Assert(containedRect.X >= surroundingRect.X && containedRect.Right <= surroundingRect.Right, "containedRect is not contained in surroundingRect"); if (alignToRight) return surroundingRect.Right - containedRect.Right + surroundingRect.X; else return containedRect.X; } private class Layout { public Rectangle data; public Rectangle leftArrow; public Rectangle rightArrow; public Layout() { data = Rectangle.Empty; leftArrow = Rectangle.Empty; rightArrow = Rectangle.Empty; } public override string ToString() { StringBuilder sb = new StringBuilder(200); sb.Append("ParentRows Layout: \n"); sb.Append("data = "); sb.Append(data.ToString()); sb.Append("\n leftArrow = "); sb.Append(leftArrow.ToString()); sb.Append("\n rightArrow = "); sb.Append(rightArrow.ToString()); sb.Append("\n"); return sb.ToString(); } } [ComVisible(true)] protected internal class DataGridParentRowsAccessibleObject : AccessibleObject { DataGridParentRows owner = null; public DataGridParentRowsAccessibleObject(DataGridParentRows owner) : base() { Debug.Assert(owner != null, "DataGridParentRowsAccessibleObject must have a valid owner"); this.owner = owner; } internal DataGridParentRows Owner { get { return owner; } } public override Rectangle Bounds { get { return owner.dataGrid.RectangleToScreen(owner.dataGrid.ParentRowsBounds); } } public override string DefaultAction { get { return SR.GetString(SR.AccDGNavigateBack); } } public override string Name { get { return SR.GetString(SR.AccDGParentRows); } } public override AccessibleObject Parent { [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] get { return owner.dataGrid.AccessibilityObject; } } public override AccessibleRole Role { get { return AccessibleRole.List; } } public override AccessibleStates State { get { AccessibleStates state = AccessibleStates.ReadOnly; if (owner.parentsCount == 0) { state |= AccessibleStates.Invisible; } if (owner.dataGrid.ParentRowsVisible) { state |= AccessibleStates.Expanded; } else { state |= AccessibleStates.Collapsed; } return state; } } public override string Value { [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] get { return null; } } [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] public override void DoDefaultAction() { owner.dataGrid.NavigateBack(); } public override AccessibleObject GetChild(int index) { return ((DataGridState)owner.parents[index]).ParentRowAccessibleObject; } public override int GetChildCount() { return owner.parentsCount; } ////// /// Returns the currently focused child, if any. /// Returns this if the object itself is focused. /// public override AccessibleObject GetFocused() { return null; } internal AccessibleObject GetNext(AccessibleObject child) { int children = GetChildCount(); bool hit = false; for (int i=0; i=0; i--) { if (hit) { return GetChild(i); } if (GetChild(i) == child) { hit = true; } } return null; } /// /// /// Navigate to the next or previous grid entry. /// [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] public override AccessibleObject Navigate(AccessibleNavigation navdir) { switch (navdir) { case AccessibleNavigation.Right: case AccessibleNavigation.Next: case AccessibleNavigation.Down: return Parent.GetChild(1); case AccessibleNavigation.Up: case AccessibleNavigation.Left: case AccessibleNavigation.Previous: return Parent.GetChild(GetChildCount() - 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) { } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- BuildManager.cs
- SqlNotificationRequest.cs
- SimpleTextLine.cs
- CorrelationQuery.cs
- X509ClientCertificateAuthenticationElement.cs
- WebPartHelpVerb.cs
- DbConnectionPoolIdentity.cs
- StringUtil.cs
- SafeNativeMethods.cs
- ScrollContentPresenter.cs
- WebPartZoneAutoFormat.cs
- Rectangle.cs
- OptionUsage.cs
- BulletDecorator.cs
- MediaEntryAttribute.cs
- UnauthorizedWebPart.cs
- StreamUpdate.cs
- JsonObjectDataContract.cs
- UrlPath.cs
- WebResourceUtil.cs
- AssertHelper.cs
- XmlCountingReader.cs
- SqlLiftWhereClauses.cs
- DataSet.cs
- RealizationDrawingContextWalker.cs
- UnmanagedBitmapWrapper.cs
- XmlSchemaComplexContent.cs
- LookupBindingPropertiesAttribute.cs
- ParameterToken.cs
- WebPartDisplayModeCancelEventArgs.cs
- HttpListenerResponse.cs
- DetailsViewUpdateEventArgs.cs
- precedingsibling.cs
- MachineKey.cs
- DependencyObject.cs
- ViewStateException.cs
- SqlStream.cs
- DocumentGridPage.cs
- DataGridViewColumnDividerDoubleClickEventArgs.cs
- HtmlInputReset.cs
- SourceSwitch.cs
- KeyEvent.cs
- AssemblyAttributesGoHere.cs
- EndEvent.cs
- OrderByBuilder.cs
- ChainedAsyncResult.cs
- SubordinateTransaction.cs
- CodeNamespace.cs
- Privilege.cs
- DropShadowBitmapEffect.cs
- XmlSchemaComplexContentRestriction.cs
- XmlElementList.cs
- StsCommunicationException.cs
- DataStorage.cs
- Marshal.cs
- AttachedPropertyBrowsableAttribute.cs
- AudioBase.cs
- DefaultTextStore.cs
- DecimalFormatter.cs
- MatrixStack.cs
- TraceInternal.cs
- BoundColumn.cs
- CompilerParameters.cs
- UriSection.cs
- TemplateXamlParser.cs
- HashStream.cs
- XmlLinkedNode.cs
- BuildTopDownAttribute.cs
- ReadOnlyDictionary.cs
- DefaultWorkflowSchedulerService.cs
- AutomationPatternInfo.cs
- ValueSerializer.cs
- COAUTHINFO.cs
- IPEndPoint.cs
- MultipleCopiesCollection.cs
- LabelLiteral.cs
- ListDesigner.cs
- SafeCertificateContext.cs
- ArglessEventHandlerProxy.cs
- TypefaceCollection.cs
- TextCollapsingProperties.cs
- EventLogger.cs
- BamlBinaryWriter.cs
- BaseEntityWrapper.cs
- CertificateReferenceElement.cs
- XmlAttributeAttribute.cs
- MarkupCompiler.cs
- RoutedEventHandlerInfo.cs
- SerializationFieldInfo.cs
- CodeMemberMethod.cs
- ExceptionNotification.cs
- WindowsStartMenu.cs
- ChtmlTextWriter.cs
- SystemIcons.cs
- ErrorTolerantObjectWriter.cs
- ButtonFieldBase.cs
- HostingEnvironmentSection.cs
- ContentElementAutomationPeer.cs
- TypedRowHandler.cs
- PolyBezierSegmentFigureLogic.cs