MenuControl.cs
上传用户:nnpulika
上传日期:2013-02-15
资源大小:597k
文件大小:44k
源码类别:

状态条

开发平台:

C#

  1. using System;
  2. using System.IO;
  3. using System.Drawing;
  4. using System.Reflection;
  5. using System.Collections;
  6. using System.Drawing.Text;
  7. using System.Windows.Forms;
  8. using System.ComponentModel;
  9. using System.Drawing.Imaging;
  10. using UtilityLibrary.Win32;
  11. using UtilityLibrary.Menus;
  12. using UtilityLibrary.General;
  13. using UtilityLibrary.Collections;
  14. namespace UtilityLibrary.Menus
  15. {
  16. [ToolboxItem(false)]
  17. [DefaultProperty("MenuCommands")]
  18. [DefaultEvent("PopupStart")]
  19. [Designer(typeof(UtilityLibrary.Designers.MenuControlDesigner))]
  20. public class MenuControl : ContainerControl, IMessageFilter
  21. {
  22. #region Class Variables
  23. // Class constants
  24. protected const int  breadthGap = 5;
  25. protected const int  lengthGap = 3;
  26. protected const int  boxExpandUpper = 1;
  27. protected const int  boxExpandSides = 2;
  28. protected const int  shadowGap = 3;
  29. protected const int  shadowYOffset = 3;
  30. protected const int  separatorWidth = 15;
  31. protected const int  subMenuBorderAdjust = 2;
  32. protected const int  chevronIndex = 0;
  33. protected const int  chevronLength = 10;
  34. protected const int  chevronBreadth = 10;
  35. // Class constant is marked as 'readonly' to allow non constant initialization
  36. protected readonly int WM_OPERATEMENU = (int)Win32.Msg.WM_USER + 1;
  37. // Class fields
  38. protected static ImageList  menuImages = null;
  39. protected static bool  supportsLayered = false;
  40. // Declare the popup change event signature
  41. public delegate void PopupHandler(MenuCommand item);
  42. // Instance fields
  43. protected int  rowWidth;
  44. protected int  rowHeight;
  45. protected bool  selected;
  46. protected int  trackItem;
  47. protected bool  multiLine;
  48. protected bool  mouseOver;
  49. protected IntPtr  oldFocus;
  50. protected bool  manualFocus;
  51. protected bool  drawUpwards;
  52. protected VisualStyle  style;
  53. protected bool  plainAsBlock;
  54. protected Direction  direction;
  55. protected bool  ignoreEscapeUp;
  56. protected PopupMenu  popupMenu;
  57. protected bool  dismissTransfer;
  58. protected bool  ignoreMouseMove;
  59. protected ArrayList  drawCommands;
  60. protected MenuCommand  chevronStartCommand;
  61. protected MenuCommandCollection  menuCommands;
  62. // Instance fields - events
  63. public event PopupHandler PopupStart;
  64. public event PopupHandler PopupEnd;
  65. #endregion
  66. #region Constructors
  67. static MenuControl()
  68. {
  69. // Create a strip of images by loading an embedded bitmap resource
  70.  menuImages = ResourceUtil.LoadImageListResource(Type.GetType("UtilityLibrary.Menus.MenuControl"),
  71.  "UtilityLibrary.Resources.ImagesMenu",
  72.  "MenuControlImages",
  73.  new Size( chevronLength,  chevronBreadth),
  74.                                          true,
  75.  new Point(0,0));
  76. // We need to know if the OS supports layered windows
  77.  supportsLayered = (OSFeature.Feature.GetVersionPresent(OSFeature.LayeredWindows) != null);
  78. }
  79. public MenuControl()
  80. {
  81. // Set default values
  82. this.Dock = DockStyle.Top;
  83.  trackItem = -1;
  84.  selected = false;
  85.  multiLine = false;
  86.  popupMenu = null;
  87.  mouseOver = false;
  88.  manualFocus = false;
  89.  drawUpwards = false;
  90.  plainAsBlock = false;
  91.  oldFocus = IntPtr.Zero;
  92.  ignoreEscapeUp = false;
  93.  ignoreMouseMove = false;
  94.  dismissTransfer = false;
  95.  style = VisualStyle.IDE;
  96.  chevronStartCommand = null;
  97.  direction = Direction.Horizontal;
  98.  menuCommands = new MenuCommandCollection();
  99. // Prevent flicker with double buffering and all painting inside WM PAINT
  100. SetStyle(ControlStyles.DoubleBuffer, true);
  101. SetStyle(ControlStyles.AllPaintingInWmPaint, true);
  102. // Should not be allowed to select this control
  103. SetStyle(ControlStyles.Selectable, false);
  104. // Hookup to collection events
  105.  menuCommands.Cleared += new CollectionWithEvents.CollectionClear(OnCollectionCleared);
  106.  menuCommands.Inserted += new CollectionWithEvents.CollectionChange(OnCollectionInserted);
  107.  menuCommands.Removed += new CollectionWithEvents.CollectionChange(OnCollectionRemoved);
  108. // Set the default menu color as background
  109. this.BackColor = SystemColors.Control;
  110. // Do not allow tab key to select this control
  111. this.TabStop = false;
  112. // Default the Font we use
  113. this.Font = SystemInformation.MenuFont;
  114. // Calculate the initial height/width of the control
  115.  rowWidth =  rowHeight = this.Font.Height +  breadthGap * 2 + 1;
  116. // Default to one line of items
  117. this.Height =  rowHeight;
  118. // Add ourself to the application filtering list
  119. Application.AddMessageFilter(this);
  120. }
  121. #endregion
  122. #region Overrides
  123. protected override void OnMouseDown(MouseEventArgs e)
  124. {
  125. Point pos = new Point(e.X, e.Y);
  126. for(int i=0; i< drawCommands.Count; i++)
  127. {
  128. DrawCommand dc =  drawCommands[i] as DrawCommand;
  129. // Find the DrawCommand this is over
  130. if (dc.DrawRect.Contains(pos))
  131. {
  132. // Is an item already selected?
  133. if ( selected)
  134. {
  135. // Is it this item that is already selected?
  136. if ( trackItem == i)
  137. {
  138. // Is a popupMenu showing
  139. if ( popupMenu != null)
  140. {
  141. // Dismiss the submenu
  142.  popupMenu.Dismiss();
  143. // No reference needed
  144.  popupMenu = null;
  145. }
  146. }
  147. }
  148. else
  149. {
  150. // Select the tracked item
  151.  selected = true;
  152.  drawUpwards = false;
  153. GrabTheFocus();
  154. // Is there a change in tracking?
  155. if ( trackItem != i)
  156. {
  157. // Modify the display of the two items 
  158.  trackItem = SwitchTrackingItem( trackItem, i);
  159. }
  160. else
  161. {
  162. // Update display to show as selected
  163. DrawCommand( trackItem, true);
  164. }
  165. // Is there a submenu to show?
  166. if (dc.Chevron || (dc.MenuCommand.MenuCommands.Count > 0))
  167. WindowsAPI.PostMessage(this.Handle, WM_OPERATEMENU, 1, 0);
  168. }
  169. break;
  170. }
  171. }
  172. base.OnMouseDown(e);
  173. }
  174. protected override void OnMouseUp(MouseEventArgs e)
  175. {
  176. // Is an item currently being tracked?
  177. if ( trackItem != -1)
  178. {
  179. // Is it also selected?
  180. if ( selected == true)
  181. {
  182. // Is it also showing a submenu
  183. if ( popupMenu == null)
  184. {
  185. // Deselect the item
  186.  selected = false;
  187.  drawUpwards = false;
  188. DrawCommand( trackItem, true);
  189. ReturnTheFocus();
  190. }
  191. }
  192. }
  193. base.OnMouseUp(e);
  194. }
  195. protected override void OnMouseMove(MouseEventArgs e)
  196. {
  197. // Sometimes we need to ignore this message
  198. if ( ignoreMouseMove)
  199.  ignoreMouseMove = false;
  200. else
  201. {
  202. // Is the first time we have noticed a mouse movement over our window
  203. if (! mouseOver)
  204. {
  205. // Crea the structure needed for WindowsAPI call
  206. Win32.TRACKMOUSEEVENTS tme = new Win32.TRACKMOUSEEVENTS();
  207. // Fill in the structure
  208. tme.cbSize = 16;
  209. tme.dwFlags = Win32.TrackerEventFlags.TME_LEAVE;
  210. tme.hWnd = this.Handle;
  211. tme.dwHoverTime = 0;
  212. // Request that a message gets sent when mouse leaves this window
  213. WindowsAPI.TrackMouseEvent(ref tme);
  214. // Yes, we know the mouse is over window
  215.  mouseOver = true;
  216. }
  217. Form parentForm = this.FindForm();
  218. // Only hot track if this Form is active
  219. if ((parentForm != null) && parentForm.ContainsFocus)
  220. {
  221. Point pos = new Point(e.X, e.Y);
  222. int i = 0;
  223. for(i=0; i< drawCommands.Count; i++)
  224. {
  225. DrawCommand dc =  drawCommands[i] as DrawCommand;
  226. // Find the DrawCommand this is over
  227. if (dc.DrawRect.Contains(pos))
  228. {
  229. // Is there a change in selected item?
  230. if ( trackItem != i)
  231. {
  232. // We we currently selecting an item
  233. if ( selected)
  234. {
  235. if ( popupMenu != null)
  236. {
  237. // Note that we are dismissing the submenu but not removing
  238. // the selection of items, this flag is tested by the routine
  239. // that will return because the submenu has been finished
  240.  dismissTransfer = true;
  241. // Dismiss the submenu
  242.  popupMenu.Dismiss();
  243. // Default to downward drawing
  244.  drawUpwards = false;
  245. }
  246. // Modify the display of the two items 
  247.  trackItem = SwitchTrackingItem( trackItem, i);
  248. // Does the newly selected item have a submenu?
  249. if (dc.Chevron || (dc.MenuCommand.MenuCommands.Count > 0))
  250. WindowsAPI.PostMessage(this.Handle, WM_OPERATEMENU, 1, 0);
  251. }
  252. else
  253. {
  254. // Modify the display of the two items 
  255.  trackItem = SwitchTrackingItem( trackItem, i);
  256. }
  257. }
  258. break;
  259. }
  260. }
  261. // If not in selected mode
  262. if (! selected)
  263. {
  264. // None of the commands match?
  265. if (i ==  drawCommands.Count)
  266. {
  267. // Modify the display of the two items 
  268.  trackItem = SwitchTrackingItem( trackItem, -1);
  269. }
  270. }
  271. }
  272. }
  273. base.OnMouseMove(e);
  274. }
  275. protected override void OnMouseLeave(EventArgs e)
  276. {
  277.  mouseOver = false;
  278. // If we manually grabbed focus then do not switch
  279. // selection when the mouse leaves the control area
  280. if (! manualFocus)
  281. {
  282. if ( trackItem != -1)
  283. {
  284. // If an item is selected then do not change tracking item when the 
  285. // mouse leaves the control area, as a popup menu might be showing and 
  286. // so keep the tracking and selection indication visible
  287. if ( selected == false)
  288.  trackItem = SwitchTrackingItem( trackItem, -1);
  289. }
  290. }
  291. base.OnMouseLeave(e);
  292. }
  293. protected override void OnResize(EventArgs e)
  294. {
  295. Recalculate();
  296. // Any resize of control should redraw all of it otherwise when you 
  297. // stretch to the right it will not paint correctly as we have a one
  298. // pixel gao between text and min button which is not honoured otherwise
  299. this.Invalidate();
  300. base.OnResize(e);
  301. }
  302. protected override void OnLostFocus(EventArgs e)
  303. {
  304. // We lost the focus
  305.  manualFocus = false;
  306. // Remove tracking of any item
  307.  trackItem = SwitchTrackingItem( trackItem, -1);
  308. // No need to remember old focus anymore
  309.  oldFocus = IntPtr.Zero;
  310. // When we lose focus we sometimes miss the WM MOUSELEAVE message
  311.  mouseOver = false;
  312. base.OnLostFocus(e);
  313. }
  314. protected override void OnPaint(PaintEventArgs e)
  315. {
  316. DrawAllCommands(e.Graphics);
  317. base.OnPaint(e);
  318. }
  319. protected override void OnKeyDown(KeyEventArgs e)
  320. {
  321. switch(e.KeyCode)
  322. {
  323. case Keys.Escape:
  324. // Is an item being tracked
  325. if ( trackItem != -1)
  326. {
  327. // Is that item also selected
  328. if ( selected)
  329. {
  330. // Is it also showing a submenu
  331. if ( popupMenu == null)
  332. {
  333. // Deselect the item
  334.  selected = false;
  335.  drawUpwards = false;
  336. // Repaint the item
  337. DrawCommand( trackItem, true);
  338. ReturnTheFocus();
  339. // Key has been processed
  340. e.Handled = true;
  341. }
  342. else
  343. {
  344. // The popup menu will swallow the escape
  345.  ignoreEscapeUp = true;
  346. }
  347. }
  348. }
  349. break;
  350. case Keys.Left:
  351. if ( direction == Direction.Horizontal)
  352. ProcessMoveLeft(false);
  353. if ( selected)
  354.  ignoreMouseMove = true;
  355. break;
  356. case Keys.Right:
  357. if ( direction == Direction.Horizontal)
  358. ProcessMoveRight(false);
  359. else
  360. ProcessMoveDown();
  361. if ( selected)
  362.  ignoreMouseMove = true;
  363. break;
  364. case Keys.Down:
  365. if ( direction == Direction.Horizontal)
  366. ProcessMoveDown();
  367. else
  368. ProcessMoveRight(false);
  369. break;
  370. case Keys.Up:
  371. if ( direction == Direction.Vertical)
  372. ProcessMoveLeft(false);
  373. break;
  374. }
  375. base.OnKeyDown(e);
  376. }
  377. protected override void OnKeyUp(KeyEventArgs e)
  378. {
  379. switch(e.KeyCode)
  380. {
  381. case Keys.Escape:
  382. if ( manualFocus)
  383. {
  384. if (! selected)
  385. {
  386. // Did we see the key down for escape? If not then the 
  387. // escape must have been used to dismiss a popup menu
  388. if (! ignoreEscapeUp)
  389. {
  390. // No longer in manual focus mode
  391.  manualFocus = false;
  392. // Remove tracking of any item
  393.  trackItem = SwitchTrackingItem( trackItem, -1);
  394. // Send the focus back to origin
  395. ReturnTheFocus();
  396. }
  397. }
  398. }
  399.  ignoreEscapeUp = false;
  400. break;
  401. default:
  402. ProcessMnemonicKey((char)e.KeyValue);
  403. if ( selected)
  404.  ignoreMouseMove = true;
  405. break;
  406. }
  407. base.OnKeyUp(e);
  408. }
  409. protected override void WndProc(ref Message m)
  410. {
  411. // WM OPERATEMENU is not a constant and so cannot be in a switch
  412. if (m.Msg == WM_OPERATEMENU)
  413. OnOperateMenu(ref m);
  414. else
  415. {
  416. switch(m.Msg)
  417. {
  418. case (int)Win32.Msg.WM_GETDLGCODE:
  419. OnGetDlgCode(ref m);
  420. return;
  421. }
  422. }
  423. base.WndProc(ref m);
  424. }
  425. #endregion
  426. #region Properties
  427. [Category("Behaviour")]
  428. [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
  429. public MenuCommandCollection MenuCommands
  430. {
  431. get { return  menuCommands; }
  432. set
  433. {
  434.  menuCommands.Clear();
  435.  menuCommands = value;
  436. Recalculate();
  437. Invalidate();
  438. }
  439. [Category("Appearance")]
  440. public VisualStyle Style
  441. {
  442. get { return  style; }
  443. set
  444. {
  445. if ( style != value)
  446. {
  447.  style = value;
  448. Recalculate();
  449. Invalidate();
  450. }
  451. }
  452. }
  453. [Category("Appearance")]
  454. public override Font Font
  455. {
  456. get { return base.Font; }
  457. set
  458. {
  459. base.Font = value;
  460. // Resize to take into accout the new height
  461.  rowHeight = this.Font.Height +  lengthGap * 2;
  462. Recalculate();
  463. Invalidate();
  464. }
  465. }
  466. [Category("Appearance")]
  467. [DefaultValue(false)]
  468. public bool PlainAsBlock
  469. {
  470. get { return  plainAsBlock; }
  471. set
  472. {
  473. if ( plainAsBlock != value)
  474. {
  475.  plainAsBlock = value;
  476. Recalculate();
  477. Invalidate();
  478. }
  479. }
  480. }
  481. [Category("Appearance")]
  482. [DefaultValue(false)]
  483. public bool MultiLine
  484. {
  485. get { return  multiLine; }
  486. set
  487. {
  488. if ( multiLine != value)
  489. {
  490.  multiLine = value;
  491. Recalculate();
  492. Invalidate();
  493. }
  494. }
  495. }
  496. [Category("Appearance")]
  497. public Direction Direction
  498. {
  499. get { return  direction; }
  500. set
  501. {
  502. if ( direction != value)
  503. {
  504.  direction = value;
  505. Recalculate();
  506. Invalidate();
  507. }
  508. }
  509. }
  510. public override DockStyle Dock
  511. {
  512. get { return base.Dock; }
  513. set
  514. {
  515. base.Dock = value;
  516. switch(value)
  517. {
  518. case DockStyle.None:
  519.  direction = Direction.Horizontal;
  520. break;
  521. case DockStyle.Top:
  522. case DockStyle.Bottom:
  523. this.Height = 0;
  524.  direction = Direction.Horizontal;
  525. break;
  526. case DockStyle.Left:
  527. case DockStyle.Right:
  528. this.Width = 0;
  529.  direction = Direction.Vertical;
  530. break;
  531. }
  532. Recalculate();
  533. Invalidate();
  534. }
  535. }
  536. #endregion
  537. #region Methods
  538. public bool PreFilterMessage(ref Message msg)
  539. {
  540. Form parentForm = this.FindForm();
  541. // Only interested if the Form we are on is activate (i.e. contains focus)
  542. if ((parentForm != null) && parentForm.ContainsFocus)
  543. {
  544. switch(msg.Msg)
  545. {
  546. case (int)Win32.Msg.WM_MDISETMENU:
  547. case (int)Win32.Msg.WM_MDIREFRESHMENU:
  548. return true;
  549. case (int)Win32.Msg.WM_KEYUP:
  550. {
  551. // Find up/down state of shift and control keys
  552. ushort shiftKey = WindowsAPI.GetKeyState((int)Win32.VirtualKeys.VK_SHIFT);
  553. ushort controlKey = WindowsAPI.GetKeyState((int)Win32.VirtualKeys.VK_CONTROL);
  554. // Basic code we are looking for is the key pressed...
  555. int code = (int)msg.WParam;
  556. // ...plus the modifier for SHIFT...
  557. if (((int)shiftKey & 0x00008000) != 0)
  558. code += 0x00010000;
  559. // ...plus the modifier for CONTROL
  560. if (((int)controlKey & 0x00008000) != 0)
  561. code += 0x00020000;
  562. // Construct shortcut from keystate and keychar
  563. Shortcut sc = (Shortcut)(code);
  564. // Search for a matching command
  565. return GenerateShortcut(sc,  menuCommands);
  566. }
  567. case (int)Win32.Msg.WM_SYSKEYUP:
  568. if ((int)msg.WParam == (int)Win32.VirtualKeys.VK_MENU)
  569. {
  570. // If not already in manual focus mode
  571. if (! manualFocus)
  572. {
  573. // Are there any menu commands?
  574. if ( drawCommands.Count > 0)
  575. {
  576. // If no item is currently tracked then...
  577. if ( trackItem == -1)
  578. {
  579. // ...start tracking the first valid command
  580. for(int i=0; i< drawCommands.Count; i++)
  581. {
  582. DrawCommand dc =  drawCommands[i] as DrawCommand;
  583. if (!dc.Separator && (dc.Chevron || dc.MenuCommand.Enabled))
  584. {
  585.  trackItem = SwitchTrackingItem(-1, i);
  586. break;
  587. }
  588. }
  589. }
  590. // We keep the focus until they move it
  591.  manualFocus = true;
  592. // Grab the focus for key events
  593. GrabTheFocus();
  594. }
  595. return true;
  596. }
  597. }
  598. else
  599. {
  600. // Construct shortcut from ALT + keychar
  601. Shortcut sc = (Shortcut)(0x00040000 + (int)msg.WParam);
  602. if (GenerateShortcut(sc,  menuCommands))
  603. return true;
  604. // Last resort is treat as a potential mnemonic
  605. return ProcessMnemonicKey((char)msg.WParam);
  606. }
  607. break;
  608. default:
  609. break;
  610. }
  611. }
  612. return false;
  613. }
  614. #endregion
  615. #region Implementation
  616. protected void OnPopupStart(MenuCommand mc)
  617. {
  618. if (PopupStart != null)
  619. PopupStart(mc);
  620. }
  621. protected void OnPopupEnd(MenuCommand mc)
  622. {
  623. if (PopupEnd != null)
  624. PopupEnd(mc);
  625. }
  626. protected void OnCollectionCleared()
  627. {
  628. // Reset state ready for a recalculation
  629.  selected = false;
  630.  trackItem = -1;
  631. Recalculate();
  632. Invalidate();
  633. }
  634. protected void OnCollectionInserted(int index, object value)
  635. {
  636. MenuCommand mc = value as MenuCommand;
  637. // We need notification whenever the properties of this command change
  638. mc.PropertyChanged += new MenuCommand.PropChangeHandler(OnCommandChanged);
  639. // Reset state ready for a recalculation
  640.  selected = false;
  641.  trackItem = -1;
  642. Recalculate();
  643. Invalidate();
  644. }
  645. protected void OnCollectionRemoved(int index, object value)
  646. {
  647. // Reset state ready for a recalculation
  648.  selected = false;
  649.  trackItem = -1;
  650. Recalculate();
  651. Invalidate();
  652. }
  653. protected void OnCommandChanged(MenuCommand item, MenuCommand.Property prop)
  654. {
  655. Recalculate();
  656. Invalidate();
  657. }
  658. internal void DrawSelectionUpwards()
  659. {
  660. // Double check the state is correct for this method to be called
  661. if (( trackItem != -1) && ( selected))
  662. {
  663. // This flag is tested in the DrawCommand method
  664.  drawUpwards = true;
  665. // Force immediate redraw of the item
  666. DrawCommand( trackItem, true);
  667. }
  668. }
  669. protected void Recalculate()
  670. {
  671. int length;
  672. if ( direction == Direction.Horizontal)
  673. length = this.Width;
  674. else 
  675. length = this.Height;
  676. // Is there space for any commands?
  677. if (length > 0)
  678. {
  679. // Count the number of rows needed
  680. int rows = 0;
  681. // Number of items on this row
  682. int columns = 0;
  683. // Create a collection of drawing objects
  684.  drawCommands = new ArrayList();
  685. // Minimum length is a gap on either side of the text
  686. int cellMinLength =  lengthGap * 2;
  687. // Each cell is as broad as the whole control
  688. int cellBreadth = this.Height;
  689. // Accumulate starting position of each cell
  690. int lengthStart = 0;
  691. // If the chevron is already displayed then reduce length by its length
  692. if ( chevronStartCommand != null)
  693. length -= cellMinLength +  chevronLength;
  694. // Assume chevron is not needed by default
  695.  chevronStartCommand = null;
  696. using(Graphics g = this.CreateGraphics())
  697. {
  698. // Count the item we are processing
  699. int index = 0;
  700. foreach(MenuCommand command in  menuCommands)
  701. {
  702. // Give the command a chance to update its state
  703. command.OnUpdate(EventArgs.Empty);
  704. // Ignore items that are marked as hidden
  705. if (!command.Visible)
  706. continue;
  707. int cellLength = 0;
  708. // Is this a separator?
  709. if (command.Text == "-")
  710. cellLength =  separatorWidth;
  711. else
  712. {
  713. // Calculate the text width of the cell
  714. SizeF dimension = g.MeasureString(command.Text, this.Font);
  715. // Always add 1 to ensure that rounding is up and not down
  716. cellLength = cellMinLength + (int)dimension.Width + 1;
  717. }
  718. Rectangle cellRect;
  719. // Create a new position rectangle
  720. if ( direction == Direction.Horizontal)
  721. cellRect = new Rectangle(lengthStart,  rowHeight * rows, cellLength,  rowHeight);
  722. else
  723. cellRect = new Rectangle( rowWidth * rows, lengthStart,  rowWidth, cellLength);
  724. lengthStart += cellLength;
  725. columns++;
  726. // If this item is overlapping the control edge and it is not the first
  727. // item on the line then we should wrap around to the next row.
  728. if ((lengthStart > length) && (columns > 1))
  729. {
  730. if ( multiLine)
  731. {
  732. // Move to next row
  733. rows++;
  734. // Reset number of items on this column
  735. columns = 1;
  736. // Reset starting position of next item
  737. lengthStart = cellLength;
  738. // Reset position of this item
  739. if ( direction == Direction.Horizontal)
  740. {
  741. cellRect.X = 0;
  742. cellRect.Y +=  rowHeight;
  743. }
  744. else
  745. {
  746. cellRect.X +=  rowWidth;
  747. cellRect.Y = 0;
  748. }
  749. }
  750. else
  751. {
  752. // Is a tracked item being make invisible
  753. if (index <=  trackItem)
  754. {
  755. // Need to remove tracking of this item
  756.  trackItem = -1;
  757. }
  758. // Remember which item is first for the chevron submenu
  759.  chevronStartCommand = command;
  760. if ( direction == Direction.Horizontal)
  761. {
  762. cellRect.Y = 0;
  763. cellRect.Width = cellMinLength +  chevronLength;
  764. cellRect.X = this.Width - cellRect.Width;
  765. cellRect.Height =  rowHeight;
  766. }
  767. else
  768. {
  769. cellRect.X = 0;
  770. cellRect.Height = cellMinLength +  chevronLength;
  771. cellRect.Y = this.Height - (cellMinLength +  chevronLength);
  772. cellRect.Width =  rowWidth;
  773. }
  774. // Create a draw command for this chevron
  775.  drawCommands.Add(new DrawCommand(cellRect));
  776. // Exit, do not add the current item or any afterwards
  777. break;
  778. }
  779. }
  780. // Create a drawing object
  781.  drawCommands.Add(new DrawCommand(command, cellRect));
  782. index++;
  783. }
  784. }
  785. if ( direction == Direction.Horizontal)
  786. {
  787. int controlHeight = (rows + 1) *  rowHeight;
  788. // Ensure the control is the correct height
  789. if (this.Height != controlHeight)
  790. this.Height = controlHeight;
  791. }
  792. else
  793. {
  794. int controlWidth = (rows + 1) *  rowWidth;
  795. // Ensure the control is the correct width
  796. if (this.Width != controlWidth)
  797. this.Width = controlWidth;
  798. }
  799. }
  800. }
  801. protected void DrawCommand(int drawItem, bool tracked)
  802. {
  803. // Create a graphics object for drawing
  804. using(Graphics g = this.CreateGraphics())
  805. DrawSingleCommand(g,  drawCommands[drawItem] as DrawCommand, tracked);
  806. }
  807. internal void DrawSingleCommand(Graphics g, DrawCommand dc, bool tracked)
  808. {
  809. Rectangle drawRect = dc.DrawRect;
  810. MenuCommand mc = dc.MenuCommand;
  811. // Copy the rectangle used for drawing cell
  812. Rectangle shadowRect = drawRect;
  813. // Expand to right and bottom to cover the area used to draw shadows
  814. shadowRect.Width +=  shadowGap;
  815. shadowRect.Height +=  shadowGap;
  816. // Draw background color over cell and shadow area to the right
  817. using(SolidBrush back = new SolidBrush(SystemColors.Control))
  818. g.FillRectangle(back, shadowRect);
  819. if (!dc.Separator)
  820. {
  821. Rectangle textRect;
  822. // Text rectangle size depends on type of draw command we are drawing
  823. if (dc.Chevron)
  824. {
  825. // Create chevron drawing rectangle
  826. textRect = new Rectangle(drawRect.Left +  lengthGap, drawRect.Top +  boxExpandUpper,
  827.  drawRect.Width -  lengthGap * 2, drawRect.Height - ( boxExpandUpper * 2));
  828. }
  829. else
  830. {
  831. // Create text drawing rectangle
  832. textRect = new Rectangle(drawRect.Left +  lengthGap, drawRect.Top +  lengthGap,
  833.  drawRect.Width -  lengthGap * 2, drawRect.Height -  lengthGap * 2);
  834. }
  835. if (dc.Enabled)
  836. {
  837. // Draw selection 
  838. if (tracked)
  839. {
  840. Rectangle boxRect;
  841. // Create the rectangle for box around the text
  842. if ( direction == Direction.Horizontal)
  843. {
  844. boxRect = new Rectangle(textRect.Left -  boxExpandSides,
  845.     textRect.Top -  boxExpandUpper,
  846.     textRect.Width +  boxExpandSides * 2,
  847.     textRect.Height +  boxExpandUpper);
  848. }
  849. else
  850. {
  851. if (!dc.Chevron)
  852. {
  853. boxRect = new Rectangle(textRect.Left,
  854. textRect.Top -  boxExpandSides,
  855. textRect.Width -  boxExpandSides,
  856. textRect.Height +  boxExpandSides * 2);
  857. }
  858. else
  859. boxRect = textRect;
  860. }
  861. switch( style)
  862. {
  863. case VisualStyle.IDE:
  864. if ( selected)
  865. {
  866. // Fill the entire inside
  867. g.FillRectangle(SystemBrushes.ControlLight, boxRect);
  868. int rightLeft = boxRect.Right + 1;
  869. int rightBottom = boxRect.Bottom;
  870. if ( drawUpwards && ( direction == Direction.Horizontal))
  871. {
  872. // Draw the box around the selection area
  873. using(Pen dark = new Pen(SystemColors.ControlDark))
  874. g.DrawRectangle(dark, boxRect.Left, boxRect.Top -  shadowGap, 
  875.   boxRect.Width, boxRect.Height +  shadowGap);
  876. // Remove the top line of the selection area
  877. using(Pen dark = new Pen(SystemColors.ControlLight))
  878. g.DrawLine(dark, boxRect.Left + 1, boxRect.Top, boxRect.Right - 2, boxRect.Top);
  879. int rightTop = boxRect.Top;
  880. int leftLeft = boxRect.Left +  shadowGap;
  881. SolidBrush shadowBrush;
  882. // Decide if we need to use an alpha brush
  883. if ( supportsLayered && ( style == VisualStyle.IDE))
  884. shadowBrush = new SolidBrush(Color.FromArgb(64, 0, 0, 0));
  885. else
  886. shadowBrush = new SolidBrush(SystemColors.ControlDark);
  887. g.FillRectangle(shadowBrush, new Rectangle(rightLeft, rightTop +  shadowGap - 1,  shadowGap, rightBottom - rightTop -  shadowGap*2));
  888. shadowBrush.Dispose();
  889. }
  890. else
  891. {
  892. // Draw the box around the selection area
  893. using(Pen dark = new Pen(SystemColors.ControlDark))
  894. g.DrawRectangle(dark, boxRect);
  895. if ( direction == Direction.Horizontal)
  896. {
  897. // Remove the bottom line of the selection area
  898. using(Pen dark = new Pen(SystemColors.ControlLight))
  899. g.DrawLine(dark, boxRect.Left, boxRect.Bottom, boxRect.Right, boxRect.Bottom);
  900. int rightTop = boxRect.Top +  shadowYOffset;
  901. SolidBrush shadowBrush;
  902. // Decide if we need to use an alpha brush
  903. if ( supportsLayered && ( style == VisualStyle.IDE))
  904. shadowBrush = new SolidBrush(Color.FromArgb(64, 0, 0, 0));
  905. else
  906. shadowBrush = new SolidBrush(SystemColors.ControlDark);
  907. g.FillRectangle(shadowBrush, new Rectangle(rightLeft, rightTop,  shadowGap, rightBottom - rightTop));
  908. shadowBrush.Dispose();
  909. }
  910. else
  911. {
  912. // Remove the right line of the selection area
  913. using(Pen dark = new Pen(SystemColors.ControlLight))
  914. g.DrawLine(dark, boxRect.Right, boxRect.Top, boxRect.Right, boxRect.Bottom);
  915. int leftLeft = boxRect.Left +  shadowYOffset;
  916. SolidBrush shadowBrush;
  917. // Decide if we need to use an alpha brush
  918. if ( supportsLayered && ( style == VisualStyle.IDE))
  919. shadowBrush = new SolidBrush(Color.FromArgb(64, 0, 0, 0));
  920. else
  921. shadowBrush = new SolidBrush(SystemColors.ControlDark);
  922. g.FillRectangle(shadowBrush, new Rectangle(leftLeft, rightBottom+1, rightBottom - leftLeft -  shadowGap,  shadowGap));
  923. shadowBrush.Dispose();
  924. }
  925. }
  926. }
  927. else
  928. {
  929. using (Pen selectPen = new Pen(ColorUtil.VSNetBorderColor))
  930. {
  931. // Draw the selection area in white so can alpha draw over the top
  932. using (SolidBrush whiteBrush = new SolidBrush(Color.White))
  933. g.FillRectangle(whiteBrush, boxRect);
  934. using (SolidBrush selectBrush = new SolidBrush(Color.FromArgb(70, ColorUtil.VSNetBorderColor)))
  935. {
  936. // Draw the selection area
  937. g.FillRectangle(selectBrush, boxRect);
  938. // Draw a border around the selection area
  939. g.DrawRectangle(selectPen, boxRect);
  940. }
  941. }
  942. }
  943. break;
  944. case VisualStyle.Plain:
  945. if ( plainAsBlock)
  946. {
  947. using (SolidBrush selectBrush = new SolidBrush(ColorUtil.VSNetBorderColor))
  948. g.FillRectangle(selectBrush, drawRect);
  949. }
  950. else
  951. {
  952. using(Pen lighlight = new Pen(SystemColors.ControlLightLight),
  953.   dark = new Pen(SystemColors.ControlDark))
  954. {
  955. if ( selected)
  956. {
  957. g.DrawLine(dark, boxRect.Left, boxRect.Bottom, boxRect.Left, boxRect.Top);
  958. g.DrawLine(dark, boxRect.Left, boxRect.Top, boxRect.Right, boxRect.Top);
  959. g.DrawLine(lighlight, boxRect.Right, boxRect.Top, boxRect.Right, boxRect.Bottom);
  960. g.DrawLine(lighlight, boxRect.Right, boxRect.Bottom, boxRect.Left, boxRect.Bottom);
  961. }
  962. else
  963. {
  964. g.DrawLine(lighlight, boxRect.Left, boxRect.Bottom, boxRect.Left, boxRect.Top);
  965. g.DrawLine(lighlight, boxRect.Left, boxRect.Top, boxRect.Right, boxRect.Top);
  966. g.DrawLine(dark, boxRect.Right, boxRect.Top, boxRect.Right, boxRect.Bottom);
  967. g.DrawLine(dark, boxRect.Right, boxRect.Bottom, boxRect.Left, boxRect.Bottom);
  968. }
  969. }
  970. }
  971. break;
  972. }
  973. }
  974. }
  975. if (dc.Chevron)
  976. {
  977. // Draw the chevron image in the centre of the text area
  978. int yPos = drawRect.Top;
  979. int xPos = drawRect.X + ((drawRect.Width -  chevronLength) / 2);
  980. // When selected...
  981. if ( selected)
  982. {
  983. // ...offset down and to the right
  984. xPos += 1;
  985. yPos += 1;
  986. }
  987. g.DrawImage( menuImages.Images[ chevronIndex], xPos, yPos);
  988. }
  989. else
  990. {
  991. // Left align the text drawing on a single line centered vertically
  992. // and process the & character to be shown as an underscore on next character
  993. StringFormat format = new StringFormat();
  994. format.FormatFlags = StringFormatFlags.NoClip | StringFormatFlags.NoWrap;
  995. format.Alignment = StringAlignment.Center;
  996. format.LineAlignment = StringAlignment.Center;
  997. format.HotkeyPrefix = HotkeyPrefix.Show;
  998. if ( direction == Direction.Vertical)
  999. format.FormatFlags |= StringFormatFlags.DirectionVertical;
  1000. if (dc.Enabled)
  1001. {
  1002. if (tracked && ( style == VisualStyle.Plain) &&  plainAsBlock)
  1003. {
  1004. // Is the item selected as well as tracked?
  1005. if ( selected)
  1006. {
  1007. // Offset to show it is selected
  1008. textRect.X += 2;
  1009. textRect.Y += 2;
  1010. }
  1011. using (SolidBrush textBrush = new SolidBrush(SystemColors.HighlightText))
  1012. g.DrawString(mc.Text, this.Font, textBrush, textRect, format);
  1013. }
  1014. else
  1015. using (SolidBrush textBrush = new SolidBrush(SystemColors.MenuText))
  1016. g.DrawString(mc.Text, this.Font, textBrush, textRect, format);
  1017. }
  1018. else 
  1019. {
  1020. // Helper values used when drawing grayed text in plain style
  1021. Rectangle rectDownRight = textRect;
  1022. rectDownRight.Offset(1,1);
  1023. // Draw then text offset down and right
  1024. using (SolidBrush whiteBrush = new SolidBrush(SystemColors.HighlightText))
  1025. g.DrawString(mc.Text, this.Font, whiteBrush, rectDownRight, format);
  1026. // Draw then text offset up and left
  1027. using (SolidBrush grayBrush = new SolidBrush(SystemColors.GrayText))
  1028. g.DrawString(mc.Text, this.Font, grayBrush, textRect, format);
  1029. }
  1030. }
  1031. }
  1032. }
  1033. protected void DrawAllCommands(Graphics g)
  1034. {
  1035. for(int i=0; i< drawCommands.Count; i++)
  1036. {
  1037. // Grab some commonly used values
  1038. DrawCommand dc =  drawCommands[i] as DrawCommand;
  1039. // Draw this command only
  1040. DrawSingleCommand(g, dc, (i ==  trackItem));
  1041. }
  1042. }
  1043. protected int SwitchTrackingItem(int oldItem, int newItem)
  1044. {
  1045. // Create a graphics object for drawinh
  1046. using(Graphics g = this.CreateGraphics())
  1047. {
  1048. // Deselect the old draw command
  1049. if (oldItem != -1)
  1050. {
  1051. DrawCommand dc =  drawCommands[oldItem] as DrawCommand;
  1052. // Draw old item not selected
  1053. DrawSingleCommand(g,  drawCommands[oldItem] as DrawCommand, false);
  1054. }
  1055.  trackItem = newItem;
  1056. // Select the new draw command
  1057. if ( trackItem != -1)
  1058. {
  1059. DrawCommand dc =  drawCommands[ trackItem] as DrawCommand;
  1060. // Draw new item selected
  1061. DrawSingleCommand(g,  drawCommands[ trackItem] as DrawCommand, true);
  1062. }
  1063. }
  1064. return  trackItem;
  1065. }
  1066. internal void OperateSubMenu(DrawCommand dc, bool selectFirst, bool trackRemove)
  1067. {
  1068. Rectangle drawRect = dc.DrawRect;
  1069. // Find screen positions for popup menu
  1070. Point screenPos;
  1071. if ( style == VisualStyle.IDE)
  1072. {
  1073. if ( direction == Direction.Horizontal)
  1074. screenPos = PointToScreen(new Point(dc.DrawRect.Left + 1, drawRect.Bottom -  lengthGap - 1));
  1075. else
  1076. screenPos = PointToScreen(new Point(dc.DrawRect.Right -  breadthGap, drawRect.Top +  boxExpandSides - 1));
  1077. }
  1078. else
  1079. {
  1080. if ( direction == Direction.Horizontal)
  1081. screenPos = PointToScreen(new Point(dc.DrawRect.Left + 1, drawRect.Bottom));
  1082. else
  1083. screenPos = PointToScreen(new Point(dc.DrawRect.Right, drawRect.Top));
  1084. }
  1085. Point aboveScreenPos;
  1086. if ( style == VisualStyle.IDE)
  1087. {
  1088. if ( direction == Direction.Horizontal)
  1089. aboveScreenPos = PointToScreen(new Point(dc.DrawRect.Left + 1, drawRect.Top +  lengthGap + 1));
  1090. else
  1091. aboveScreenPos = PointToScreen(new Point(dc.DrawRect.Right -  breadthGap, drawRect.Bottom +  lengthGap));
  1092. }
  1093. else
  1094. {
  1095. if ( direction == Direction.Horizontal)
  1096. aboveScreenPos = PointToScreen(new Point(dc.DrawRect.Left + 1, drawRect.Top));
  1097. else
  1098. aboveScreenPos = PointToScreen(new Point(dc.DrawRect.Right, drawRect.Bottom));
  1099. }
  1100. int borderGap;
  1101. // Calculate the missing gap in the PopupMenu border
  1102. if ( direction == Direction.Horizontal)
  1103. borderGap = dc.DrawRect.Width -  subMenuBorderAdjust;
  1104. else
  1105. borderGap = dc.DrawRect.Height -  subMenuBorderAdjust;
  1106.  popupMenu = new PopupMenu();
  1107. // Define the correct visual style based on ours
  1108.  popupMenu.Style = this.Style;
  1109. // Key direction when keys cause dismissal
  1110. int returnDir = 0;
  1111. if (dc.Chevron)
  1112. {
  1113. MenuCommandCollection mcc = new MenuCommandCollection();
  1114. bool addCommands = false;
  1115. // Generate a collection of menu commands for those not visible
  1116. foreach(MenuCommand command in  menuCommands)
  1117. {
  1118. if (!addCommands && (command ==  chevronStartCommand))
  1119. addCommands = true;
  1120. if (addCommands)
  1121. mcc.Add(command);
  1122. }
  1123. // Track the popup using provided menu item collection
  1124.  popupMenu.TrackPopup(screenPos, 
  1125.   aboveScreenPos,
  1126.    direction,
  1127.   mcc, 
  1128.   borderGap,
  1129.   selectFirst,
  1130.   this,
  1131.   ref returnDir);
  1132. }
  1133. else
  1134. {
  1135. // Generate event so that caller has chance to modify MenuCommand contents
  1136. OnPopupStart(dc.MenuCommand);
  1137. // Track the popup using provided menu item collection
  1138.  popupMenu.TrackPopup(screenPos, 
  1139.   aboveScreenPos,
  1140.    direction,
  1141.   dc.MenuCommand.MenuCommands, 
  1142.   borderGap,
  1143.   selectFirst,
  1144.   this,
  1145.   ref returnDir);
  1146. // Generate event so that caller has chance to modify MenuCommand contents
  1147. OnPopupEnd(dc.MenuCommand);
  1148. }
  1149. // Remove unwanted object
  1150.  popupMenu = null;
  1151. // Was arrow key used to dismiss the submenu?
  1152. if (returnDir != 0)
  1153. {
  1154. // Using keyboard movements means we should have the focus
  1155. if (! manualFocus)
  1156. {
  1157.  manualFocus = true;
  1158. GrabTheFocus();
  1159. }
  1160. if (returnDir < 0)
  1161. {
  1162. // Shift selection left one
  1163. ProcessMoveLeft(true);
  1164. }
  1165. else
  1166. {
  1167. // Shift selection right one
  1168. ProcessMoveRight(true);
  1169. }
  1170. // A WM MOUSEMOVE is generated when we open up the new submenu for 
  1171. // display, ignore this as it causes the selection to move
  1172.  ignoreMouseMove = true;
  1173. }
  1174. else
  1175. {
  1176. // Only if the submenu was dismissed at the request of the submenu
  1177. // should the selection mode be cancelled, otherwise keep selection mode
  1178. if (! dismissTransfer)
  1179. {
  1180. // This item is no longer selected
  1181.  selected = false;
  1182.  drawUpwards = false;
  1183. // Should we stop tracking this item
  1184. if (trackRemove)
  1185. {
  1186. ReturnTheFocus();
  1187. // Unselect the current item
  1188.  trackItem = SwitchTrackingItem( trackItem, -1);
  1189. }
  1190. else
  1191. {
  1192. // Repaint the item
  1193. DrawCommand( trackItem, true);
  1194. }
  1195. }
  1196. else
  1197. {
  1198. // Do not change  selected status
  1199.  dismissTransfer = false;
  1200. }
  1201. }
  1202. }
  1203. protected void GrabTheFocus()
  1204. {
  1205. // Remember the current focus location
  1206.  oldFocus = WindowsAPI.GetFocus();
  1207. // Grab the focus
  1208. this.Focus();
  1209. }
  1210. protected void ReturnTheFocus()
  1211. {
  1212. // Do we remember where it came from?
  1213. if ( oldFocus != IntPtr.Zero)
  1214. {
  1215. // Send the focus back
  1216. WindowsAPI.SetFocus( oldFocus);
  1217. // No need to remember old focus anymore
  1218.  oldFocus = IntPtr.Zero;
  1219. }
  1220. // We lost the focus
  1221.  manualFocus = false;
  1222. }
  1223. protected void ProcessMoveLeft(bool select)
  1224. {
  1225. if ( popupMenu == null)
  1226. {
  1227. int newItem =  trackItem;
  1228. int startItem = newItem;
  1229. for(int i=0; i< drawCommands.Count; i++)
  1230. {
  1231. // Move to previous item
  1232. newItem--;
  1233. // Have we looped all the way around all choices
  1234. if (newItem == startItem)
  1235. return;
  1236. // Check limits
  1237. if (newItem < 0)
  1238. newItem =  drawCommands.Count - 1;
  1239. DrawCommand dc =  drawCommands[newItem] as DrawCommand;
  1240. // Can we select this item?
  1241. if (!dc.Separator && (dc.Chevron || dc.MenuCommand.Enabled))
  1242. {
  1243. // If a change has occured
  1244. if (newItem !=  trackItem)
  1245. {
  1246. // Modify the display of the two items 
  1247.  trackItem = SwitchTrackingItem( trackItem, newItem);
  1248. if ( selected)
  1249. {
  1250. if (dc.Chevron || (dc.MenuCommand.MenuCommands.Count > 0))
  1251. WindowsAPI.PostMessage(this.Handle, WM_OPERATEMENU, 0, 1);
  1252. }
  1253. break;
  1254. }
  1255. }
  1256. }
  1257. }
  1258. }
  1259. protected void ProcessMoveRight(bool select)
  1260. {
  1261. if ( popupMenu == null)
  1262. {
  1263. int newItem =  trackItem;
  1264. int startItem = newItem;
  1265. for(int i=0; i< drawCommands.Count; i++)
  1266. {
  1267. // Move to previous item
  1268. newItem++;
  1269. // Check limits
  1270. if (newItem >=  drawCommands.Count)
  1271. newItem = 0;
  1272. DrawCommand dc =  drawCommands[newItem] as DrawCommand;
  1273. // Can we select this item?
  1274. if (!dc.Separator && (dc.Chevron || dc.MenuCommand.Enabled))
  1275. {
  1276. // If a change has occured
  1277. if (newItem !=  trackItem)
  1278. {
  1279. // Modify the display of the two items 
  1280.  trackItem = SwitchTrackingItem( trackItem, newItem);
  1281. if ( selected)
  1282. {
  1283. if (dc.Chevron || (dc.MenuCommand.MenuCommands.Count > 0))
  1284. WindowsAPI.PostMessage(this.Handle, WM_OPERATEMENU, 0, 1);
  1285. }
  1286. break;
  1287. }
  1288. }
  1289. }
  1290. }
  1291. }
  1292. protected void ProcessMoveDown()
  1293. {
  1294. if ( popupMenu == null)
  1295. {
  1296. // Are we tracking an item?
  1297. if ( trackItem != -1)
  1298. {
  1299. // The item must not already be selected
  1300. if (! selected)
  1301. {
  1302. DrawCommand dc =  drawCommands[ trackItem] as DrawCommand;
  1303. // Is there a submenu to show?
  1304. if (dc.Chevron || (dc.MenuCommand.MenuCommands.Count > 0))
  1305. {
  1306. // Select the tracked item
  1307.  selected = true;
  1308.  drawUpwards = false;
  1309. // Update display to show as selected
  1310. DrawCommand( trackItem, true);
  1311. // Show the submenu
  1312. WindowsAPI.PostMessage(this.Handle, WM_OPERATEMENU, 0, 1);
  1313. }
  1314. }
  1315. }
  1316. }
  1317. }
  1318. protected bool ProcessMnemonicKey(char key)
  1319. {
  1320. // We should always gain focus
  1321. if (! manualFocus)
  1322. {
  1323. // We keep the focus until they move it
  1324.  manualFocus = true;
  1325. GrabTheFocus();
  1326. }
  1327. // No current selection
  1328. if (! selected)
  1329. {
  1330. // Search for an item that matches
  1331. for(int i=0; i< drawCommands.Count; i++)
  1332. {
  1333. DrawCommand dc =  drawCommands[i] as DrawCommand;
  1334. // Only interested in enabled items
  1335. if ((dc.MenuCommand != null) && dc.MenuCommand.Enabled)
  1336. {
  1337. // Does the character match?
  1338. if (key == dc.Mnemonic)
  1339. {
  1340. // Select the tracked item
  1341.  selected = true;
  1342.  drawUpwards = false;
  1343. // Is there a change in tracking?
  1344. if ( trackItem != i)
  1345. {
  1346. // Modify the display of the two items 
  1347.  trackItem = SwitchTrackingItem( trackItem, i);
  1348. }
  1349. else
  1350. {
  1351. // Update display to show as selected
  1352. DrawCommand( trackItem, true);
  1353. }
  1354. // Is there a submenu to show?
  1355. if (dc.Chevron || (dc.MenuCommand.MenuCommands.Count > 0))
  1356. WindowsAPI.PostMessage(this.Handle, WM_OPERATEMENU, 0, 1);
  1357. else
  1358. {
  1359. // No, pulse the Click event for the command
  1360. dc.MenuCommand.OnClick(EventArgs.Empty);
  1361. }
  1362. return true;
  1363. }
  1364. }
  1365. }
  1366. }
  1367. return false;
  1368. }
  1369. protected bool GenerateShortcut(Shortcut sc, MenuCommandCollection mcc)
  1370. {
  1371. foreach(MenuCommand mc in mcc)
  1372. {
  1373. // Does the command match?
  1374. if (mc.Enabled && (mc.Shortcut == sc))
  1375. {
  1376. // Generate event for command
  1377. mc.OnClick(EventArgs.Empty);
  1378. return true;
  1379. }
  1380. else
  1381. {
  1382. // Any child items to test?
  1383. if (mc.MenuCommands.Count > 0)
  1384. {
  1385. // Recursive descent of all collections
  1386. if (GenerateShortcut(sc, mc.MenuCommands))
  1387. return true;
  1388. }
  1389. }
  1390. }
  1391. return false;
  1392. }
  1393. protected void OnOperateMenu(ref Message m)
  1394. {
  1395. // Is there a valid item being tracted?
  1396. if ( trackItem != -1)
  1397. {
  1398. DrawCommand dc =  drawCommands[ trackItem] as DrawCommand;
  1399. OperateSubMenu(dc, (m.LParam != IntPtr.Zero), (m.WParam != IntPtr.Zero));
  1400. }
  1401. }
  1402. protected void OnGetDlgCode(ref Message m)
  1403. {
  1404. // We want to the Form to provide all keyboard input to us
  1405. m.Result = (IntPtr)Win32.DialogCodes.DLGC_WANTALLKEYS;
  1406. }
  1407. #endregion
  1408. }
  1409. }