ToolStripDropDown.cs 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. using System.ComponentModel;
  2. namespace System.Windows.Forms
  3. {
  4. // In WinForms, ContextMenuStrip must be a descendant of ToolStripDropDown.
  5. // However in WPF these two classes use different approaches (Popup vs ContextMenu).
  6. // That's why we use IPopupWrapper which supplies correct implementation.
  7. public partial class ToolStripDropDown : ToolStrip
  8. {
  9. private IPopupWrapper popup;
  10. private bool isOpened;
  11. private int closedTicks;
  12. internal IPopupWrapper Popup => popup ??= InitPopup();
  13. public new bool Visible => isOpened || (Environment.TickCount - closedTicks) < 300;
  14. public event CancelEventHandler Opening;
  15. public event EventHandler Opened;
  16. public event ToolStripDropDownClosedEventHandler Closed;
  17. protected virtual void OnOpening(CancelEventArgs e) => Opening?.Invoke(this, e);
  18. protected virtual void OnOpened(EventArgs e) => Opened?.Invoke(this, e);
  19. protected virtual void OnClosed(ToolStripDropDownClosedEventArgs e) => Closed?.Invoke(this, e);
  20. internal virtual IPopupWrapper CreatePopup() => new PopupWrapper(this);
  21. private IPopupWrapper InitPopup()
  22. {
  23. var popup = CreatePopup();
  24. popup.Opened += (s, e) =>
  25. {
  26. OnOpened(e);
  27. isOpened = true;
  28. };
  29. popup.Closed += (s, e) =>
  30. {
  31. OnClosed(new ToolStripDropDownClosedEventArgs(ToolStripDropDownCloseReason.ItemClicked));
  32. isOpened = false;
  33. closedTicks = Environment.TickCount;
  34. };
  35. return popup;
  36. }
  37. private void OpenPopup()
  38. {
  39. if (isOpened)
  40. {
  41. Close();
  42. return;
  43. }
  44. Popup.FixupDpi();
  45. var args = new CancelEventArgs();
  46. OnOpening(args);
  47. if (!args.Cancel)
  48. Popup.IsOpen = true;
  49. }
  50. public void Show(int x, int y)
  51. {
  52. Popup.Placement = Windows.Controls.Primitives.PlacementMode.Absolute;
  53. Popup.HorizontalOffset = x / DpiScale;
  54. Popup.VerticalOffset = y / DpiScale;
  55. OpenPopup();
  56. }
  57. public void Show(System.Drawing.Point screenLocation) => Show(screenLocation.X, screenLocation.Y);
  58. public void Show(Control control, int x, int y)
  59. {
  60. x = (int)(x / control.DpiScale);
  61. y = (int)(y / control.DpiScale);
  62. Popup.Placement = Windows.Controls.Primitives.PlacementMode.Relative;
  63. Popup.PlacementTarget = control.control;
  64. Popup.HorizontalOffset = control.control.FlowDirection == FlowDirection.RightToLeft ? control.Width / control.DpiScale - x : x;
  65. Popup.VerticalOffset = y;
  66. OpenPopup();
  67. }
  68. public void Show(Control control, System.Drawing.Point position) => Show(control, position.X, position.Y);
  69. internal void Show(Control control)
  70. {
  71. Popup.Placement = Windows.Controls.Primitives.PlacementMode.Bottom;
  72. Popup.PlacementTarget = control.control;
  73. OpenPopup();
  74. }
  75. public void Close()
  76. {
  77. // do not close not-yet-opened popup. Case: DataColumnDropDown control
  78. if (isOpened)
  79. {
  80. Popup.IsOpen = false;
  81. isOpened = false;
  82. }
  83. }
  84. // TODO? reason param
  85. public void Close(ToolStripDropDownCloseReason reason) => Close();
  86. internal override void AddChild(Control child) => Popup.AddChild(child);
  87. internal override void RemoveChild(Control child) => Popup.RemoveChild(child);
  88. internal override void SetChildIndex(Control child, int index) => Popup.SetChildIndex(child, index);
  89. }
  90. }