问题描述
这是一个 CLR 项目(.NET Framework 4.5)。
我有一个 ContextMenuStrip
,当在 ToolStripMenuItem
上按下鼠标右键时会显示它,因为 ToolStripMenuItem
没有 ContextMenuStrip
属性我必须设置右键单击MouseUp
事件中的事件如下所示:
private: System::Void itemToolStripMenuItem_MouseUp(System::Object^ sender,System::Windows::Forms::MouseEventArgs^ e) {
if (e->Button == System::Windows::Forms::MouseButtons::Right) {
this->contextMenuStrip1->Show(this->menuStrip1,this->menuStrip1->PointToClient(System::Drawing::Point(this->Cursor->Position.X,this->Cursor->Position.Y)));
}
}
我希望 DropDown
在单击鼠标右键后保持打开状态,我知道有一个 AutoClose
属性可以设置为 false:
private: System::Void contextMenuStrip1_opening(System::Object^ sender,System::ComponentModel::CancelEventArgs^ e) {
this->itemsToolStripMenuItem->DropDown->AutoClose = false;
}
问题是当将 AutoClose
设置为 false 时,它不会再关闭 DropDown
。我找不到用于关闭 Leave
并将 DropDown
设置回 true 的 AutoClose
事件。
我还需要在单击鼠标右键后突出显示 DropDown
项。
这就是我想要实现的目标:
解决方法
menu.DropDown.Closing += ...;
事件用于防止在右键单击项目时关闭菜单。 IsSelected
属性在派生的 ToolStripMenuItem3
类中被覆盖,以保持选中状态。
public class MyForm : Form {
MenuStrip menubar = new MenuStrip { Dock = DockStyle.Top };
ToolStripMenuItem menu = new ToolStripMenuItem("Menu");
ToolStripDropDown menuRemove = new ToolStripDropDown();
ToolStripMenuItem miRemove = new ToolStripMenuItem("Remove");
ToolStripItem miClickedItem = null;
Object tag = new Object();
public MyForm() : base() {
var item1 = new ToolStripMenuItem3("Item1");
var item2 = new ToolStripMenuItem3("Item2");
var item3 = new ToolStripMenuItem3("Item3");
menu.DropDownItems.AddRange(new [] { item1,item2,item3 });
menubar.Items.Add(menu);
this.MainMenuStrip = menubar;
Controls.Add(menubar);
menuRemove.Items.Add(miRemove);
menu.DropDown.MouseClick += DropDown_MouseClick;
menu.DropDown.Closing += DropDown_Closing;
menuRemove.Closed += menuRemove_Closed;
menuRemove.ItemClicked += menuRemove_ItemClicked;
}
private class ToolStripMenuItem3 : ToolStripMenuItem {
public ToolStripMenuItem3(String text) : base(text) { }
public override bool Selected {
get {
bool b = base.Selected;
return b || this.Tag != null;
}
}
}
void menuRemove_Closed(object sender,ToolStripDropDownClosedEventArgs e) {
if (miClickedItem != null) {
miClickedItem.Tag = null;
miClickedItem.Invalidate();
miClickedItem = null;
menu.DropDown.Refresh();
}
// There is a bug that prevents the DropDown from auto-closing.
// The menu.DropDown.Closing event is no longer received.
// Calling Visible = true fixes this problem.
menu.DropDown.Visible = true;
bool inside = menu.DropDown.Bounds.Contains(Cursor.Position);
if (e.CloseReason == ToolStripDropDownCloseReason.ItemClicked) { } // Remove clicked,keep menu open
else if (e.CloseReason == ToolStripDropDownCloseReason.AppClicked) {
if (!inside)
menu.DropDown.Close(); // mouse was clicked outside of the menu bounds
}
else
menu.DropDown.Close();
}
void menuRemove_ItemClicked(object sender,ToolStripItemClickedEventArgs e) {
if (e.ClickedItem == miRemove) {
menu.DropDown.Items.Remove(miClickedItem);
miClickedItem = null;
if (menu.DropDown.Items.Count == 0)
menu.DropDown.Close(ToolStripDropDownCloseReason.CloseCalled);
}
}
void DropDown_Closing(object sender,ToolStripDropDownClosingEventArgs e) {
e.Cancel = (miClickedItem != null);
}
void DropDown_MouseClick(object sender,MouseEventArgs e) {
var dropDown = (ToolStripDropDown) sender;
if (e.Button == MouseButtons.Right) {
Point p = e.Location;
if (miClickedItem != null) {
miClickedItem.Tag = null;
miClickedItem.Invalidate();
}
miClickedItem = dropDown.GetItemAt(p);
if (miClickedItem != null) {
// miClickedItem is null if the mouse click is on the border of the menu
miClickedItem.Tag = tag; // any non-null object
menuRemove.Show(dropDown,p);
miRemove.Select();
dropDown.Invalidate();
}
}
}
protected override void Dispose(bool disposing) {
base.Dispose(disposing);
if (disposing) {
if (menuRemove != null) {
menuRemove.Dispose();
menuRemove = null;
}
}
}
}
ToolStripMenuItemX
类:
public class MyForm2 : Form {
MenuStrip menubar = new MenuStrip { Dock = DockStyle.Top };
ToolStripMenuItem menu = new ToolStripMenuItem("Menu");
public MyForm2() : base() {
var item1 = new ToolStripMenuItemX("Item1");
var item2 = new ToolStripMenuItemX("Item2");
var item3 = new ToolStripMenuItemX("Item3");
menu.DropDownItems.AddRange(new [] { item1,item3 });
menubar.Items.Add(menu);
this.MainMenuStrip = menubar;
Controls.Add(menubar);
}
}
public class ToolStripMenuItemX : ToolStripMenuItem {
DrawItemState closeState = DrawItemState.None;
bool isHit = false;
bool cancelClose = false;
Point ptMouse = Point.Empty;
ToolStripDropDown menuCurrent;
bool dispose = false;
public ToolStripMenuItemX(String text) : base(text) {
}
protected override void OnParentChanged(ToolStrip oldParent,ToolStrip newParent) {
base.OnParentChanged(oldParent,newParent);
if (newParent is ToolStripDropDown) {
var menu = (ToolStripDropDown) newParent;
menu.Closing += menu_Closing;
menuCurrent = menu;
}
}
void menu_Closing(object sender,ToolStripDropDownClosingEventArgs e) {
if (cancelClose || isHit)
e.Cancel = true;
if (dispose) {
// after the MouseUp event,this menu_Closing is called immediately after,one last time
// unhook the event and dispose this item
menuCurrent.Closing -= menu_Closing;
menuCurrent = null;
Dispose();
}
}
protected override void OnMouseMove(MouseEventArgs mea) {
ptMouse = mea.Location;
var r = GetCloseRectangle();
closeState = (ptMouse.X >= r.X ? DrawItemState.HotLight : DrawItemState.Selected);
if (!isHit && mea.Button == MouseButtons.Left) {
// originally the user clicked the item,but then moved the mouse over the X button
// in this case,don't close the
cancelClose = (ptMouse.X >= r.X);
}
base.OnMouseMove(mea);
Invalidate();
}
protected override void OnMouseLeave(EventArgs e) {
base.OnMouseLeave(e);
closeState = DrawItemState.None;
cancelClose = false;
isHit = false;
Invalidate();
}
protected override void OnMouseDown(MouseEventArgs e) {
var r = GetCloseRectangle();
isHit = r.Contains(e.Location);
cancelClose = isHit;
base.OnMouseDown(e);
Invalidate();
}
protected override void OnMouseUp(MouseEventArgs e) {
base.OnMouseUp(e);
cancelClose = false;
// OnMouseUp is called after the 'Closing' event is processed,so setting canceClose has no effect here
var r = GetCloseRectangle();
if (isHit) {
if (r.Contains(e.Location)) {
// remove the menu item,keep the drop down menu open
menuCurrent.Items.Remove(this);
menuCurrent.Invalidate();
dispose = true; // flags this item for disposal
}
}
}
public override bool Selected {
get {
Rectangle r = GetCloseRectangle();
return (ptMouse.X >= r.X ? false : base.Selected);
}
}
private Rectangle GetCloseRectangle() {
var r = this.ContentRectangle;
int h = r.Height;
return new Rectangle(r.Right - h,r.Y,h,h);
}
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
var g = e.Graphics;
if (closeState == DrawItemState.HotLight || closeState == DrawItemState.Selected) {
var r = GetCloseRectangle();
if (closeState == DrawItemState.HotLight) {
var renderer = (ToolStripProfessionalRenderer) this.DropDown.Renderer;
var ct = renderer.ColorTable;
Color c1 = (isHit ? ct.ButtonPressedHighlight : ct.ButtonSelectedHighlight);
Color c2 = (isHit ? ct.ButtonPressedBorder : ct.ButtonSelectedBorder);
using (var b = new SolidBrush(c1))
g.FillRectangle(b,r);
using (var p = new Pen(c2))
g.DrawRectangle(p,r);
}
using (var sf = StringFormat.GenericDefault) {
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Center;
g.DrawString("x",this.Font,SystemBrushes.ActiveCaptionText,r,sf);
}
}
}
}