MenuControl.cs
上传用户:nnpulika
上传日期:2013-02-15
资源大小:597k
文件大小:44k
- using System;
- using System.IO;
- using System.Drawing;
- using System.Reflection;
- using System.Collections;
- using System.Drawing.Text;
- using System.Windows.Forms;
- using System.ComponentModel;
- using System.Drawing.Imaging;
- using UtilityLibrary.Win32;
- using UtilityLibrary.Menus;
- using UtilityLibrary.General;
- using UtilityLibrary.Collections;
- namespace UtilityLibrary.Menus
- {
- [ToolboxItem(false)]
- [DefaultProperty("MenuCommands")]
- [DefaultEvent("PopupStart")]
- [Designer(typeof(UtilityLibrary.Designers.MenuControlDesigner))]
- public class MenuControl : ContainerControl, IMessageFilter
- {
- #region Class Variables
-
- // Class constants
- protected const int breadthGap = 5;
- protected const int lengthGap = 3;
- protected const int boxExpandUpper = 1;
- protected const int boxExpandSides = 2;
- protected const int shadowGap = 3;
- protected const int shadowYOffset = 3;
- protected const int separatorWidth = 15;
- protected const int subMenuBorderAdjust = 2;
- protected const int chevronIndex = 0;
- protected const int chevronLength = 10;
- protected const int chevronBreadth = 10;
- // Class constant is marked as 'readonly' to allow non constant initialization
- protected readonly int WM_OPERATEMENU = (int)Win32.Msg.WM_USER + 1;
- // Class fields
- protected static ImageList menuImages = null;
- protected static bool supportsLayered = false;
- // Declare the popup change event signature
- public delegate void PopupHandler(MenuCommand item);
- // Instance fields
- protected int rowWidth;
- protected int rowHeight;
- protected bool selected;
- protected int trackItem;
- protected bool multiLine;
- protected bool mouseOver;
- protected IntPtr oldFocus;
- protected bool manualFocus;
- protected bool drawUpwards;
- protected VisualStyle style;
- protected bool plainAsBlock;
- protected Direction direction;
- protected bool ignoreEscapeUp;
- protected PopupMenu popupMenu;
- protected bool dismissTransfer;
- protected bool ignoreMouseMove;
- protected ArrayList drawCommands;
- protected MenuCommand chevronStartCommand;
- protected MenuCommandCollection menuCommands;
- // Instance fields - events
- public event PopupHandler PopupStart;
- public event PopupHandler PopupEnd;
- #endregion
- #region Constructors
- static MenuControl()
- {
- // Create a strip of images by loading an embedded bitmap resource
- menuImages = ResourceUtil.LoadImageListResource(Type.GetType("UtilityLibrary.Menus.MenuControl"),
- "UtilityLibrary.Resources.ImagesMenu",
- "MenuControlImages",
- new Size( chevronLength, chevronBreadth),
- true,
- new Point(0,0));
- // We need to know if the OS supports layered windows
- supportsLayered = (OSFeature.Feature.GetVersionPresent(OSFeature.LayeredWindows) != null);
- }
- public MenuControl()
- {
- // Set default values
- this.Dock = DockStyle.Top;
- trackItem = -1;
- selected = false;
- multiLine = false;
- popupMenu = null;
- mouseOver = false;
- manualFocus = false;
- drawUpwards = false;
- plainAsBlock = false;
- oldFocus = IntPtr.Zero;
- ignoreEscapeUp = false;
- ignoreMouseMove = false;
- dismissTransfer = false;
- style = VisualStyle.IDE;
- chevronStartCommand = null;
- direction = Direction.Horizontal;
- menuCommands = new MenuCommandCollection();
-
- // Prevent flicker with double buffering and all painting inside WM PAINT
- SetStyle(ControlStyles.DoubleBuffer, true);
- SetStyle(ControlStyles.AllPaintingInWmPaint, true);
- // Should not be allowed to select this control
- SetStyle(ControlStyles.Selectable, false);
- // Hookup to collection events
- menuCommands.Cleared += new CollectionWithEvents.CollectionClear(OnCollectionCleared);
- menuCommands.Inserted += new CollectionWithEvents.CollectionChange(OnCollectionInserted);
- menuCommands.Removed += new CollectionWithEvents.CollectionChange(OnCollectionRemoved);
- // Set the default menu color as background
- this.BackColor = SystemColors.Control;
- // Do not allow tab key to select this control
- this.TabStop = false;
- // Default the Font we use
- this.Font = SystemInformation.MenuFont;
- // Calculate the initial height/width of the control
- rowWidth = rowHeight = this.Font.Height + breadthGap * 2 + 1;
- // Default to one line of items
- this.Height = rowHeight;
- // Add ourself to the application filtering list
- Application.AddMessageFilter(this);
- }
- #endregion
- #region Overrides
- protected override void OnMouseDown(MouseEventArgs e)
- {
- Point pos = new Point(e.X, e.Y);
- for(int i=0; i< drawCommands.Count; i++)
- {
- DrawCommand dc = drawCommands[i] as DrawCommand;
- // Find the DrawCommand this is over
- if (dc.DrawRect.Contains(pos))
- {
- // Is an item already selected?
- if ( selected)
- {
- // Is it this item that is already selected?
- if ( trackItem == i)
- {
- // Is a popupMenu showing
- if ( popupMenu != null)
- {
- // Dismiss the submenu
- popupMenu.Dismiss();
- // No reference needed
- popupMenu = null;
- }
- }
- }
- else
- {
- // Select the tracked item
- selected = true;
- drawUpwards = false;
-
- GrabTheFocus();
-
- // Is there a change in tracking?
- if ( trackItem != i)
- {
- // Modify the display of the two items
- trackItem = SwitchTrackingItem( trackItem, i);
- }
- else
- {
- // Update display to show as selected
- DrawCommand( trackItem, true);
- }
- // Is there a submenu to show?
- if (dc.Chevron || (dc.MenuCommand.MenuCommands.Count > 0))
- WindowsAPI.PostMessage(this.Handle, WM_OPERATEMENU, 1, 0);
- }
- break;
- }
- }
- base.OnMouseDown(e);
- }
- protected override void OnMouseUp(MouseEventArgs e)
- {
- // Is an item currently being tracked?
- if ( trackItem != -1)
- {
- // Is it also selected?
- if ( selected == true)
- {
- // Is it also showing a submenu
- if ( popupMenu == null)
- {
- // Deselect the item
- selected = false;
- drawUpwards = false;
- DrawCommand( trackItem, true);
- ReturnTheFocus();
- }
- }
- }
- base.OnMouseUp(e);
- }
- protected override void OnMouseMove(MouseEventArgs e)
- {
- // Sometimes we need to ignore this message
- if ( ignoreMouseMove)
- ignoreMouseMove = false;
- else
- {
- // Is the first time we have noticed a mouse movement over our window
- if (! mouseOver)
- {
- // Crea the structure needed for WindowsAPI call
- Win32.TRACKMOUSEEVENTS tme = new Win32.TRACKMOUSEEVENTS();
- // Fill in the structure
- tme.cbSize = 16;
- tme.dwFlags = Win32.TrackerEventFlags.TME_LEAVE;
- tme.hWnd = this.Handle;
- tme.dwHoverTime = 0;
- // Request that a message gets sent when mouse leaves this window
- WindowsAPI.TrackMouseEvent(ref tme);
- // Yes, we know the mouse is over window
- mouseOver = true;
- }
- Form parentForm = this.FindForm();
- // Only hot track if this Form is active
- if ((parentForm != null) && parentForm.ContainsFocus)
- {
- Point pos = new Point(e.X, e.Y);
- int i = 0;
- for(i=0; i< drawCommands.Count; i++)
- {
- DrawCommand dc = drawCommands[i] as DrawCommand;
- // Find the DrawCommand this is over
- if (dc.DrawRect.Contains(pos))
- {
- // Is there a change in selected item?
- if ( trackItem != i)
- {
- // We we currently selecting an item
- if ( selected)
- {
- if ( popupMenu != null)
- {
- // Note that we are dismissing the submenu but not removing
- // the selection of items, this flag is tested by the routine
- // that will return because the submenu has been finished
- dismissTransfer = true;
- // Dismiss the submenu
- popupMenu.Dismiss();
-
- // Default to downward drawing
- drawUpwards = false;
- }
- // Modify the display of the two items
- trackItem = SwitchTrackingItem( trackItem, i);
- // Does the newly selected item have a submenu?
- if (dc.Chevron || (dc.MenuCommand.MenuCommands.Count > 0))
- WindowsAPI.PostMessage(this.Handle, WM_OPERATEMENU, 1, 0);
- }
- else
- {
- // Modify the display of the two items
- trackItem = SwitchTrackingItem( trackItem, i);
- }
- }
- break;
- }
- }
- // If not in selected mode
- if (! selected)
- {
- // None of the commands match?
- if (i == drawCommands.Count)
- {
- // Modify the display of the two items
- trackItem = SwitchTrackingItem( trackItem, -1);
- }
- }
- }
- }
- base.OnMouseMove(e);
- }
- protected override void OnMouseLeave(EventArgs e)
- {
- mouseOver = false;
- // If we manually grabbed focus then do not switch
- // selection when the mouse leaves the control area
- if (! manualFocus)
- {
- if ( trackItem != -1)
- {
- // If an item is selected then do not change tracking item when the
- // mouse leaves the control area, as a popup menu might be showing and
- // so keep the tracking and selection indication visible
- if ( selected == false)
- trackItem = SwitchTrackingItem( trackItem, -1);
- }
- }
- base.OnMouseLeave(e);
- }
- protected override void OnResize(EventArgs e)
- {
- Recalculate();
- // Any resize of control should redraw all of it otherwise when you
- // stretch to the right it will not paint correctly as we have a one
- // pixel gao between text and min button which is not honoured otherwise
- this.Invalidate();
- base.OnResize(e);
- }
- protected override void OnLostFocus(EventArgs e)
- {
- // We lost the focus
- manualFocus = false;
- // Remove tracking of any item
- trackItem = SwitchTrackingItem( trackItem, -1);
- // No need to remember old focus anymore
- oldFocus = IntPtr.Zero;
- // When we lose focus we sometimes miss the WM MOUSELEAVE message
- mouseOver = false;
- base.OnLostFocus(e);
- }
- protected override void OnPaint(PaintEventArgs e)
- {
- DrawAllCommands(e.Graphics);
- base.OnPaint(e);
- }
- protected override void OnKeyDown(KeyEventArgs e)
- {
- switch(e.KeyCode)
- {
- case Keys.Escape:
- // Is an item being tracked
- if ( trackItem != -1)
- {
- // Is that item also selected
- if ( selected)
- {
- // Is it also showing a submenu
- if ( popupMenu == null)
- {
- // Deselect the item
- selected = false;
- drawUpwards = false;
- // Repaint the item
- DrawCommand( trackItem, true);
- ReturnTheFocus();
- // Key has been processed
- e.Handled = true;
- }
- else
- {
- // The popup menu will swallow the escape
- ignoreEscapeUp = true;
- }
- }
- }
- break;
- case Keys.Left:
- if ( direction == Direction.Horizontal)
- ProcessMoveLeft(false);
- if ( selected)
- ignoreMouseMove = true;
- break;
- case Keys.Right:
- if ( direction == Direction.Horizontal)
- ProcessMoveRight(false);
- else
- ProcessMoveDown();
- if ( selected)
- ignoreMouseMove = true;
- break;
- case Keys.Down:
- if ( direction == Direction.Horizontal)
- ProcessMoveDown();
- else
- ProcessMoveRight(false);
- break;
- case Keys.Up:
- if ( direction == Direction.Vertical)
- ProcessMoveLeft(false);
- break;
- }
- base.OnKeyDown(e);
- }
- protected override void OnKeyUp(KeyEventArgs e)
- {
- switch(e.KeyCode)
- {
- case Keys.Escape:
- if ( manualFocus)
- {
- if (! selected)
- {
- // Did we see the key down for escape? If not then the
- // escape must have been used to dismiss a popup menu
- if (! ignoreEscapeUp)
- {
- // No longer in manual focus mode
- manualFocus = false;
-
- // Remove tracking of any item
- trackItem = SwitchTrackingItem( trackItem, -1);
- // Send the focus back to origin
- ReturnTheFocus();
- }
- }
- }
-
- ignoreEscapeUp = false;
- break;
- default:
- ProcessMnemonicKey((char)e.KeyValue);
- if ( selected)
- ignoreMouseMove = true;
- break;
- }
- base.OnKeyUp(e);
- }
- protected override void WndProc(ref Message m)
- {
- // WM OPERATEMENU is not a constant and so cannot be in a switch
- if (m.Msg == WM_OPERATEMENU)
- OnOperateMenu(ref m);
- else
- {
- switch(m.Msg)
- {
- case (int)Win32.Msg.WM_GETDLGCODE:
- OnGetDlgCode(ref m);
- return;
- }
- }
- base.WndProc(ref m);
- }
- #endregion
-
- #region Properties
- [Category("Behaviour")]
- [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
- public MenuCommandCollection MenuCommands
- {
- get { return menuCommands; }
- set
- {
- menuCommands.Clear();
- menuCommands = value;
- Recalculate();
- Invalidate();
- }
- }
- [Category("Appearance")]
- public VisualStyle Style
- {
- get { return style; }
-
- set
- {
- if ( style != value)
- {
- style = value;
- Recalculate();
- Invalidate();
- }
- }
- }
- [Category("Appearance")]
- public override Font Font
- {
- get { return base.Font; }
-
- set
- {
- base.Font = value;
- // Resize to take into accout the new height
- rowHeight = this.Font.Height + lengthGap * 2;
- Recalculate();
- Invalidate();
- }
- }
- [Category("Appearance")]
- [DefaultValue(false)]
- public bool PlainAsBlock
- {
- get { return plainAsBlock; }
- set
- {
- if ( plainAsBlock != value)
- {
- plainAsBlock = value;
- Recalculate();
- Invalidate();
- }
- }
- }
- [Category("Appearance")]
- [DefaultValue(false)]
- public bool MultiLine
- {
- get { return multiLine; }
- set
- {
- if ( multiLine != value)
- {
- multiLine = value;
- Recalculate();
- Invalidate();
- }
- }
- }
- [Category("Appearance")]
- public Direction Direction
- {
- get { return direction; }
- set
- {
- if ( direction != value)
- {
- direction = value;
- Recalculate();
- Invalidate();
- }
- }
- }
- public override DockStyle Dock
- {
- get { return base.Dock; }
- set
- {
- base.Dock = value;
- switch(value)
- {
- case DockStyle.None:
- direction = Direction.Horizontal;
- break;
- case DockStyle.Top:
- case DockStyle.Bottom:
- this.Height = 0;
- direction = Direction.Horizontal;
- break;
- case DockStyle.Left:
- case DockStyle.Right:
- this.Width = 0;
- direction = Direction.Vertical;
- break;
- }
- Recalculate();
- Invalidate();
- }
- }
- #endregion
- #region Methods
- public bool PreFilterMessage(ref Message msg)
- {
- Form parentForm = this.FindForm();
- // Only interested if the Form we are on is activate (i.e. contains focus)
- if ((parentForm != null) && parentForm.ContainsFocus)
- {
- switch(msg.Msg)
- {
- case (int)Win32.Msg.WM_MDISETMENU:
- case (int)Win32.Msg.WM_MDIREFRESHMENU:
- return true;
- case (int)Win32.Msg.WM_KEYUP:
- {
- // Find up/down state of shift and control keys
- ushort shiftKey = WindowsAPI.GetKeyState((int)Win32.VirtualKeys.VK_SHIFT);
- ushort controlKey = WindowsAPI.GetKeyState((int)Win32.VirtualKeys.VK_CONTROL);
- // Basic code we are looking for is the key pressed...
- int code = (int)msg.WParam;
- // ...plus the modifier for SHIFT...
- if (((int)shiftKey & 0x00008000) != 0)
- code += 0x00010000;
- // ...plus the modifier for CONTROL
- if (((int)controlKey & 0x00008000) != 0)
- code += 0x00020000;
- // Construct shortcut from keystate and keychar
- Shortcut sc = (Shortcut)(code);
-
- // Search for a matching command
- return GenerateShortcut(sc, menuCommands);
- }
- case (int)Win32.Msg.WM_SYSKEYUP:
- if ((int)msg.WParam == (int)Win32.VirtualKeys.VK_MENU)
- {
- // If not already in manual focus mode
- if (! manualFocus)
- {
- // Are there any menu commands?
- if ( drawCommands.Count > 0)
- {
- // If no item is currently tracked then...
- if ( trackItem == -1)
- {
- // ...start tracking the first valid command
- for(int i=0; i< drawCommands.Count; i++)
- {
- DrawCommand dc = drawCommands[i] as DrawCommand;
-
- if (!dc.Separator && (dc.Chevron || dc.MenuCommand.Enabled))
- {
- trackItem = SwitchTrackingItem(-1, i);
- break;
- }
- }
- }
-
- // We keep the focus until they move it
- manualFocus = true;
-
- // Grab the focus for key events
- GrabTheFocus();
- }
- return true;
- }
- }
- else
- {
- // Construct shortcut from ALT + keychar
- Shortcut sc = (Shortcut)(0x00040000 + (int)msg.WParam);
-
- if (GenerateShortcut(sc, menuCommands))
- return true;
-
- // Last resort is treat as a potential mnemonic
- return ProcessMnemonicKey((char)msg.WParam);
- }
- break;
- default:
- break;
- }
- }
- return false;
- }
- #endregion
- #region Implementation
- protected void OnPopupStart(MenuCommand mc)
- {
- if (PopupStart != null)
- PopupStart(mc);
- }
-
- protected void OnPopupEnd(MenuCommand mc)
- {
- if (PopupEnd != null)
- PopupEnd(mc);
- }
- protected void OnCollectionCleared()
- {
- // Reset state ready for a recalculation
- selected = false;
- trackItem = -1;
- Recalculate();
- Invalidate();
- }
- protected void OnCollectionInserted(int index, object value)
- {
- MenuCommand mc = value as MenuCommand;
- // We need notification whenever the properties of this command change
- mc.PropertyChanged += new MenuCommand.PropChangeHandler(OnCommandChanged);
-
- // Reset state ready for a recalculation
- selected = false;
- trackItem = -1;
- Recalculate();
- Invalidate();
- }
- protected void OnCollectionRemoved(int index, object value)
- {
- // Reset state ready for a recalculation
- selected = false;
- trackItem = -1;
- Recalculate();
- Invalidate();
- }
- protected void OnCommandChanged(MenuCommand item, MenuCommand.Property prop)
- {
- Recalculate();
- Invalidate();
- }
- internal void DrawSelectionUpwards()
- {
- // Double check the state is correct for this method to be called
- if (( trackItem != -1) && ( selected))
- {
- // This flag is tested in the DrawCommand method
- drawUpwards = true;
- // Force immediate redraw of the item
- DrawCommand( trackItem, true);
- }
- }
- protected void Recalculate()
- {
- int length;
- if ( direction == Direction.Horizontal)
- length = this.Width;
- else
- length = this.Height;
- // Is there space for any commands?
- if (length > 0)
- {
- // Count the number of rows needed
- int rows = 0;
- // Number of items on this row
- int columns = 0;
- // Create a collection of drawing objects
- drawCommands = new ArrayList();
- // Minimum length is a gap on either side of the text
- int cellMinLength = lengthGap * 2;
- // Each cell is as broad as the whole control
- int cellBreadth = this.Height;
-
- // Accumulate starting position of each cell
- int lengthStart = 0;
- // If the chevron is already displayed then reduce length by its length
- if ( chevronStartCommand != null)
- length -= cellMinLength + chevronLength;
- // Assume chevron is not needed by default
- chevronStartCommand = null;
- using(Graphics g = this.CreateGraphics())
- {
- // Count the item we are processing
- int index = 0;
- foreach(MenuCommand command in menuCommands)
- {
- // Give the command a chance to update its state
- command.OnUpdate(EventArgs.Empty);
- // Ignore items that are marked as hidden
- if (!command.Visible)
- continue;
- int cellLength = 0;
- // Is this a separator?
- if (command.Text == "-")
- cellLength = separatorWidth;
- else
- {
- // Calculate the text width of the cell
- SizeF dimension = g.MeasureString(command.Text, this.Font);
- // Always add 1 to ensure that rounding is up and not down
- cellLength = cellMinLength + (int)dimension.Width + 1;
- }
- Rectangle cellRect;
- // Create a new position rectangle
- if ( direction == Direction.Horizontal)
- cellRect = new Rectangle(lengthStart, rowHeight * rows, cellLength, rowHeight);
- else
- cellRect = new Rectangle( rowWidth * rows, lengthStart, rowWidth, cellLength);
- lengthStart += cellLength;
- columns++;
- // If this item is overlapping the control edge and it is not the first
- // item on the line then we should wrap around to the next row.
- if ((lengthStart > length) && (columns > 1))
- {
- if ( multiLine)
- {
- // Move to next row
- rows++;
- // Reset number of items on this column
- columns = 1;
- // Reset starting position of next item
- lengthStart = cellLength;
- // Reset position of this item
- if ( direction == Direction.Horizontal)
- {
- cellRect.X = 0;
- cellRect.Y += rowHeight;
- }
- else
- {
- cellRect.X += rowWidth;
- cellRect.Y = 0;
- }
- }
- else
- {
- // Is a tracked item being make invisible
- if (index <= trackItem)
- {
- // Need to remove tracking of this item
- trackItem = -1;
- }
- // Remember which item is first for the chevron submenu
- chevronStartCommand = command;
- if ( direction == Direction.Horizontal)
- {
- cellRect.Y = 0;
- cellRect.Width = cellMinLength + chevronLength;
- cellRect.X = this.Width - cellRect.Width;
- cellRect.Height = rowHeight;
- }
- else
- {
- cellRect.X = 0;
- cellRect.Height = cellMinLength + chevronLength;
- cellRect.Y = this.Height - (cellMinLength + chevronLength);
- cellRect.Width = rowWidth;
- }
- // Create a draw command for this chevron
- drawCommands.Add(new DrawCommand(cellRect));
- // Exit, do not add the current item or any afterwards
- break;
- }
- }
- // Create a drawing object
- drawCommands.Add(new DrawCommand(command, cellRect));
- index++;
- }
- }
- if ( direction == Direction.Horizontal)
- {
- int controlHeight = (rows + 1) * rowHeight;
- // Ensure the control is the correct height
- if (this.Height != controlHeight)
- this.Height = controlHeight;
- }
- else
- {
- int controlWidth = (rows + 1) * rowWidth;
- // Ensure the control is the correct width
- if (this.Width != controlWidth)
- this.Width = controlWidth;
- }
- }
- }
- protected void DrawCommand(int drawItem, bool tracked)
- {
- // Create a graphics object for drawing
- using(Graphics g = this.CreateGraphics())
- DrawSingleCommand(g, drawCommands[drawItem] as DrawCommand, tracked);
- }
- internal void DrawSingleCommand(Graphics g, DrawCommand dc, bool tracked)
- {
- Rectangle drawRect = dc.DrawRect;
- MenuCommand mc = dc.MenuCommand;
- // Copy the rectangle used for drawing cell
- Rectangle shadowRect = drawRect;
- // Expand to right and bottom to cover the area used to draw shadows
- shadowRect.Width += shadowGap;
- shadowRect.Height += shadowGap;
- // Draw background color over cell and shadow area to the right
- using(SolidBrush back = new SolidBrush(SystemColors.Control))
- g.FillRectangle(back, shadowRect);
- if (!dc.Separator)
- {
- Rectangle textRect;
- // Text rectangle size depends on type of draw command we are drawing
- if (dc.Chevron)
- {
- // Create chevron drawing rectangle
- textRect = new Rectangle(drawRect.Left + lengthGap, drawRect.Top + boxExpandUpper,
- drawRect.Width - lengthGap * 2, drawRect.Height - ( boxExpandUpper * 2));
- }
- else
- {
- // Create text drawing rectangle
- textRect = new Rectangle(drawRect.Left + lengthGap, drawRect.Top + lengthGap,
- drawRect.Width - lengthGap * 2, drawRect.Height - lengthGap * 2);
- }
- if (dc.Enabled)
- {
- // Draw selection
- if (tracked)
- {
- Rectangle boxRect;
- // Create the rectangle for box around the text
- if ( direction == Direction.Horizontal)
- {
- boxRect = new Rectangle(textRect.Left - boxExpandSides,
- textRect.Top - boxExpandUpper,
- textRect.Width + boxExpandSides * 2,
- textRect.Height + boxExpandUpper);
- }
- else
- {
- if (!dc.Chevron)
- {
- boxRect = new Rectangle(textRect.Left,
- textRect.Top - boxExpandSides,
- textRect.Width - boxExpandSides,
- textRect.Height + boxExpandSides * 2);
- }
- else
- boxRect = textRect;
- }
- switch( style)
- {
- case VisualStyle.IDE:
- if ( selected)
- {
- // Fill the entire inside
- g.FillRectangle(SystemBrushes.ControlLight, boxRect);
-
- int rightLeft = boxRect.Right + 1;
- int rightBottom = boxRect.Bottom;
- if ( drawUpwards && ( direction == Direction.Horizontal))
- {
- // Draw the box around the selection area
- using(Pen dark = new Pen(SystemColors.ControlDark))
- g.DrawRectangle(dark, boxRect.Left, boxRect.Top - shadowGap,
- boxRect.Width, boxRect.Height + shadowGap);
- // Remove the top line of the selection area
- using(Pen dark = new Pen(SystemColors.ControlLight))
- g.DrawLine(dark, boxRect.Left + 1, boxRect.Top, boxRect.Right - 2, boxRect.Top);
- int rightTop = boxRect.Top;
- int leftLeft = boxRect.Left + shadowGap;
- SolidBrush shadowBrush;
- // Decide if we need to use an alpha brush
- if ( supportsLayered && ( style == VisualStyle.IDE))
- shadowBrush = new SolidBrush(Color.FromArgb(64, 0, 0, 0));
- else
- shadowBrush = new SolidBrush(SystemColors.ControlDark);
- g.FillRectangle(shadowBrush, new Rectangle(rightLeft, rightTop + shadowGap - 1, shadowGap, rightBottom - rightTop - shadowGap*2));
- shadowBrush.Dispose();
- }
- else
- {
- // Draw the box around the selection area
- using(Pen dark = new Pen(SystemColors.ControlDark))
- g.DrawRectangle(dark, boxRect);
- if ( direction == Direction.Horizontal)
- {
- // Remove the bottom line of the selection area
- using(Pen dark = new Pen(SystemColors.ControlLight))
- g.DrawLine(dark, boxRect.Left, boxRect.Bottom, boxRect.Right, boxRect.Bottom);
- int rightTop = boxRect.Top + shadowYOffset;
- SolidBrush shadowBrush;
- // Decide if we need to use an alpha brush
- if ( supportsLayered && ( style == VisualStyle.IDE))
- shadowBrush = new SolidBrush(Color.FromArgb(64, 0, 0, 0));
- else
- shadowBrush = new SolidBrush(SystemColors.ControlDark);
- g.FillRectangle(shadowBrush, new Rectangle(rightLeft, rightTop, shadowGap, rightBottom - rightTop));
- shadowBrush.Dispose();
- }
- else
- {
- // Remove the right line of the selection area
- using(Pen dark = new Pen(SystemColors.ControlLight))
- g.DrawLine(dark, boxRect.Right, boxRect.Top, boxRect.Right, boxRect.Bottom);
- int leftLeft = boxRect.Left + shadowYOffset;
- SolidBrush shadowBrush;
- // Decide if we need to use an alpha brush
- if ( supportsLayered && ( style == VisualStyle.IDE))
- shadowBrush = new SolidBrush(Color.FromArgb(64, 0, 0, 0));
- else
- shadowBrush = new SolidBrush(SystemColors.ControlDark);
- g.FillRectangle(shadowBrush, new Rectangle(leftLeft, rightBottom+1, rightBottom - leftLeft - shadowGap, shadowGap));
- shadowBrush.Dispose();
- }
- }
- }
- else
- {
- using (Pen selectPen = new Pen(ColorUtil.VSNetBorderColor))
- {
- // Draw the selection area in white so can alpha draw over the top
- using (SolidBrush whiteBrush = new SolidBrush(Color.White))
- g.FillRectangle(whiteBrush, boxRect);
- using (SolidBrush selectBrush = new SolidBrush(Color.FromArgb(70, ColorUtil.VSNetBorderColor)))
- {
- // Draw the selection area
- g.FillRectangle(selectBrush, boxRect);
- // Draw a border around the selection area
- g.DrawRectangle(selectPen, boxRect);
- }
- }
- }
- break;
- case VisualStyle.Plain:
- if ( plainAsBlock)
- {
- using (SolidBrush selectBrush = new SolidBrush(ColorUtil.VSNetBorderColor))
- g.FillRectangle(selectBrush, drawRect);
- }
- else
- {
- using(Pen lighlight = new Pen(SystemColors.ControlLightLight),
- dark = new Pen(SystemColors.ControlDark))
- {
- if ( selected)
- {
- g.DrawLine(dark, boxRect.Left, boxRect.Bottom, boxRect.Left, boxRect.Top);
- g.DrawLine(dark, boxRect.Left, boxRect.Top, boxRect.Right, boxRect.Top);
- g.DrawLine(lighlight, boxRect.Right, boxRect.Top, boxRect.Right, boxRect.Bottom);
- g.DrawLine(lighlight, boxRect.Right, boxRect.Bottom, boxRect.Left, boxRect.Bottom);
- }
- else
- {
- g.DrawLine(lighlight, boxRect.Left, boxRect.Bottom, boxRect.Left, boxRect.Top);
- g.DrawLine(lighlight, boxRect.Left, boxRect.Top, boxRect.Right, boxRect.Top);
- g.DrawLine(dark, boxRect.Right, boxRect.Top, boxRect.Right, boxRect.Bottom);
- g.DrawLine(dark, boxRect.Right, boxRect.Bottom, boxRect.Left, boxRect.Bottom);
- }
- }
- }
- break;
- }
- }
- }
- if (dc.Chevron)
- {
- // Draw the chevron image in the centre of the text area
- int yPos = drawRect.Top;
- int xPos = drawRect.X + ((drawRect.Width - chevronLength) / 2);
- // When selected...
- if ( selected)
- {
- // ...offset down and to the right
- xPos += 1;
- yPos += 1;
- }
- g.DrawImage( menuImages.Images[ chevronIndex], xPos, yPos);
- }
- else
- {
- // Left align the text drawing on a single line centered vertically
- // and process the & character to be shown as an underscore on next character
- StringFormat format = new StringFormat();
- format.FormatFlags = StringFormatFlags.NoClip | StringFormatFlags.NoWrap;
- format.Alignment = StringAlignment.Center;
- format.LineAlignment = StringAlignment.Center;
- format.HotkeyPrefix = HotkeyPrefix.Show;
- if ( direction == Direction.Vertical)
- format.FormatFlags |= StringFormatFlags.DirectionVertical;
- if (dc.Enabled)
- {
- if (tracked && ( style == VisualStyle.Plain) && plainAsBlock)
- {
- // Is the item selected as well as tracked?
- if ( selected)
- {
- // Offset to show it is selected
- textRect.X += 2;
- textRect.Y += 2;
- }
- using (SolidBrush textBrush = new SolidBrush(SystemColors.HighlightText))
- g.DrawString(mc.Text, this.Font, textBrush, textRect, format);
- }
- else
- using (SolidBrush textBrush = new SolidBrush(SystemColors.MenuText))
- g.DrawString(mc.Text, this.Font, textBrush, textRect, format);
- }
- else
- {
- // Helper values used when drawing grayed text in plain style
- Rectangle rectDownRight = textRect;
- rectDownRight.Offset(1,1);
- // Draw then text offset down and right
- using (SolidBrush whiteBrush = new SolidBrush(SystemColors.HighlightText))
- g.DrawString(mc.Text, this.Font, whiteBrush, rectDownRight, format);
- // Draw then text offset up and left
- using (SolidBrush grayBrush = new SolidBrush(SystemColors.GrayText))
- g.DrawString(mc.Text, this.Font, grayBrush, textRect, format);
- }
- }
- }
- }
- protected void DrawAllCommands(Graphics g)
- {
- for(int i=0; i< drawCommands.Count; i++)
- {
- // Grab some commonly used values
- DrawCommand dc = drawCommands[i] as DrawCommand;
- // Draw this command only
- DrawSingleCommand(g, dc, (i == trackItem));
- }
- }
- protected int SwitchTrackingItem(int oldItem, int newItem)
- {
- // Create a graphics object for drawinh
- using(Graphics g = this.CreateGraphics())
- {
- // Deselect the old draw command
- if (oldItem != -1)
- {
- DrawCommand dc = drawCommands[oldItem] as DrawCommand;
- // Draw old item not selected
- DrawSingleCommand(g, drawCommands[oldItem] as DrawCommand, false);
- }
- trackItem = newItem;
- // Select the new draw command
- if ( trackItem != -1)
- {
- DrawCommand dc = drawCommands[ trackItem] as DrawCommand;
- // Draw new item selected
- DrawSingleCommand(g, drawCommands[ trackItem] as DrawCommand, true);
- }
- }
- return trackItem;
- }
- internal void OperateSubMenu(DrawCommand dc, bool selectFirst, bool trackRemove)
- {
- Rectangle drawRect = dc.DrawRect;
- // Find screen positions for popup menu
- Point screenPos;
-
- if ( style == VisualStyle.IDE)
- {
- if ( direction == Direction.Horizontal)
- screenPos = PointToScreen(new Point(dc.DrawRect.Left + 1, drawRect.Bottom - lengthGap - 1));
- else
- screenPos = PointToScreen(new Point(dc.DrawRect.Right - breadthGap, drawRect.Top + boxExpandSides - 1));
- }
- else
- {
- if ( direction == Direction.Horizontal)
- screenPos = PointToScreen(new Point(dc.DrawRect.Left + 1, drawRect.Bottom));
- else
- screenPos = PointToScreen(new Point(dc.DrawRect.Right, drawRect.Top));
- }
- Point aboveScreenPos;
-
- if ( style == VisualStyle.IDE)
- {
- if ( direction == Direction.Horizontal)
- aboveScreenPos = PointToScreen(new Point(dc.DrawRect.Left + 1, drawRect.Top + lengthGap + 1));
- else
- aboveScreenPos = PointToScreen(new Point(dc.DrawRect.Right - breadthGap, drawRect.Bottom + lengthGap));
- }
- else
- {
- if ( direction == Direction.Horizontal)
- aboveScreenPos = PointToScreen(new Point(dc.DrawRect.Left + 1, drawRect.Top));
- else
- aboveScreenPos = PointToScreen(new Point(dc.DrawRect.Right, drawRect.Bottom));
- }
- int borderGap;
- // Calculate the missing gap in the PopupMenu border
- if ( direction == Direction.Horizontal)
- borderGap = dc.DrawRect.Width - subMenuBorderAdjust;
- else
- borderGap = dc.DrawRect.Height - subMenuBorderAdjust;
-
- popupMenu = new PopupMenu();
- // Define the correct visual style based on ours
- popupMenu.Style = this.Style;
- // Key direction when keys cause dismissal
- int returnDir = 0;
- if (dc.Chevron)
- {
- MenuCommandCollection mcc = new MenuCommandCollection();
- bool addCommands = false;
- // Generate a collection of menu commands for those not visible
- foreach(MenuCommand command in menuCommands)
- {
- if (!addCommands && (command == chevronStartCommand))
- addCommands = true;
- if (addCommands)
- mcc.Add(command);
- }
- // Track the popup using provided menu item collection
- popupMenu.TrackPopup(screenPos,
- aboveScreenPos,
- direction,
- mcc,
- borderGap,
- selectFirst,
- this,
- ref returnDir);
- }
- else
- {
- // Generate event so that caller has chance to modify MenuCommand contents
- OnPopupStart(dc.MenuCommand);
- // Track the popup using provided menu item collection
- popupMenu.TrackPopup(screenPos,
- aboveScreenPos,
- direction,
- dc.MenuCommand.MenuCommands,
- borderGap,
- selectFirst,
- this,
- ref returnDir);
- // Generate event so that caller has chance to modify MenuCommand contents
- OnPopupEnd(dc.MenuCommand);
- }
- // Remove unwanted object
- popupMenu = null;
- // Was arrow key used to dismiss the submenu?
- if (returnDir != 0)
- {
- // Using keyboard movements means we should have the focus
- if (! manualFocus)
- {
- manualFocus = true;
- GrabTheFocus();
- }
- if (returnDir < 0)
- {
- // Shift selection left one
- ProcessMoveLeft(true);
- }
- else
- {
- // Shift selection right one
- ProcessMoveRight(true);
- }
- // A WM MOUSEMOVE is generated when we open up the new submenu for
- // display, ignore this as it causes the selection to move
- ignoreMouseMove = true;
- }
- else
- {
- // Only if the submenu was dismissed at the request of the submenu
- // should the selection mode be cancelled, otherwise keep selection mode
- if (! dismissTransfer)
- {
- // This item is no longer selected
- selected = false;
- drawUpwards = false;
- // Should we stop tracking this item
- if (trackRemove)
- {
- ReturnTheFocus();
- // Unselect the current item
- trackItem = SwitchTrackingItem( trackItem, -1);
- }
- else
- {
- // Repaint the item
- DrawCommand( trackItem, true);
- }
- }
- else
- {
- // Do not change selected status
- dismissTransfer = false;
- }
- }
- }
- protected void GrabTheFocus()
- {
- // Remember the current focus location
- oldFocus = WindowsAPI.GetFocus();
- // Grab the focus
- this.Focus();
- }
- protected void ReturnTheFocus()
- {
- // Do we remember where it came from?
- if ( oldFocus != IntPtr.Zero)
- {
- // Send the focus back
- WindowsAPI.SetFocus( oldFocus);
- // No need to remember old focus anymore
- oldFocus = IntPtr.Zero;
- }
- // We lost the focus
- manualFocus = false;
- }
- protected void ProcessMoveLeft(bool select)
- {
- if ( popupMenu == null)
- {
- int newItem = trackItem;
- int startItem = newItem;
- for(int i=0; i< drawCommands.Count; i++)
- {
- // Move to previous item
- newItem--;
- // Have we looped all the way around all choices
- if (newItem == startItem)
- return;
- // Check limits
- if (newItem < 0)
- newItem = drawCommands.Count - 1;
- DrawCommand dc = drawCommands[newItem] as DrawCommand;
- // Can we select this item?
- if (!dc.Separator && (dc.Chevron || dc.MenuCommand.Enabled))
- {
- // If a change has occured
- if (newItem != trackItem)
- {
- // Modify the display of the two items
- trackItem = SwitchTrackingItem( trackItem, newItem);
-
- if ( selected)
- {
- if (dc.Chevron || (dc.MenuCommand.MenuCommands.Count > 0))
- WindowsAPI.PostMessage(this.Handle, WM_OPERATEMENU, 0, 1);
- }
- break;
- }
- }
- }
- }
- }
- protected void ProcessMoveRight(bool select)
- {
- if ( popupMenu == null)
- {
- int newItem = trackItem;
- int startItem = newItem;
- for(int i=0; i< drawCommands.Count; i++)
- {
- // Move to previous item
- newItem++;
- // Check limits
- if (newItem >= drawCommands.Count)
- newItem = 0;
- DrawCommand dc = drawCommands[newItem] as DrawCommand;
- // Can we select this item?
- if (!dc.Separator && (dc.Chevron || dc.MenuCommand.Enabled))
- {
- // If a change has occured
- if (newItem != trackItem)
- {
- // Modify the display of the two items
- trackItem = SwitchTrackingItem( trackItem, newItem);
- if ( selected)
- {
- if (dc.Chevron || (dc.MenuCommand.MenuCommands.Count > 0))
- WindowsAPI.PostMessage(this.Handle, WM_OPERATEMENU, 0, 1);
- }
- break;
- }
- }
- }
- }
- }
- protected void ProcessMoveDown()
- {
- if ( popupMenu == null)
- {
- // Are we tracking an item?
- if ( trackItem != -1)
- {
- // The item must not already be selected
- if (! selected)
- {
- DrawCommand dc = drawCommands[ trackItem] as DrawCommand;
- // Is there a submenu to show?
- if (dc.Chevron || (dc.MenuCommand.MenuCommands.Count > 0))
- {
- // Select the tracked item
- selected = true;
- drawUpwards = false;
-
- // Update display to show as selected
- DrawCommand( trackItem, true);
- // Show the submenu
- WindowsAPI.PostMessage(this.Handle, WM_OPERATEMENU, 0, 1);
- }
- }
- }
- }
- }
- protected bool ProcessMnemonicKey(char key)
- {
- // We should always gain focus
- if (! manualFocus)
- {
- // We keep the focus until they move it
- manualFocus = true;
-
- GrabTheFocus();
- }
- // No current selection
- if (! selected)
- {
- // Search for an item that matches
- for(int i=0; i< drawCommands.Count; i++)
- {
- DrawCommand dc = drawCommands[i] as DrawCommand;
- // Only interested in enabled items
- if ((dc.MenuCommand != null) && dc.MenuCommand.Enabled)
- {
- // Does the character match?
- if (key == dc.Mnemonic)
- {
- // Select the tracked item
- selected = true;
- drawUpwards = false;
-
- // Is there a change in tracking?
- if ( trackItem != i)
- {
- // Modify the display of the two items
- trackItem = SwitchTrackingItem( trackItem, i);
- }
- else
- {
- // Update display to show as selected
- DrawCommand( trackItem, true);
- }
- // Is there a submenu to show?
- if (dc.Chevron || (dc.MenuCommand.MenuCommands.Count > 0))
- WindowsAPI.PostMessage(this.Handle, WM_OPERATEMENU, 0, 1);
- else
- {
- // No, pulse the Click event for the command
- dc.MenuCommand.OnClick(EventArgs.Empty);
- }
- return true;
- }
- }
- }
- }
- return false;
- }
- protected bool GenerateShortcut(Shortcut sc, MenuCommandCollection mcc)
- {
- foreach(MenuCommand mc in mcc)
- {
- // Does the command match?
- if (mc.Enabled && (mc.Shortcut == sc))
- {
- // Generate event for command
- mc.OnClick(EventArgs.Empty);
- return true;
- }
- else
- {
- // Any child items to test?
- if (mc.MenuCommands.Count > 0)
- {
- // Recursive descent of all collections
- if (GenerateShortcut(sc, mc.MenuCommands))
- return true;
- }
- }
- }
- return false;
- }
- protected void OnOperateMenu(ref Message m)
- {
- // Is there a valid item being tracted?
- if ( trackItem != -1)
- {
- DrawCommand dc = drawCommands[ trackItem] as DrawCommand;
- OperateSubMenu(dc, (m.LParam != IntPtr.Zero), (m.WParam != IntPtr.Zero));
- }
- }
- protected void OnGetDlgCode(ref Message m)
- {
- // We want to the Form to provide all keyboard input to us
- m.Result = (IntPtr)Win32.DialogCodes.DLGC_WANTALLKEYS;
- }
- #endregion
-
- }
- }