public static bool Compare(IndentBitmapData operand1, IndentBitmapData operand2) { Debug.Assert(false); // There is no need to compare these return(false); }
private Bitmap CreateIndentBmp(Color backColor, Color foreColor, int xExtent, int xMid, int yExtent) { var buttons = HasButtons; var lines = HasLines; var rootLines = HasRootLines; var rootButtons = HasRootButtons; var oddItemHeight = (yExtent & 1) == 1; int rows; int columns; if (lines) { // The bitmap is laid out with one row for no/plus/minus buttons. // There are three columns for normal lines, with a fourth and // fifth added if root lines are also wanted. columns = (rootLines || rootButtons) ? 5 : 3; rows = buttons ? 3 : 1; } else if (buttons) { // Layout in a column to facilitate the same calculations as // with the fuller bitmap. columns = 1; rows = 2; } else { return(null); } if (lines && oddItemHeight) { // in the odd item height case, we double the number of columns. This is because there are two copies of each bitmap, // identical except dot pattern in the dotted line is reversed. We paint these on alternating rows in the tree, to // keep the vertical lines looking good. columns *= 2; } var buttonExtent = 0; // 1/2 the size of a button // yMid must be even for the indent bitmaps to draw correctly. Horizontal gridlines occupy a pixel a the // bottom of each item, so take this into account. var yMid = ((yExtent / 2) + (HasHorizontalGridLines ? 0 : 1)) & ~1; if (buttons) { buttonExtent = Math.Min(yMid, xMid) / 2; } Bitmap returnBmp = null; Bitmap bmp = null; Graphics graphics = null; Brush backgroundBrush = null; Pen linePen = null; Pen boxPen = null; Brush plusMinusBrush = null; var themeHandle = IntPtr.Zero; var data = new IndentBitmapData(); try { // themeHandle is not used when the tree expander is derived from VS theme. if (!UseVSThemeForTreeExpander) { // see if XP themes are available to draw the buttons. themeHandle = OpenTheme(Handle, EnableExplorerTheme); // we've already asserted handle creation at this point } data = GetIndentBitmapData(backColor, buttons && themeHandle == IntPtr.Zero, buttonExtent); var plusPath = data.PlusPath; var minusPath = data.MinusPath; var boxPath = data.BoxPath; bmp = new Bitmap(columns * xExtent, rows * yExtent); graphics = Graphics.FromImage(bmp); // Fill using a transparent brush EnsureBrush(ref backgroundBrush, data.BackgroundColor); graphics.FillRectangle(backgroundBrush, 0, 0, bmp.Width, bmp.Height); graphics.CompositingMode = CompositingMode.SourceCopy; if (!lines) { // Draw the two buttons and get out graphics.TranslateTransform(xMid, yMid); EnsureBrush(ref plusMinusBrush, data.PlusMinusColor); EnsurePen(ref boxPen, data.BoxColor); if (UseVSThemeForTreeExpander) { DrawTreeExpanderPolygon(graphics, foreColor, data, false /*expanded*/); } else if (themeHandle != IntPtr.Zero) { DrawThemedButtonGlyph(themeHandle, graphics, data.BackgroundColor, buttonExtent, false /* expanded */); } else { if (plusPath != null) { graphics.FillPath(plusMinusBrush, plusPath); } if (boxPath != null) { graphics.DrawPath(boxPen, boxPath); } } graphics.TranslateTransform(0, yExtent); if (UseVSThemeForTreeExpander) { DrawTreeExpanderPolygon(graphics, foreColor, data, true /*expanded*/); } else if (themeHandle != IntPtr.Zero) { DrawThemedButtonGlyph(themeHandle, graphics, data.BackgroundColor, buttonExtent, true /* expanded */); } else { if (minusPath != null) { graphics.FillPath(plusMinusBrush, minusPath); } if (boxPath != null) { graphics.DrawPath(boxPen, boxPath); } } returnBmp = bmp; bmp = null; return(returnBmp); } // We always create this pen to support the different dash styles linePen = new Pen(data.LineColor); linePen.DashStyle = data.LineStyle; var oddItemOffset = 0; var finaloddItemOffset = (oddItemHeight && lines) ? 1 : 0; // loop through twice if we have an odd item height, since we need two sets of bitmaps to draw the lines smoothly. while (oddItemOffset <= finaloddItemOffset) { // don't use GraphicsContainers here because GDI drawing (used for themes) doesn't respect the transform matrix. // BeginContainer resets the transform matrix, so there's no way to get the information we need to draw the themed // buttons in the right place. graphics.ResetTransform(); graphics.TranslateTransform(oddItemOffset * (bmp.Width / 2), 0); // offset for odd item heights // Draw the first row (no buttons) var beginTransform = graphics.Transform; graphics.DrawLine(linePen, xMid, oddItemOffset, xMid, yExtent); graphics.TranslateTransform(xExtent, 0); graphics.DrawLine(linePen, xMid, oddItemOffset, xMid, yExtent); // add 2*oddItemOffset below because this is an intersection of horizontal and vertical gridlines. // In the odd height case, not adding an offset creates an ugly pixel cluster, and only adding 1 // causes the ends of the lines to be jagged. Adding 2 creates a bit of extra whitespace, but I believe // it is visually the best compromise. graphics.DrawLine(linePen, xMid + (2 * oddItemOffset), yMid, xExtent, yMid); graphics.TranslateTransform(xExtent, 0); graphics.DrawLine(linePen, xMid, oddItemOffset, xMid, yMid); graphics.DrawLine(linePen, xMid + (2 * oddItemOffset), yMid, xExtent, yMid); // see comment about 2*oddItemOffset above. if (rootLines) { graphics.TranslateTransform(xExtent, 0); graphics.DrawLine(linePen, xMid, yMid + oddItemOffset, xMid, yExtent); graphics.DrawLine(linePen, xMid + (2 * oddItemOffset), yMid, xExtent, yMid); // see comment about 2*oddItemOffset above. graphics.TranslateTransform(xExtent, 0); graphics.DrawLine(linePen, xMid, yMid, xExtent, yMid); } graphics.Transform = beginTransform; // Back to beginning of line if (buttons) { // Draw the second and third rows (plus and minus signs) // These are the same except for the sign in the button, so // use the same code with a different path EnsureBrush(ref plusMinusBrush, data.PlusMinusColor); EnsurePen(ref boxPen, data.BoxColor); for (var i = 0; i < 2; i++) { var curPath = (i == 0) ? plusPath : minusPath; graphics.TranslateTransform(0, yExtent); beginTransform = graphics.Transform; // First column is the same in all three rows graphics.DrawLine(linePen, xMid, oddItemOffset, xMid, yExtent); graphics.TranslateTransform(xExtent + xMid, yMid); graphics.DrawLine(linePen, 0, -yMid + oddItemOffset, 0, -buttonExtent); graphics.DrawLine(linePen, 0, ((buttonExtent + 1) & ~1) + oddItemOffset, 0, yExtent - yMid); graphics.DrawLine(linePen, buttonExtent, 0, xExtent - xMid, 0); if (UseVSThemeForTreeExpander) { DrawTreeExpanderPolygon(graphics, foreColor, data, i == 1 /*expanded*/); } else if (themeHandle != IntPtr.Zero) { DrawThemedButtonGlyph(themeHandle, graphics, data.BackgroundColor, buttonExtent, i == 1 /* expanded */); } else { if (curPath != null) { graphics.FillPath(plusMinusBrush, curPath); } if (boxPath != null) { graphics.DrawPath(boxPen, boxPath); } } graphics.TranslateTransform(xExtent, 0); if (UseVSThemeForTreeExpander) { DrawTreeExpanderPolygon(graphics, foreColor, data, i == 1 /*expanded*/); } else if (themeHandle != IntPtr.Zero) { DrawThemedButtonGlyph(themeHandle, graphics, data.BackgroundColor, buttonExtent, i == 1 /* expanded */); } else { if (curPath != null) { graphics.FillPath(plusMinusBrush, curPath); } if (boxPath != null) { graphics.DrawPath(boxPen, boxPath); } } graphics.DrawLine(linePen, 0, -yMid + oddItemOffset, 0, -buttonExtent); graphics.DrawLine(linePen, buttonExtent, 0, xExtent - xMid, 0); if (rootButtons) { graphics.TranslateTransform(xExtent, 0); if (UseVSThemeForTreeExpander) { DrawTreeExpanderPolygon(graphics, foreColor, data, i == 1 /*expanded*/); } else if (themeHandle != IntPtr.Zero) { DrawThemedButtonGlyph(themeHandle, graphics, data.BackgroundColor, buttonExtent, i == 1 /* expanded */); } else { if (curPath != null) { graphics.FillPath(plusMinusBrush, curPath); } if (boxPath != null) { graphics.DrawPath(boxPen, boxPath); } } graphics.DrawLine(linePen, 0, ((buttonExtent + 1) & ~1) + oddItemOffset, 0, yExtent - yMid); graphics.DrawLine(linePen, buttonExtent, 0, xExtent - xMid, 0); graphics.TranslateTransform(xExtent, 0); graphics.DrawLine(linePen, buttonExtent, 0, xExtent - xMid, 0); if (UseVSThemeForTreeExpander) { DrawTreeExpanderPolygon(graphics, foreColor, data, i == 1 /*expanded*/); } else if (themeHandle != IntPtr.Zero) { DrawThemedButtonGlyph(themeHandle, graphics, data.BackgroundColor, buttonExtent, i == 1 /* expanded */); } else { if (curPath != null) { graphics.FillPath(plusMinusBrush, curPath); } if (boxPath != null) { graphics.DrawPath(boxPen, boxPath); } } } graphics.Transform = beginTransform; // Back to beginning of line } } oddItemOffset++; } returnBmp = bmp; bmp = null; } finally { if (themeHandle != IntPtr.Zero) { CloseTheme(themeHandle); } if (graphics != null) { graphics.Dispose(); } if (bmp != null) { bmp.Dispose(); } CleanBrush(ref backgroundBrush, data.BackgroundColor); CleanPen(ref boxPen, data.BoxColor); CleanBrush(ref plusMinusBrush, data.PlusMinusColor); if (linePen != null) { linePen.Dispose(); } data.Dispose(); } return(returnBmp); }
/// <summary> /// Draws the tree expander polygon. /// </summary> private static void DrawTreeExpanderPolygon(Graphics graphics, Color foreColor, IndentBitmapData data, bool expanded) { Brush brush = null; Pen pen = null; try { if (expanded) { EnsureBrush(ref brush, foreColor); graphics.FillPolygon(brush, data.ExpandedIconPoints); } else { EnsurePen(ref pen, foreColor); graphics.DrawPolygon(pen, data.UnexpandedIconPoints); } } finally { CleanPen(ref pen, foreColor); CleanBrush(ref brush, foreColor); } }
private Bitmap CreateIndentBmp(Color backColor, Color foreColor, int xExtent, int xMid, int yExtent) { var buttons = HasButtons; var lines = HasLines; var rootLines = HasRootLines; var rootButtons = HasRootButtons; var oddItemHeight = (yExtent & 1) == 1; int rows; int columns; if (lines) { // The bitmap is laid out with one row for no/plus/minus buttons. // There are three columns for normal lines, with a fourth and // fifth added if root lines are also wanted. columns = (rootLines || rootButtons) ? 5 : 3; rows = buttons ? 3 : 1; } else if (buttons) { // Layout in a column to facilitate the same calculations as // with the fuller bitmap. columns = 1; rows = 2; } else { return null; } if (lines && oddItemHeight) { // in the odd item height case, we double the number of columns. This is because there are two copies of each bitmap, // identical except dot pattern in the dotted line is reversed. We paint these on alternating rows in the tree, to // keep the vertical lines looking good. columns *= 2; } var buttonExtent = 0; // 1/2 the size of a button // yMid must be even for the indent bitmaps to draw correctly. Horizontal gridlines occupy a pixel a the // bottom of each item, so take this into account. var yMid = ((yExtent / 2) + (HasHorizontalGridLines ? 0 : 1)) & ~1; if (buttons) { buttonExtent = Math.Min(yMid, xMid) / 2; } Bitmap returnBmp = null; Bitmap bmp = null; Graphics graphics = null; Brush backgroundBrush = null; Pen linePen = null; Pen boxPen = null; Brush plusMinusBrush = null; var themeHandle = IntPtr.Zero; var data = new IndentBitmapData(); try { // themeHandle is not used when the tree expander is derived from VS theme. if (!UseVSThemeForTreeExpander) { // see if XP themes are available to draw the buttons. themeHandle = OpenTheme(Handle, EnableExplorerTheme); // we've already asserted handle creation at this point } data = GetIndentBitmapData(backColor, buttons && themeHandle == IntPtr.Zero, buttonExtent); var plusPath = data.PlusPath; var minusPath = data.MinusPath; var boxPath = data.BoxPath; bmp = new Bitmap(columns * xExtent, rows * yExtent); graphics = Graphics.FromImage(bmp); // Fill using a transparent brush EnsureBrush(ref backgroundBrush, data.BackgroundColor); graphics.FillRectangle(backgroundBrush, 0, 0, bmp.Width, bmp.Height); graphics.CompositingMode = CompositingMode.SourceCopy; if (!lines) { // Draw the two buttons and get out graphics.TranslateTransform(xMid, yMid); EnsureBrush(ref plusMinusBrush, data.PlusMinusColor); EnsurePen(ref boxPen, data.BoxColor); if (UseVSThemeForTreeExpander) { DrawTreeExpanderPolygon(graphics, foreColor, data, false /*expanded*/); } else if (themeHandle != IntPtr.Zero) { DrawThemedButtonGlyph(themeHandle, graphics, data.BackgroundColor, buttonExtent, false /* expanded */); } else { if (plusPath != null) { graphics.FillPath(plusMinusBrush, plusPath); } if (boxPath != null) { graphics.DrawPath(boxPen, boxPath); } } graphics.TranslateTransform(0, yExtent); if (UseVSThemeForTreeExpander) { DrawTreeExpanderPolygon(graphics, foreColor, data, true /*expanded*/); } else if (themeHandle != IntPtr.Zero) { DrawThemedButtonGlyph(themeHandle, graphics, data.BackgroundColor, buttonExtent, true /* expanded */); } else { if (minusPath != null) { graphics.FillPath(plusMinusBrush, minusPath); } if (boxPath != null) { graphics.DrawPath(boxPen, boxPath); } } returnBmp = bmp; bmp = null; return returnBmp; } // We always create this pen to support the different dash styles linePen = new Pen(data.LineColor); linePen.DashStyle = data.LineStyle; var oddItemOffset = 0; var finaloddItemOffset = (oddItemHeight && lines) ? 1 : 0; // loop through twice if we have an odd item height, since we need two sets of bitmaps to draw the lines smoothly. while (oddItemOffset <= finaloddItemOffset) { // don't use GraphicsContainers here because GDI drawing (used for themes) doesn't respect the transform matrix. // BeginContainer resets the transform matrix, so there's no way to get the information we need to draw the themed // buttons in the right place. graphics.ResetTransform(); graphics.TranslateTransform(oddItemOffset * (bmp.Width / 2), 0); // offset for odd item heights // Draw the first row (no buttons) var beginTransform = graphics.Transform; graphics.DrawLine(linePen, xMid, oddItemOffset, xMid, yExtent); graphics.TranslateTransform(xExtent, 0); graphics.DrawLine(linePen, xMid, oddItemOffset, xMid, yExtent); // add 2*oddItemOffset below because this is an intersection of horizontal and vertical gridlines. // In the odd height case, not adding an offset creates an ugly pixel cluster, and only adding 1 // causes the ends of the lines to be jagged. Adding 2 creates a bit of extra whitespace, but I believe // it is visually the best compromise. graphics.DrawLine(linePen, xMid + (2 * oddItemOffset), yMid, xExtent, yMid); graphics.TranslateTransform(xExtent, 0); graphics.DrawLine(linePen, xMid, oddItemOffset, xMid, yMid); graphics.DrawLine(linePen, xMid + (2 * oddItemOffset), yMid, xExtent, yMid); // see comment about 2*oddItemOffset above. if (rootLines) { graphics.TranslateTransform(xExtent, 0); graphics.DrawLine(linePen, xMid, yMid + oddItemOffset, xMid, yExtent); graphics.DrawLine(linePen, xMid + (2 * oddItemOffset), yMid, xExtent, yMid); // see comment about 2*oddItemOffset above. graphics.TranslateTransform(xExtent, 0); graphics.DrawLine(linePen, xMid, yMid, xExtent, yMid); } graphics.Transform = beginTransform; // Back to beginning of line if (buttons) { // Draw the second and third rows (plus and minus signs) // These are the same except for the sign in the button, so // use the same code with a different path EnsureBrush(ref plusMinusBrush, data.PlusMinusColor); EnsurePen(ref boxPen, data.BoxColor); for (var i = 0; i < 2; i++) { var curPath = (i == 0) ? plusPath : minusPath; graphics.TranslateTransform(0, yExtent); beginTransform = graphics.Transform; // First column is the same in all three rows graphics.DrawLine(linePen, xMid, oddItemOffset, xMid, yExtent); graphics.TranslateTransform(xExtent + xMid, yMid); graphics.DrawLine(linePen, 0, -yMid + oddItemOffset, 0, -buttonExtent); graphics.DrawLine(linePen, 0, ((buttonExtent + 1) & ~1) + oddItemOffset, 0, yExtent - yMid); graphics.DrawLine(linePen, buttonExtent, 0, xExtent - xMid, 0); if (UseVSThemeForTreeExpander) { DrawTreeExpanderPolygon(graphics, foreColor, data, i == 1 /*expanded*/); } else if (themeHandle != IntPtr.Zero) { DrawThemedButtonGlyph(themeHandle, graphics, data.BackgroundColor, buttonExtent, i == 1 /* expanded */); } else { if (curPath != null) { graphics.FillPath(plusMinusBrush, curPath); } if (boxPath != null) { graphics.DrawPath(boxPen, boxPath); } } graphics.TranslateTransform(xExtent, 0); if (UseVSThemeForTreeExpander) { DrawTreeExpanderPolygon(graphics, foreColor, data, i == 1 /*expanded*/); } else if (themeHandle != IntPtr.Zero) { DrawThemedButtonGlyph(themeHandle, graphics, data.BackgroundColor, buttonExtent, i == 1 /* expanded */); } else { if (curPath != null) { graphics.FillPath(plusMinusBrush, curPath); } if (boxPath != null) { graphics.DrawPath(boxPen, boxPath); } } graphics.DrawLine(linePen, 0, -yMid + oddItemOffset, 0, -buttonExtent); graphics.DrawLine(linePen, buttonExtent, 0, xExtent - xMid, 0); if (rootButtons) { graphics.TranslateTransform(xExtent, 0); if (UseVSThemeForTreeExpander) { DrawTreeExpanderPolygon(graphics, foreColor, data, i == 1 /*expanded*/); } else if (themeHandle != IntPtr.Zero) { DrawThemedButtonGlyph(themeHandle, graphics, data.BackgroundColor, buttonExtent, i == 1 /* expanded */); } else { if (curPath != null) { graphics.FillPath(plusMinusBrush, curPath); } if (boxPath != null) { graphics.DrawPath(boxPen, boxPath); } } graphics.DrawLine(linePen, 0, ((buttonExtent + 1) & ~1) + oddItemOffset, 0, yExtent - yMid); graphics.DrawLine(linePen, buttonExtent, 0, xExtent - xMid, 0); graphics.TranslateTransform(xExtent, 0); graphics.DrawLine(linePen, buttonExtent, 0, xExtent - xMid, 0); if (UseVSThemeForTreeExpander) { DrawTreeExpanderPolygon(graphics, foreColor, data, i == 1 /*expanded*/); } else if (themeHandle != IntPtr.Zero) { DrawThemedButtonGlyph(themeHandle, graphics, data.BackgroundColor, buttonExtent, i == 1 /* expanded */); } else { if (curPath != null) { graphics.FillPath(plusMinusBrush, curPath); } if (boxPath != null) { graphics.DrawPath(boxPen, boxPath); } } } graphics.Transform = beginTransform; // Back to beginning of line } } oddItemOffset++; } returnBmp = bmp; bmp = null; } finally { if (themeHandle != IntPtr.Zero) { CloseTheme(themeHandle); } if (graphics != null) { graphics.Dispose(); } if (bmp != null) { bmp.Dispose(); } CleanBrush(ref backgroundBrush, data.BackgroundColor); CleanPen(ref boxPen, data.BoxColor); CleanBrush(ref plusMinusBrush, data.PlusMinusColor); if (linePen != null) { linePen.Dispose(); } data.Dispose(); } return returnBmp; }
/// <summary> /// Generates the points which represent the tree expander in expanded/unexpanded state, and updates the data parameter. /// </summary> /// <param name="buttonExtent"></param> /// <param name="data"></param> /// <returns></returns> private static IndentBitmapData GenerateExpanderIconPoints(int buttonExtent, ref IndentBitmapData data) { var p = (buttonExtent * 7) / 10; data.UnexpandedIconPoints = new[] { new Point(-p, -p * 2), new Point(-p, p * 2), new Point(p, 0) }; data.ExpandedIconPoints = new[] { new Point(p, -p * 2), new Point(p, p), new Point(-2 * p, p) }; return data; }
/// <summary> /// Get data used to draw the indent bitmaps /// </summary> /// <param name="backColor">Color to be used in background</param> /// <param name="requireButtonPaths">True if paths for button drawing are required. Paths are only required if buttons will be drawn, and XP themes are not available.</param> /// <param name="buttonExtent">Half the size of a button. The returned paths should be centered at (0, 0).</param> /// <returns>Colors and paths used to draw the indent bitmap.</returns> protected virtual IndentBitmapData GetIndentBitmapData(Color backColor, bool requireButtonPaths, int buttonExtent) { var data = new IndentBitmapData(); data.BackgroundColor = backColor; data.LineColor = SystemColors.GrayText; data.LineStyle = DashStyle.Dot; data.BoxColor = SystemColors.GrayText; data.PlusMinusColor = SystemColors.WindowText; // Update the expander points given the button extent. GenerateExpanderIconPoints(buttonExtent, ref data); if (requireButtonPaths) { GraphicsPath plusPath; GraphicsPath minusPath; GraphicsPath boxPath; GeneratePlusMinusPaths(buttonExtent, out plusPath, out minusPath, out boxPath); data.SetPaths(plusPath, minusPath, boxPath, true); } return data; }
public static bool Compare(IndentBitmapData operand1, IndentBitmapData operand2) { Debug.Assert(false); // There is no need to compare these return false; }