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 }