1 using System; 2 using System.Drawing; 3 using System.Drawing.Drawing2D; 4 using System.Windows.Forms; 5 6 namespace WeifenLuo.WinFormsUI.Docking 7 { 8 public class VisualStudioToolStripRenderer : ToolStripProfessionalRenderer 9 { 10 private static Rectangle[] baseSizeGripRectangles = 11 { 12 new Rectangle(6,0,1,1), 13 new Rectangle(6,2,1,1), 14 new Rectangle(6,4,1,1), 15 new Rectangle(6,6,1,1), 16 new Rectangle(4,2,1,1), 17 new Rectangle(4,4,1,1), 18 new Rectangle(4,6,1,1), 19 new Rectangle(2,4,1,1), 20 new Rectangle(2,6,1,1), 21 new Rectangle(0,6,1,1) 22 }; 23 24 private const int GRIP_PADDING = 4; 25 private SolidBrush _statusBarBrush; 26 private SolidBrush _statusGripBrush; 27 private SolidBrush _statusGripAccentBrush; 28 private SolidBrush _toolBarBrush; 29 private SolidBrush _gripBrush; 30 private Pen _toolBarBorderPen; 31 private VisualStudioColorTable _table; 32 private DockPanelColorPalette _palette; 33 34 public bool UseGlassOnMenuStrip { get; set; } 35 VisualStudioToolStripRenderer(DockPanelColorPalette palette)36 public VisualStudioToolStripRenderer(DockPanelColorPalette palette) 37 : base(new VisualStudioColorTable(palette)) 38 { 39 _table = (VisualStudioColorTable)ColorTable; 40 _palette = palette; 41 RoundedEdges = false; 42 _statusBarBrush = new SolidBrush(palette.MainWindowStatusBarDefault.Background); 43 _statusGripBrush = new SolidBrush(palette.MainWindowStatusBarDefault.ResizeGrip); 44 _statusGripAccentBrush = new SolidBrush(palette.MainWindowStatusBarDefault.ResizeGripAccent); 45 _toolBarBrush = new SolidBrush(palette.CommandBarToolbarDefault.Background); 46 _gripBrush = new SolidBrush(palette.CommandBarToolbarDefault.Grip); 47 _toolBarBorderPen = new Pen(palette.CommandBarToolbarDefault.Border); 48 49 UseGlassOnMenuStrip = true; 50 } 51 52 #region Rendering Improvements (includes fixes for bugs occured when Windows Classic theme is on). 53 //* OnRenderMenuItemBackground(ToolStripItemRenderEventArgs e)54 protected override void OnRenderMenuItemBackground(ToolStripItemRenderEventArgs e) 55 { 56 // Do not draw disabled item background. 57 if (e.Item.Enabled) 58 { 59 bool isMenuDropDown = e.Item.Owner is MenuStrip; 60 if (isMenuDropDown && e.Item.Pressed) 61 { 62 base.OnRenderMenuItemBackground(e); 63 } 64 else if (e.Item.Selected) 65 { 66 // Rect of item's content area. 67 Rectangle contentRect = e.Item.ContentRectangle; 68 69 // Fix item rect. 70 Rectangle itemRect = isMenuDropDown 71 ? new Rectangle( 72 contentRect.X + 2, contentRect.Y - 2, 73 contentRect.Width - 5, contentRect.Height + 3) 74 : new Rectangle( 75 contentRect.X, contentRect.Y - 1, 76 contentRect.Width, contentRect.Height + 1); 77 78 // Border pen and fill brush. 79 Color pen = ColorTable.MenuItemBorder; 80 Color brushBegin; 81 Color brushEnd; 82 83 if (isMenuDropDown) 84 { 85 brushBegin = ColorTable.MenuItemSelectedGradientBegin; 86 brushEnd = ColorTable.MenuItemSelectedGradientEnd; 87 } 88 else 89 { 90 brushBegin = ColorTable.MenuItemSelected; 91 brushEnd = Color.Empty; 92 } 93 94 DrawRectangle(e.Graphics, itemRect, brushBegin, brushEnd, pen, UseGlassOnMenuStrip); 95 } 96 } 97 } 98 OnRenderToolStripBorder(ToolStripRenderEventArgs e)99 protected override void OnRenderToolStripBorder(ToolStripRenderEventArgs e) 100 { 101 var status = e.ToolStrip as StatusStrip; 102 if (status != null) 103 { 104 // IMPORTANT: left empty to remove white border. 105 return; 106 } 107 108 var context = e.ToolStrip as MenuStrip; 109 if (context != null) 110 { 111 base.OnRenderToolStripBorder(e); 112 return; 113 } 114 115 var drop = e.ToolStrip as ToolStripDropDown; 116 if (drop != null) 117 { 118 base.OnRenderToolStripBorder(e); 119 return; 120 } 121 122 var rect = e.ToolStrip.ClientRectangle; 123 e.Graphics.DrawRectangle(_toolBarBorderPen, new Rectangle(rect.Location, new Size(rect.Width - 1, rect.Height - 1))); 124 } 125 OnRenderToolStripBackground(ToolStripRenderEventArgs e)126 protected override void OnRenderToolStripBackground(ToolStripRenderEventArgs e) 127 { 128 var status = e.ToolStrip as StatusStrip; 129 if (status != null) 130 { 131 base.OnRenderToolStripBackground(e); 132 return; 133 } 134 135 var context = e.ToolStrip as MenuStrip; 136 if (context != null) 137 { 138 base.OnRenderToolStripBackground(e); 139 return; 140 } 141 142 var drop = e.ToolStrip as ToolStripDropDown; 143 if (drop != null) 144 { 145 base.OnRenderToolStripBackground(e); 146 return; 147 } 148 149 e.Graphics.FillRectangle(_toolBarBrush, e.ToolStrip.ClientRectangle); 150 } 151 OnRenderStatusStripSizingGrip(ToolStripRenderEventArgs e)152 protected override void OnRenderStatusStripSizingGrip(ToolStripRenderEventArgs e) 153 { 154 // IMPORTANT: below code was taken from Microsoft's reference code (MIT license). 155 Graphics g = e.Graphics; 156 StatusStrip statusStrip = e.ToolStrip as StatusStrip; 157 158 // we have a set of stock rectangles. Translate them over to where the grip is to be drawn 159 // for the white set, then translate them up and right one pixel for the grey. 160 161 162 if (statusStrip != null) 163 { 164 Rectangle sizeGripBounds = statusStrip.SizeGripBounds; 165 if (!LayoutUtils.IsZeroWidthOrHeight(sizeGripBounds)) 166 { 167 Rectangle[] whiteRectangles = new Rectangle[baseSizeGripRectangles.Length]; 168 Rectangle[] greyRectangles = new Rectangle[baseSizeGripRectangles.Length]; 169 170 for (int i = 0; i < baseSizeGripRectangles.Length; i++) 171 { 172 Rectangle baseRect = baseSizeGripRectangles[i]; 173 if (statusStrip.RightToLeft == RightToLeft.Yes) 174 { 175 baseRect.X = sizeGripBounds.Width - baseRect.X - baseRect.Width; 176 } 177 baseRect.Offset(sizeGripBounds.X, sizeGripBounds.Bottom - 12 /*height of pyramid (10px) + 2px padding from bottom*/); 178 greyRectangles[i] = baseRect; 179 if (statusStrip.RightToLeft == RightToLeft.Yes) 180 { 181 baseRect.Offset(1, -1); 182 } 183 else 184 { 185 baseRect.Offset(-1, -1); 186 } 187 whiteRectangles[i] = baseRect; 188 } 189 190 g.FillRectangles(_statusGripAccentBrush, whiteRectangles); 191 g.FillRectangles(_statusGripBrush, greyRectangles); 192 } 193 } 194 } 195 OnRenderGrip(ToolStripGripRenderEventArgs e)196 protected override void OnRenderGrip(ToolStripGripRenderEventArgs e) 197 { 198 Graphics g = e.Graphics; 199 Rectangle bounds = e.GripBounds; 200 ToolStrip toolStrip = e.ToolStrip; 201 202 bool rightToLeft = (e.ToolStrip.RightToLeft == RightToLeft.Yes); 203 204 int height = (toolStrip.Orientation == Orientation.Horizontal) ? bounds.Height : bounds.Width; 205 int width = (toolStrip.Orientation == Orientation.Horizontal) ? bounds.Width : bounds.Height; 206 207 int numRectangles = (height - (GRIP_PADDING * 2)) / 4; 208 209 if (numRectangles > 0) 210 { 211 numRectangles++; 212 // a MenuStrip starts its grip lower and has fewer grip rectangles. 213 int yOffset = (toolStrip is MenuStrip) ? 2 : 0; 214 215 Rectangle[] shadowRects = new Rectangle[numRectangles]; 216 int startY = GRIP_PADDING + 1 + yOffset; 217 int startX = (width / 2); 218 219 for (int i = 0; i < numRectangles; i++) 220 { 221 shadowRects[i] = (toolStrip.Orientation == Orientation.Horizontal) ? 222 new Rectangle(startX, startY, 1, 1) : 223 new Rectangle(startY, startX, 1, 1); 224 225 startY += 4; 226 } 227 228 // in RTL the GripLight rects should paint to the left of the GripDark rects. 229 int xOffset = (rightToLeft) ? 2 : -2; 230 231 if (rightToLeft) 232 { 233 // scoot over the rects in RTL so they fit within the bounds. 234 for (int i = 0; i < numRectangles; i++) 235 { 236 shadowRects[i].Offset(-xOffset, 0); 237 } 238 } 239 240 Brush b = _gripBrush; 241 for (int i = 0; i < numRectangles - 1; i++) 242 { 243 g.FillRectangle(b, shadowRects[i]); 244 } 245 246 for (int i = 0; i < numRectangles; i++) 247 { 248 shadowRects[i].Offset(xOffset, -2); 249 } 250 251 g.FillRectangles(b, shadowRects); 252 253 for (int i = 0; i < numRectangles; i++) 254 { 255 shadowRects[i].Offset(-2 * xOffset, 0); 256 } 257 258 g.FillRectangles(b, shadowRects); 259 } 260 } 261 OnRenderButtonBackground(ToolStripItemRenderEventArgs e)262 protected override void OnRenderButtonBackground(ToolStripItemRenderEventArgs e) 263 { 264 ToolStripButton button = e.Item as ToolStripButton; 265 if (button != null && button.Enabled) 266 { 267 if (button.Selected || button.Checked) 268 { 269 // Rect of item's content area. 270 Rectangle contentRect = new Rectangle(0, 0, button.Width - 1, button.Height - 1); 271 272 Color pen; 273 Color brushBegin; 274 Color brushMiddle; 275 Color brushEnd; 276 277 if (button.Checked) 278 { 279 if (button.Selected) 280 { 281 pen = _table.ButtonCheckedHoveredBorder; 282 brushBegin = _table.ButtonCheckedHoveredBackground; 283 brushMiddle = _table.ButtonCheckedHoveredBackground; 284 brushEnd = _table.ButtonCheckedHoveredBackground; 285 } 286 else 287 { 288 pen = _table.ButtonCheckedBorder; 289 brushBegin = ColorTable.ButtonCheckedGradientBegin; 290 brushMiddle = ColorTable.ButtonCheckedGradientMiddle; 291 brushEnd = ColorTable.ButtonCheckedGradientEnd; 292 } 293 } 294 else if (button.Pressed) 295 { 296 pen = ColorTable.ButtonPressedBorder; 297 brushBegin = ColorTable.ButtonPressedGradientBegin; 298 brushMiddle = ColorTable.ButtonPressedGradientMiddle; 299 brushEnd = ColorTable.ButtonPressedGradientEnd; 300 } 301 else 302 { 303 pen = ColorTable.ButtonSelectedBorder; 304 brushBegin = ColorTable.ButtonSelectedGradientBegin; 305 brushMiddle = ColorTable.ButtonSelectedGradientMiddle; 306 brushEnd = ColorTable.ButtonSelectedGradientEnd; 307 } 308 309 DrawRectangle(e.Graphics, contentRect, 310 brushBegin, brushMiddle, brushEnd, pen, false); 311 } 312 } 313 else 314 { 315 base.OnRenderButtonBackground(e); 316 } 317 } 318 Initialize(ToolStrip toolStrip)319 protected override void Initialize(ToolStrip toolStrip) 320 { 321 base.Initialize(toolStrip); 322 // IMPORTANT: enlarge grip area so grip can be rendered fully. 323 toolStrip.GripMargin = new Padding(toolStrip.GripMargin.All + 1); 324 } 325 OnRenderOverflowButtonBackground(ToolStripItemRenderEventArgs e)326 protected override void OnRenderOverflowButtonBackground(ToolStripItemRenderEventArgs e) 327 { 328 var cache = _palette.CommandBarMenuPopupDefault.BackgroundTop; 329 330 // IMPORTANT: not 100% accurate as the color change should only happen when the overflow menu is hovered. 331 // here color change happens when the overflow menu is displayed. 332 if (e.Item.Pressed) 333 _palette.CommandBarMenuPopupDefault.BackgroundTop = _palette.CommandBarToolbarOverflowPressed.Background; 334 base.OnRenderOverflowButtonBackground(e); 335 if (e.Item.Pressed) 336 _palette.CommandBarMenuPopupDefault.BackgroundTop = cache; 337 } 338 OnRenderItemText(ToolStripItemTextRenderEventArgs e)339 protected override void OnRenderItemText(ToolStripItemTextRenderEventArgs e) 340 { 341 Color color = Color.Black; 342 var toolStrip = e.ToolStrip; 343 if (toolStrip is StatusStrip) 344 { 345 if (e.Item.Selected) 346 { 347 color = _palette.MainWindowStatusBarDefault.HighlightText; 348 } 349 else 350 { 351 color = _palette.MainWindowStatusBarDefault.Text; 352 } 353 } 354 else if (toolStrip is MenuStrip) 355 { 356 var button = e.Item as ToolStripButton; 357 var checkedButton = button != null && button.Checked; 358 if (!e.Item.Enabled) 359 { 360 color = _palette.CommandBarMenuPopupDisabled.Text; 361 } 362 else if (button != null && button.Pressed) 363 { 364 color = _palette.CommandBarToolbarButtonPressed.Text; 365 } 366 else if (e.Item.Selected && checkedButton) 367 { 368 color = _palette.CommandBarToolbarButtonCheckedHovered.Text; 369 } 370 else if (e.Item.Selected) 371 { 372 color = _palette.CommandBarMenuTopLevelHeaderHovered.Text; 373 } 374 else if (checkedButton) 375 { 376 color = _palette.CommandBarToolbarButtonChecked.Text; 377 } 378 else 379 { 380 color = _palette.CommandBarMenuDefault.Text; 381 } 382 } 383 else if (toolStrip is ToolStripDropDown) 384 { 385 // This might differ from above branch, but left the same here. 386 var button = e.Item as ToolStripButton; 387 var checkedButton = button != null && button.Checked; 388 if (!e.Item.Enabled) 389 { 390 color = _palette.CommandBarMenuPopupDisabled.Text; 391 } 392 else if (button != null && button.Pressed) 393 { 394 color = _palette.CommandBarToolbarButtonPressed.Text; 395 } 396 else if (e.Item.Selected && checkedButton) 397 { 398 color = _palette.CommandBarToolbarButtonCheckedHovered.Text; 399 } 400 else if (e.Item.Selected) 401 { 402 color = _palette.CommandBarMenuTopLevelHeaderHovered.Text; 403 } 404 else if (checkedButton) 405 { 406 color = _palette.CommandBarToolbarButtonChecked.Text; 407 } 408 else 409 { 410 color = _palette.CommandBarMenuDefault.Text; 411 } 412 } 413 else 414 { 415 // Default color, if not it will be black no matter what 416 if (!e.Item.Enabled) 417 { 418 color = _palette.CommandBarMenuPopupDisabled.Text; 419 } 420 else 421 { 422 color = _palette.CommandBarMenuDefault.Text; 423 } 424 } 425 426 TextRenderer.DrawText(e.Graphics, e.Text, e.TextFont, e.TextRectangle, color, e.TextFormat); 427 } 428 429 #region helpers DrawRectangle(Graphics graphics, Rectangle rect, Color brushBegin, Color brushMiddle, Color brushEnd, Color penColor, bool glass)430 private static void DrawRectangle(Graphics graphics, Rectangle rect, Color brushBegin, 431 Color brushMiddle, Color brushEnd, Color penColor, bool glass) 432 { 433 RectangleF firstHalf = new RectangleF( 434 rect.X, rect.Y, 435 rect.Width, (float)rect.Height / 2); 436 437 RectangleF secondHalf = new RectangleF( 438 rect.X, rect.Y + (float)rect.Height / 2, 439 rect.Width, (float)rect.Height / 2); 440 441 if (brushMiddle.IsEmpty && brushEnd.IsEmpty) 442 { 443 graphics.FillRectangle(new SolidBrush(brushBegin), rect); 444 } 445 if (brushMiddle.IsEmpty) 446 { 447 rect.SafelyDrawLinearGradient(brushBegin, brushEnd, LinearGradientMode.Vertical, graphics); 448 } 449 else 450 { 451 firstHalf.SafelyDrawLinearGradientF(brushBegin, brushMiddle, LinearGradientMode.Vertical, graphics); 452 secondHalf.SafelyDrawLinearGradientF(brushMiddle, brushEnd, LinearGradientMode.Vertical, graphics); 453 } 454 455 if (glass) 456 { 457 Brush glassBrush = new SolidBrush(Color.FromArgb(120, Color.White)); 458 graphics.FillRectangle(glassBrush, firstHalf); 459 } 460 461 if (penColor.A > 0) 462 { 463 graphics.DrawRectangle(new Pen(penColor), rect); 464 } 465 } 466 DrawRectangle(Graphics graphics, Rectangle rect, Color brushBegin, Color brushEnd, Color penColor, bool glass)467 private static void DrawRectangle(Graphics graphics, Rectangle rect, Color brushBegin, 468 Color brushEnd, Color penColor, bool glass) 469 { 470 DrawRectangle(graphics, rect, brushBegin, Color.Empty, brushEnd, penColor, glass); 471 } 472 DrawRectangle(Graphics graphics, Rectangle rect, Color brush, Color penColor, bool glass)473 private static void DrawRectangle(Graphics graphics, Rectangle rect, Color brush, 474 Color penColor, bool glass) 475 { 476 DrawRectangle(graphics, rect, brush, Color.Empty, Color.Empty, penColor, glass); 477 } 478 FillRoundRectangle(Graphics graphics, Brush brush, Rectangle rect, int radius)479 private static void FillRoundRectangle(Graphics graphics, Brush brush, Rectangle rect, int radius) 480 { 481 float fx = Convert.ToSingle(rect.X); 482 float fy = Convert.ToSingle(rect.Y); 483 float fwidth = Convert.ToSingle(rect.Width); 484 float fheight = Convert.ToSingle(rect.Height); 485 float fradius = Convert.ToSingle(radius); 486 FillRoundRectangle(graphics, brush, fx, fy, fwidth, fheight, fradius); 487 } 488 FillRoundRectangle(Graphics graphics, Brush brush, float x, float y, float width, float height, float radius)489 private static void FillRoundRectangle(Graphics graphics, Brush brush, float x, float y, float width, float height, float radius) 490 { 491 RectangleF rectangle = new RectangleF(x, y, width, height); 492 GraphicsPath path = GetRoundedRect(rectangle, radius); 493 graphics.FillPath(brush, path); 494 } 495 DrawRoundRectangle(Graphics graphics, Pen pen, Rectangle rect, int radius)496 private static void DrawRoundRectangle(Graphics graphics, Pen pen, Rectangle rect, int radius) 497 { 498 float fx = Convert.ToSingle(rect.X); 499 float fy = Convert.ToSingle(rect.Y); 500 float fwidth = Convert.ToSingle(rect.Width); 501 float fheight = Convert.ToSingle(rect.Height); 502 float fradius = Convert.ToSingle(radius); 503 DrawRoundRectangle(graphics, pen, fx, fy, fwidth, fheight, fradius); 504 } 505 DrawRoundRectangle(Graphics graphics, Pen pen, float x, float y, float width, float height, float radius)506 private static void DrawRoundRectangle(Graphics graphics, Pen pen, float x, float y, float width, float height, float radius) 507 { 508 RectangleF rectangle = new RectangleF(x, y, width, height); 509 GraphicsPath path = GetRoundedRect(rectangle, radius); 510 graphics.DrawPath(pen, path); 511 } 512 GetRoundedRect(RectangleF baseRect, float radius)513 private static GraphicsPath GetRoundedRect(RectangleF baseRect, float radius) 514 { 515 // if corner radius is less than or equal to zero, 516 // return the original rectangle 517 518 if (radius <= 0) 519 { 520 GraphicsPath mPath = new GraphicsPath(); 521 mPath.AddRectangle(baseRect); 522 mPath.CloseFigure(); 523 return mPath; 524 } 525 526 // if the corner radius is greater than or equal to 527 // half the width, or height (whichever is shorter) 528 // then return a capsule instead of a lozenge 529 530 if (radius >= (Math.Min(baseRect.Width, baseRect.Height)) / 2.0) 531 return GetCapsule(baseRect); 532 533 // create the arc for the rectangle sides and declare 534 // a graphics path object for the drawing 535 536 float diameter = radius * 2.0F; 537 SizeF sizeF = new SizeF(diameter, diameter); 538 RectangleF arc = new RectangleF(baseRect.Location, sizeF); 539 GraphicsPath path = new GraphicsPath(); 540 541 // top left arc 542 path.AddArc(arc, 180, 90); 543 544 // top right arc 545 arc.X = baseRect.Right - diameter; 546 path.AddArc(arc, 270, 90); 547 548 // bottom right arc 549 arc.Y = baseRect.Bottom - diameter; 550 path.AddArc(arc, 0, 90); 551 552 // bottom left arc 553 arc.X = baseRect.Left; 554 path.AddArc(arc, 90, 90); 555 556 path.CloseFigure(); 557 return path; 558 } 559 GetCapsule(RectangleF baseRect)560 private static GraphicsPath GetCapsule(RectangleF baseRect) 561 { 562 RectangleF arc; 563 GraphicsPath path = new GraphicsPath(); 564 565 try 566 { 567 float diameter; 568 if (baseRect.Width > baseRect.Height) 569 { 570 // return horizontal capsule 571 diameter = baseRect.Height; 572 SizeF sizeF = new SizeF(diameter, diameter); 573 arc = new RectangleF(baseRect.Location, sizeF); 574 path.AddArc(arc, 90, 180); 575 arc.X = baseRect.Right - diameter; 576 path.AddArc(arc, 270, 180); 577 } 578 else if (baseRect.Width < baseRect.Height) 579 { 580 // return vertical capsule 581 diameter = baseRect.Width; 582 SizeF sizeF = new SizeF(diameter, diameter); 583 arc = new RectangleF(baseRect.Location, sizeF); 584 path.AddArc(arc, 180, 180); 585 arc.Y = baseRect.Bottom - diameter; 586 path.AddArc(arc, 0, 180); 587 } 588 else 589 { 590 // return circle 591 path.AddEllipse(baseRect); 592 } 593 } 594 catch 595 { 596 path.AddEllipse(baseRect); 597 } 598 finally 599 { 600 path.CloseFigure(); 601 } 602 603 return path; 604 } 605 #endregion 606 // */ 607 #endregion 608 } 609 }