388 lines
15 KiB
C#
388 lines
15 KiB
C#
using System;
|
|
using System.Text;
|
|
using System.ComponentModel;
|
|
using System.Collections;
|
|
using System.Windows.Forms;
|
|
using System.Drawing;
|
|
|
|
namespace DevComponents.DotNetBar
|
|
{
|
|
/// <summary>
|
|
/// Represents the context menu bar that provides the context menus for the System.Windows.Forms.Control inherited controls on the form.
|
|
/// </summary>
|
|
[ToolboxItem(true), Designer("DevComponents.DotNetBar.Design.ContextMenuBarDesigner, DevComponents.DotNetBar.Design, Version=14.1.0.37, Culture=neutral, PublicKeyToken=90f470f34c89ccaf"), ProvideProperty("ContextMenuEx", typeof(Control)), System.Runtime.InteropServices.ComVisible(false), DefaultEvent("ItemClick")]
|
|
public class ContextMenuBar : Bar, System.ComponentModel.IExtenderProvider
|
|
{
|
|
#region Private Variables
|
|
private Hashtable m_ContextExMenus = new Hashtable();
|
|
private Hashtable m_ContextExHandlers = new Hashtable();
|
|
private bool m_ContextMenuSubclass = true;
|
|
#endregion
|
|
|
|
#region Internal Implementation
|
|
public ContextMenuBar() : base()
|
|
{
|
|
this.Visible = false;
|
|
this.WrapItemsDock = true;
|
|
this.WrapItemsFloat = true;
|
|
}
|
|
|
|
protected override bool IsContextPopup(BaseItem popup)
|
|
{
|
|
if (this.Items.Contains(popup)) return true;
|
|
return base.IsContextPopup(popup);
|
|
}
|
|
#endregion
|
|
|
|
#region Property Hiding
|
|
/// <summary>
|
|
/// Gets/Sets whether Bar is visible or not.
|
|
/// </summary>
|
|
[DevCoBrowsable(false), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
|
public new bool Visible
|
|
{
|
|
get { return base.Visible; }
|
|
set { base.Visible = value; }
|
|
}
|
|
#endregion
|
|
|
|
#region Extender Implementation
|
|
// *********************************************************************
|
|
//
|
|
// Extended Property ContextMenuEx implementation code
|
|
//
|
|
// *********************************************************************
|
|
bool IExtenderProvider.CanExtend(object target)
|
|
{
|
|
if (target is Control)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
private delegate void WmContextEventHandler(object sender, WmContextEventArgs e);
|
|
/// <summary>
|
|
/// Returns the instance of the BaseItem that is assigned as context menu to the control.
|
|
/// </summary>
|
|
/// <param name="control">Control to return context menu for.</param>
|
|
/// <returns>Instance of the BaseItem used as context menu for the control.</returns>
|
|
[DefaultValue(null), Editor(typeof(ContextExMenuTypeEditor), typeof(System.Drawing.Design.UITypeEditor))]
|
|
public BaseItem GetContextMenuEx(Control control)
|
|
{
|
|
if (control is DevComponents.DotNetBar.Controls.RichTextBoxEx)
|
|
control = ((DevComponents.DotNetBar.Controls.RichTextBoxEx)control).RichTextBox;
|
|
|
|
BaseItem item = (BaseItem)m_ContextExMenus[control];
|
|
return item;
|
|
}
|
|
/// <summary>
|
|
/// Assigns the context menu to a control.
|
|
/// </summary>
|
|
/// <param name="control">Control to assign the context menu to.</param>
|
|
/// <param name="value">Instance of PopupItem derived class usually ButtonItem to act as context menu for a control. The SubItems collection of the item specified here actually defines the visible context menu items.</param>
|
|
public void SetContextMenuEx(Control control, BaseItem value)
|
|
{
|
|
if (control is DevComponents.DotNetBar.Controls.RichTextBoxEx)
|
|
control = ((DevComponents.DotNetBar.Controls.RichTextBoxEx)control).RichTextBox;
|
|
if (value == null)
|
|
{
|
|
if (m_ContextExMenus.Contains(control))
|
|
{
|
|
if (m_ContextExHandlers.Contains(control))
|
|
{
|
|
ContextMessageHandler h = m_ContextExHandlers[control] as ContextMessageHandler;
|
|
if (h != null)
|
|
{
|
|
h.ContextMenu -= new WmContextEventHandler(this.OnContextMenu);
|
|
h.ReleaseHandle();
|
|
h = null;
|
|
}
|
|
m_ContextExHandlers.Remove(control);
|
|
}
|
|
|
|
m_ContextExMenus.Remove(control);
|
|
control.MouseUp -= new MouseEventHandler(this.ContextExMouseUp);
|
|
try
|
|
{
|
|
control.HandleDestroyed -= new EventHandler(this.ContextExHandleDestroy);
|
|
}
|
|
catch { }
|
|
try
|
|
{
|
|
control.HandleCreated -= new EventHandler(this.ContextExHandleCreate);
|
|
}
|
|
catch { }
|
|
try { control.MouseUp -= new MouseEventHandler(this.ContextExMouseUp); }
|
|
catch { }
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (m_ContextExMenus.Contains(control))
|
|
{
|
|
m_ContextExMenus[control] = value;
|
|
}
|
|
else
|
|
{
|
|
m_ContextExMenus[control] = value;
|
|
if (!m_ContextExHandlers.Contains(control) && !this.DesignMode)
|
|
{
|
|
if (!(control is System.Windows.Forms.TreeView) && !(control is System.Windows.Forms.Form) &&
|
|
!(control is System.Windows.Forms.Panel
|
|
#if FRAMEWORK20
|
|
|| control is System.Windows.Forms.DataGridView
|
|
#endif
|
|
) &&
|
|
m_ContextMenuSubclass)
|
|
{
|
|
if (control.IsHandleCreated)
|
|
{
|
|
ContextMessageHandler h = new ContextMessageHandler();
|
|
h.ContextMenu += new WmContextEventHandler(this.OnContextMenu);
|
|
h.ParentControl = control;
|
|
h.AssignHandle(control.Handle);
|
|
m_ContextExHandlers[control] = h;
|
|
}
|
|
control.HandleDestroyed += new EventHandler(this.ContextExHandleDestroy);
|
|
control.HandleCreated += new EventHandler(this.ContextExHandleCreate);
|
|
}
|
|
if (control is ComboBox)
|
|
{
|
|
ComboBox cbo = control as ComboBox;
|
|
cbo.ContextMenu = new ContextMenu();
|
|
}
|
|
}
|
|
try { control.MouseUp += new MouseEventHandler(this.ContextExMouseUp); }
|
|
catch { }
|
|
}
|
|
}
|
|
}
|
|
|
|
internal bool HasContextMenu(Control ctrl)
|
|
{
|
|
return m_ContextExMenus.Contains(ctrl);
|
|
}
|
|
|
|
private void ContextExHandleDestroy(object sender, EventArgs e)
|
|
{
|
|
Control control = sender as Control;
|
|
if (control == null)
|
|
return;
|
|
ContextMessageHandler h = m_ContextExHandlers[control] as ContextMessageHandler;
|
|
if (h != null)
|
|
{
|
|
h.ContextMenu -= new WmContextEventHandler(this.OnContextMenu);
|
|
h.ReleaseHandle();
|
|
h = null;
|
|
}
|
|
m_ContextExHandlers.Remove(control);
|
|
}
|
|
|
|
private void ContextExHandleCreate(object sender, EventArgs e)
|
|
{
|
|
Control control = sender as Control;
|
|
if (control == null)
|
|
return;
|
|
if (m_ContextExHandlers.Contains(control))
|
|
return;
|
|
|
|
ContextMessageHandler h = new ContextMessageHandler();
|
|
h.ContextMenu += new WmContextEventHandler(this.OnContextMenu);
|
|
h.ParentControl = control;
|
|
h.AssignHandle(control.Handle);
|
|
m_ContextExHandlers[control] = h;
|
|
}
|
|
|
|
private void ContextExMouseUp(object sender, MouseEventArgs e)
|
|
{
|
|
if (e.Button != MouseButtons.Right)
|
|
return;
|
|
Control ctrl = sender as Control;
|
|
if (ctrl == null)
|
|
return;
|
|
// Find it in pop-ups
|
|
PopupItem popup = GetContextExItem(ctrl) as PopupItem;
|
|
if (popup == null) return;
|
|
|
|
if (!popup.Expanded)
|
|
{
|
|
popup.Style = this.Style;
|
|
popup.SetSourceControl(ctrl);
|
|
popup.Popup(ctrl.PointToScreen(new Point(e.X, e.Y)));
|
|
}
|
|
}
|
|
|
|
private Control FindControl(Control parent, IntPtr handle)
|
|
{
|
|
foreach (Control ctrl in parent.Controls)
|
|
{
|
|
if (ctrl.Handle == handle)
|
|
return ctrl;
|
|
if (ctrl.Controls.Count > 0)
|
|
{
|
|
Control ret = FindControl(ctrl, handle);
|
|
if (ret != null)
|
|
return ret;
|
|
}
|
|
}
|
|
return parent;
|
|
}
|
|
|
|
private BaseItem GetContextExItem(Control ctrl)
|
|
{
|
|
BaseItem item = (BaseItem)m_ContextExMenus[ctrl];
|
|
return item;
|
|
}
|
|
private void OnContextMenu(object sender, WmContextEventArgs e)
|
|
{
|
|
ContextMessageHandler h = sender as ContextMessageHandler;
|
|
if (h == null)
|
|
return;
|
|
|
|
BaseItem contextItem = GetContextExItem(h.ParentControl);
|
|
if (contextItem == null)
|
|
return;
|
|
|
|
// Check whether context menu originated elsewhere in particular a Panel which handles MouseUp
|
|
if (e.Handle != e.SourceHandle && e.SourceHandle != IntPtr.Zero)
|
|
{
|
|
Control ctrl = Control.FromChildHandle(e.Handle);
|
|
if (ctrl != null && ctrl is Panel && this.GetContextExItem(ctrl) != null) return; // WM_CONTEXT bubbles up and Panel handles MouseUp
|
|
}
|
|
|
|
// Find it in pop-ups
|
|
PopupItem popup = contextItem as PopupItem;
|
|
popup.Style = this.Style;
|
|
|
|
if (e.Button == MouseButtons.None)
|
|
{
|
|
// Get the control with focus
|
|
Control ctrl = Control.FromChildHandle(e.Handle);
|
|
if (ctrl != null && ctrl.Handle != e.Handle)
|
|
{
|
|
ctrl = FindControl(ctrl, e.Handle);
|
|
}
|
|
popup.SetSourceControl(h.ParentControl);
|
|
if (ctrl != null)
|
|
{
|
|
if (ctrl.ClientRectangle.Contains(ctrl.PointToClient(Control.MousePosition)))
|
|
popup.Popup(Control.MousePosition);
|
|
else
|
|
popup.Popup(ctrl.PointToScreen(Point.Empty));
|
|
}
|
|
else
|
|
popup.Popup(Control.MousePosition);
|
|
|
|
// We need to eat the message in OnSysKeyUp for Shift+F10 case
|
|
if (this.IgnoreSysKeyUp)
|
|
{
|
|
this.IgnoreSysKeyUp = false;
|
|
this.EatSysKeyUp = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// This is handled by the WM_RBUTTONUP just eat it
|
|
if (!e.WmContext)
|
|
{
|
|
popup.SetSourceControl(h.ParentControl);
|
|
popup.Popup(e.X, e.Y);
|
|
}
|
|
}
|
|
e.Handled = true;
|
|
}
|
|
private class ContextMessageHandler : NativeWindow
|
|
{
|
|
public event WmContextEventHandler ContextMenu;
|
|
private const int WM_CONTEXTMENU = 0x007B;
|
|
private const int WM_RBUTTONUP = 0x0205;
|
|
private const int WM_NCRBUTTONUP = 0x00A5;
|
|
private const int WM_RBUTTONDOWN = 0x0204;
|
|
public Control ParentControl = null;
|
|
protected override void WndProc(ref Message m)
|
|
{
|
|
if (m.Msg == WM_CONTEXTMENU)
|
|
{
|
|
if (ContextMenu != null)
|
|
{
|
|
int ilParam = WinApi.ToInt(m.LParam);
|
|
int y = ilParam >> 16;
|
|
int x = (short)(ilParam & 0xFFFF);
|
|
IntPtr hWnd = m.WParam;
|
|
if (hWnd == IntPtr.Zero)
|
|
hWnd = m.HWnd;
|
|
bool context = true;
|
|
if (m.HWnd != m.WParam)
|
|
context = false;
|
|
WmContextEventArgs e = new WmContextEventArgs(hWnd, x, y, ((x == -1 && y == -1) ? MouseButtons.None : MouseButtons.Right), context, m.HWnd);
|
|
ContextMenu(this, e);
|
|
if (e.Handled)
|
|
return;
|
|
}
|
|
}
|
|
// This case was taken out becouse the message was not generated for the listview control and possibly for
|
|
// treview control so this code was moved to the MouseUp event of the Control see ContextExMouseUp
|
|
// else if(m.Msg==WM_RBUTTONUP || m.Msg==WM_NCRBUTTONUP)
|
|
// {
|
|
// if(ContextMenu!=null)
|
|
// {
|
|
// int ilParam=m.LParam.ToInt32();
|
|
// int y=ilParam>>16;
|
|
// int x=ilParam & 0xFFFF;
|
|
// Point p=ParentControl.PointToScreen(new Point(x,y));
|
|
// WmContextEventArgs e=new WmContextEventArgs(m.HWnd,p.X,p.Y,MouseButtons.Right,false);
|
|
// ContextMenu(this,e);
|
|
// }
|
|
// }
|
|
base.WndProc(ref m);
|
|
}
|
|
}
|
|
private class WmContextEventArgs : EventArgs
|
|
{
|
|
private readonly int x;
|
|
private readonly int y;
|
|
private readonly MouseButtons button = 0;
|
|
private readonly IntPtr hwnd;
|
|
private readonly bool wmcontext;
|
|
public bool Handled = false;
|
|
private IntPtr sourceHandle;
|
|
public WmContextEventArgs(IntPtr phwnd, int ix, int iy, MouseButtons eButton, bool WmContextMessage, IntPtr sourceHandle)
|
|
{
|
|
this.x = ix;
|
|
this.y = iy;
|
|
this.button = eButton;
|
|
this.hwnd = phwnd;
|
|
this.wmcontext = WmContextMessage;
|
|
this.sourceHandle = sourceHandle;
|
|
}
|
|
public int X
|
|
{
|
|
get { return this.x; }
|
|
}
|
|
public int Y
|
|
{
|
|
get { return this.y; }
|
|
}
|
|
public MouseButtons Button
|
|
{
|
|
get { return this.button; }
|
|
}
|
|
public IntPtr Handle
|
|
{
|
|
get { return this.hwnd; }
|
|
}
|
|
public bool WmContext
|
|
{
|
|
get { return this.wmcontext; }
|
|
}
|
|
public IntPtr SourceHandle
|
|
{
|
|
get
|
|
{
|
|
return this.sourceHandle;
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
}
|
|
}
|