ActiveMenuImpl.cs
1 /*=============================================================================
2 *
3 * (C) Copyright 2011, Michael Carlisle (mike.carlisle@thecodeking.co.uk)
4 *
5 * http://www.TheCodeKing.co.uk
6 *
7 * All rights reserved.
8 * The code and information is provided "as-is" without waranty of any kind,
9 * either expresed or implied.
10 *
11 *-----------------------------------------------------------------------------
12 * History:
13 * 01/09/2007 Michael Carlisle Version 1.0
14 *=============================================================================
15 */
16 using System;
17 using System.Collections.Generic;
18 using System.ComponentModel;
19 using System.Drawing;
20 using System.Runtime.InteropServices;
21 using System.Windows.Forms;
24 
25 namespace sage.ew.formul.ButtonTittle
26 {
30  internal class ActiveMenuImpl : Form, IActiveMenu
31  {
36  private static readonly Dictionary<Form, IActiveMenu> parents;
37 
38  //private readonly IContainer components;
39 
43  private readonly ActiveItemsImpl items;
44 
45  private readonly Size originalMinSize;
46 
50  private readonly Form parentForm;
51 
52  private readonly SpillOverMode spillOverMode;
53  private readonly ThemeFactory themeFactory;
54 
59  private int containerMaxWidth;
60 
61  private bool isActivated;
62  private ITheme theme;
63  private ToolTip tooltip;
64 
65  // To detect redundant calls
66  private bool _disposed = false;
67 
68  static ActiveMenuImpl()
69  {
70  parents = new Dictionary<Form, IActiveMenu>();
71  }
72 
78  private ActiveMenuImpl(Form form)
79  {
80  InitializeComponent();
81  items = new ActiveItemsImpl();
82  items.CollectionModified += ItemsCollectionModified;
83  parentForm = form;
84  Show(form);
85  parentForm.Disposed += ParentFormDisposed;
86  Visible = false;
87  isActivated = form.WindowState != FormWindowState.Minimized;
88  themeFactory = new ThemeFactory(form);
89  theme = themeFactory.GetTheme();
90  originalMinSize = form.MinimumSize;
91  AttachHandlers();
92  ToolTip.ShowAlways = true;
93  TopMost = form.TopMost;
94  TopMost = false;
95  spillOverMode = SpillOverMode.IncreaseSize;
96  }
97 
106  protected override CreateParams CreateParams
107  {
108  get
109  {
110  CreateParams p = base.CreateParams;
111  p.Style = (int) Win32.WS_CHILD;
112  p.Style |= (int) Win32.WS_CLIPSIBLINGS;
113  p.ExStyle &= (int) Win32.WS_EX_LAYERED;
114  p.Parent = Win32.GetDesktopWindow();
115  return p;
116  }
117  }
118 
119  #region IActiveMenu Members
120 
124  public IActiveItems Items
125  {
126  get { return items; }
127  }
128 
133  public ToolTip ToolTip
134  {
135  get { return tooltip ?? (tooltip = new ToolTip()); }
136  set { tooltip = value; }
137  }
138 
139  // Color del titulo inactivo
140  private Color InactiveTittleColor
141  {
142  get
143  {
144  if (inactiveTittleColor == null)
145  {
146  Point actmenupos = new Point(Left - 1, Top);
147  inactiveTittleColor = GetPixel(actmenupos);
148  }
149 
150  return inactiveTittleColor;
151  }
152  }
153  private dynamic inactiveTittleColor = null;
154 
155  #endregion
156 
160  public static IActiveMenu GetInstance(Form form)
161  {
162  if (!parents.ContainsKey(form))
163  {
164  parents.Add(form, new ActiveMenuImpl(form));
165  }
166  return parents[form];
167  }
168 
173  protected override void OnLoad(EventArgs e)
174  {
175  base.OnLoad(e);
176  base.BringToFront();
177  }
178 
184  private void ItemsCollectionModified(object sender, ListModificationEventArgs e)
185  {
186  Controls.Clear();
187  foreach (ActiveButton button in Items)
188  {
189  Controls.Add(button);
190  }
191  CalcSize();
192  OnPosition();
193  }
194 
198  private void ParentFormDisposed(object sender, EventArgs e)
199  {
200  var form = (Form)sender;
201  if (form == null)
202  {
203  return;
204  }
205  if (parents.ContainsKey(form))
206  {
207  parents.Remove(form);
208  }
209  }
210 
215  protected void AttachHandlers()
216  {
217  parentForm.Deactivate += ParentFormDeactivate;
218  parentForm.Activated += ParentFormActivated;
219  parentForm.SizeChanged += ParentRefresh;
220  parentForm.VisibleChanged += ParentRefresh;
221  parentForm.Move += ParentRefresh;
222  parentForm.SystemColorsChanged += TitleButtonSystemColorsChanged;
223  // used to mask the menu control behind the buttons.
224  if (Win32.DwmIsCompositionEnabled)
225  {
226  // PE-103700. Cambiar el Fuchsia por LightGray para que si el botón se refleja en alguna parte que no se vea tan "lila"
227  BackColor = Color.LightGray;
228  TransparencyKey = Color.LightGray;
229  }
230  else
231  {
232  BackColor = Color.FromKnownColor(KnownColor.ActiveCaption);
233  TransparencyKey = BackColor;
234  }
235  }
236 
241  private void CancelAttachHandlers()
242  {
243  if (parentForm == null)
244  return;
245 
246  parentForm.Deactivate -= ParentFormDeactivate;
247  parentForm.Activated -= ParentFormActivated;
248  parentForm.SizeChanged -= ParentRefresh;
249  parentForm.VisibleChanged -= ParentRefresh;
250  parentForm.Move -= ParentRefresh;
251  parentForm.SystemColorsChanged -= TitleButtonSystemColorsChanged;
252  }
253 
257  public bool _ParentFormActivated = true;
258 
264  private void ParentFormDeactivate(object sender, EventArgs e)
265  {
266  ToolTip.ShowAlways = false;
267  _ParentFormActivated = false;
268 
269  foreach (Control loCtrl in Controls)
270  loCtrl.Invalidate();
271 
272  OnPosition();
273  }
274 
281  private void ParentFormActivated(object sender, EventArgs e)
282  {
283  ToolTip.ShowAlways = true;
284  _ParentFormActivated = true;
285 
286  foreach (Control loCtrl in Controls)
287  loCtrl.Invalidate();
288 
289  OnPosition();
290  }
291 
295  private void TitleButtonSystemColorsChanged(object sender, EventArgs e)
296  {
297  theme = themeFactory.GetTheme();
298  CalcSize();
299  OnPosition();
300  }
301 
306  private void CalcSize()
307  {
308  int left = 0;
309  for (int i = (Items.Count - 1); i >= 0; i--)
310  {
311  var button = (ThemedItem) Items[i];
312  button.Theme = theme;
313  button.Left = left;
314  left += Items[i].Width;// + theme.ButtonOffset.X;
315  button.Top = theme.ButtonOffset.Y;
316  }
317  containerMaxWidth = left;
318 
319  if (spillOverMode == SpillOverMode.IncreaseSize)
320  {
321  int w = containerMaxWidth + theme.ControlBoxSize.Width + theme.FrameBorder.Width + theme.FrameBorder.Width;
322 
323  parentForm.MinimumSize = originalMinSize;
324 
325  if (parentForm.MinimumSize.Width <= w)
326  {
327  parentForm.MinimumSize = new Size(w, parentForm.MinimumSize.Height);
328  }
329  }
330  }
331 
335  protected void ParentRefresh(object sender, EventArgs e)
336  {
337  if (parentForm.WindowState == FormWindowState.Minimized || !parentForm.Visible)
338  {
339  isActivated = false;
340  Visible = false;
341  }
342  else
343  {
344  isActivated = true;
345  OnPosition();
346  }
347  }
348 
349  private Color GetPixel(Point position)
350  {
351  Color colorpicked = SystemColors.ActiveCaption;
352 
353  try
354  {
355  using (var bitmap = new Bitmap(1, 1))
356  {
357  using (var graphics = Graphics.FromImage(bitmap))
358  {
359  graphics.CopyFromScreen(position, new Point(0, 0), new Size(1, 1));
360  }
361  colorpicked = bitmap.GetPixel(0, 0);
362  }
363  }
364  catch (Exception)
365  {
366 
367  }
368 
369  return colorpicked;
370  }
371 
372  public Color GetChromeColor()
373  {
374  int hr1 = DwmIsCompositionEnabled(out bool isEnabled);
375  if ((hr1 != 0) || !isEnabled) // 0 means S_OK.
376  {
377  return SystemColors.ActiveCaption;
378  }
379 
380  DWMCOLORIZATIONPARAMS parameters;
381  try
382  {
383  // This API is undocumented and so may become unusable in future versions of OSes.
384  int hr2 = DwmGetColorizationParameters(out parameters);
385  if (hr2 != 0) // 0 means S_OK.
386  {
387  return SystemColors.ActiveCaption;
388  }
389  }
390  catch
391  {
392  return SystemColors.ActiveCaption;
393  }
394 
395  // Convert colorization color parameter to Color ignoring alpha channel.
396  Color targetColor = Color.FromArgb(
397  (byte)(parameters.colorizationColor >> 16),
398  (byte)(parameters.colorizationColor >> 8),
399  (byte)parameters.colorizationColor);
400 
401  // Prepare base gray color.
402  Color baseColor = Color.FromArgb(217, 217, 217);
403 
404  // Blend the two colors using colorization color balance parameter.
405  return BlendColor(targetColor, baseColor, 100 - parameters.colorizationColorBalance);
406  }
407 
408  private Color BlendColor(Color color1, Color color2, double color2Perc)
409  {
410  if ((color2Perc < 0) || (100 < color2Perc))
411  {
412  throw new ArgumentOutOfRangeException("color2Perc");
413  }
414 
415  return Color.FromArgb(
416  BlendColorChannel(color1.R, color2.R, color2Perc),
417  BlendColorChannel(color1.G, color2.G, color2Perc),
418  BlendColorChannel(color1.B, color2.B, color2Perc));
419  }
420 
421  private byte BlendColorChannel(double channel1, double channel2, double channel2Perc)
422  {
423  var buff = channel1 + (channel2 - channel1) * channel2Perc / 100D;
424  return Math.Min((byte)Math.Round(buff), (byte)255);
425  }
426 
427  [DllImport("Dwmapi.dll")]
428  private static extern int DwmIsCompositionEnabled([MarshalAs(UnmanagedType.Bool)] out bool pfEnabled);
429 
430  [DllImport("Dwmapi.dll", EntryPoint = "#127")] // Undocumented API
431  private static extern int DwmGetColorizationParameters(out DWMCOLORIZATIONPARAMS parameters);
432 
433  [StructLayout(LayoutKind.Sequential)]
434  private struct DWMCOLORIZATIONPARAMS
435  {
436  public uint colorizationColor;
437  public uint colorizationAfterglow;
438  public uint colorizationColorBalance; // Ranging from 0 to 100
439  public uint colorizationAfterglowBalance;
440  public uint colorizationBlurBalance;
441  public uint colorizationGlassReflectionIntensity;
442  public uint colorizationOpaqueBlend;
443  }
444 
448  private void OnPosition()
449  {
450  if (!IsDisposed)
451  {
452  if (theme == null || !theme.IsDisplayed)
453  {
454  Visible = false;
455  return;
456  }
457 
458  int top = theme.FrameBorder.Height;
459  int left = theme.FrameBorder.Width + theme.ControlBoxSize.Width;
460 
461  Top = top + parentForm.Top;
462  Left = parentForm.Left + parentForm.Width - containerMaxWidth - left ;
463 
464  if (theme is Styled)
465  {
466  Top += 1;
467  }
468  else if (theme is Aero)
469  {
470  if (System.Diagnostics.Debugger.IsAttached)
471  {
472  switch (parentForm.FormBorderStyle)
473  {
474  case FormBorderStyle.Sizable:
475  Top += 1;
476  break;
477  case FormBorderStyle.FixedSingle:
478  case FormBorderStyle.FixedDialog:
479  Top += 5;
480  break;
481  }
482  }
483  }
484  else if (theme is Aero8)
485  {
486  switch (parentForm.FormBorderStyle)
487  {
488  case FormBorderStyle.Sizable:
489  Top += 1;
490  break;
491  case FormBorderStyle.FixedSingle:
492  case FormBorderStyle.FixedDialog:
493  Top += 1; // Para que se vean los botones de ayuda dentro del caption tanto en Windows Server 2012 como Windows 10
494  break;
495  }
496  }
497  else if (theme is Aero10)
498  {
499  switch (parentForm.FormBorderStyle)
500  {
501  case FormBorderStyle.FixedDialog:
502  Top += 5;
503  break;
504  }
505  }
506 
507  if (!(theme is Aero) && Left - 1 > 0 && Top > 0)
508  {
509  Point actmenupos = new Point(Left - 1, Top);
510  Color backColor;
511 
512  if (_ParentFormActivated)
513  {
514  if (theme is Aero10)
515  {
516  backColor = Color.White;
517  }
518  else
519  {
520  backColor = GetChromeColor();
521  }
522  }
523  else
524  {
525  backColor = InactiveTittleColor;
526  }
527 
528  if (Convert.ToInt32(backColor.R) > 0 || Convert.ToInt32(backColor.G) > 0 || Convert.ToInt32(backColor.B) > 0)
529  {
530  this.BackColor = Color.FromArgb(this.BackColor.A,
531  Math.Min(255, Convert.ToInt32(backColor.R * 1.01)),
532  Math.Min(255, Convert.ToInt32(backColor.G * 1.01f)),
533  Math.Min(255, Convert.ToInt32(backColor.B * 1.01f)));
534  }
535 
536  foreach (ActiveButton button in Items)
537  {
538  button.ChangeColors();
539  }
540  }
541 
542  Visible = theme.IsDisplayed && isActivated;
543 
544  if (Visible)
545  {
546  if (Items.Count > 0)
547  {
548  if (theme is Aero || theme is Aero8 || theme is Aero10)
549  Opacity = 1;
550  else
551  Opacity = parentForm.Opacity;
552 
553  if (parentForm.Visible)
554  {
555  if (theme is Aero || theme is Aero8 || theme is Aero10)
556  Opacity = 1;
557  else
558  Opacity = parentForm.Opacity;
559  }
560  else
561  {
562  Visible = false;
563  }
564  }
565  if (spillOverMode == SpillOverMode.Hide)
566  {
567  foreach (ActiveButton b in Items)
568  {
569  if (b.Left + Left - theme.FrameBorder.Width + 2 < parentForm.Left)
570  {
571  b.Visible = false;
572  }
573  else
574  {
575  b.Visible = true;
576  }
577  }
578  }
579  }
580  }
581  }
582 
587  protected override void Dispose(bool disposing)
588  {
589  if (_disposed)
590  return;
591 
592  if (disposing)
593  CancelAttachHandlers();
594 
595  base.Dispose(disposing);
596 
597  _disposed = true;
598  }
599 
605  private void InitializeComponent()
606  {
607  this.SuspendLayout();
608  //
609  // ActiveMenuImpl
610  //
611  this.AutoSize = true;
612  this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
613  this.ClientSize = new System.Drawing.Size(10, 10);
614  this.ControlBox = false;
615  this.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
616  this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
617  this.MaximizeBox = false;
618  this.MinimizeBox = false;
619  this.Name = "ActiveMenuImpl";
620  this.ShowIcon = false;
621  this.ShowInTaskbar = false;
622  this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
623  this.ResumeLayout(false);
624 
625  }
626  }
627 }