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
}
}