我希望实现一个Visual Studio风格的撤消下拉按钮:
我已经浏览了整个互联网,似乎找不到任何真正的实现。
我是从ToolStripSplitButton
派生出来的,但并不知道从哪里去。它的DropDown
属性是ToolStripDropDown
,但似乎没有任何关于选中多个项目的内容,更不用说滚动了,还有底部的文字。
因此,我不是默认的ToolStripDropDown,而是认为整个下拉部分应该是基于组合框的自定义控件。那么问题是,如何使右侧(下拉箭头)按钮执行除显示其默认下拉菜单之外的其他操作?
我在这里走在正确的轨道上吗?谢谢!
答案 0 :(得分:4)
是的,我认为你走在了正确的轨道上。在这种情况下,ToolStripControlHost
是您的朋友。
您不一定需要从中获取(除非您自己控制),但尝试只订阅ToolStripSplitButton's
DropDownOpening
事件:
工作示例:
private ListBox listBox1;
public Form1()
{
InitializeComponent();
listBox1 = new ListBox();
listBox1.IntegralHeight = false;
listBox1.MinimumSize = new Size(120, 120); \\ <- important
listBox1.Items.Add("Item 1");
listBox1.Items.Add("Item 2");
}
private void toolStripSplitButton1_DropDownOpening(object sender, EventArgs e) {
ToolStripControlHost toolHost = new ToolStripControlHost(listBox1);
toolHost.Size = new Size(120, 120);
toolHost.Margin = new Padding(0);
ToolStripDropDown toolDrop = new ToolStripDropDown();
toolDrop.Padding = new Padding(0);
toolDrop.Items.Add(toolHost);
toolDrop.Show(this, new Point(toolStripSplitButton1.Bounds.Left,
toolStripSplitButton1.Bounds.Bottom));
}
结果如下:
对于您的应用,您需要将ListBox
替换为您自己的UserControl
,这样您就可以包含您想要的任何内容。 ToolStripControlHost
只能容纳一个控件,设置MinimumSize
属性非常重要,否则删除的控件的大小不正确。
答案 1 :(得分:4)
非常感谢LarsTech! (几小时前我还不知道ToolStripControlHost)
这是我的实现,它非常接近VS下拉...
你应该可以放弃这个代表&amp;功能进入你的表格:
public delegate void UndoRedoCallback(int count);
private void DrawDropDown(ToolStripSplitButton button, string action, IEnumerable<string> commands, UndoRedoCallback callback)
{
int width = 277;
int listHeight = 181;
int textHeight = 29;
Panel panel = new Panel()
{
Size = new Size(width, textHeight + listHeight),
Padding = new Padding(0),
Margin = new Padding(0),
BorderStyle = BorderStyle.FixedSingle,
};
Label label = new Label()
{
Size = new Size(width, textHeight),
Location = new Point(1, listHeight - 2),
TextAlign = ContentAlignment.MiddleCenter,
Text = String.Format("{0} 1 Action", action),
Padding = new Padding(0),
Margin = new Padding(0),
};
ListBox list = new ListBox()
{
Size = new Size(width, listHeight),
Location = new Point(1,1),
SelectionMode = SelectionMode.MultiSimple,
ScrollAlwaysVisible = true,
Padding = new Padding(0),
Margin = new Padding(0),
BorderStyle = BorderStyle.None,
Font = new Font(panel.Font.FontFamily, 9),
};
foreach (var item in commands) { list.Items.Add(item); }
if (list.Items.Count == 0) return;
list.SelectedIndex = 0;
ToolStripControlHost toolHost = new ToolStripControlHost(panel)
{
Size = panel.Size,
Margin = new Padding(0),
};
ToolStripDropDown toolDrop = new ToolStripDropDown()
{
Padding = new Padding(0),
};
toolDrop.Items.Add(toolHost);
panel.Controls.Add(list);
panel.Controls.Add(label);
toolDrop.Show(this, new Point(button.Bounds.Left + button.Owner.Left, button.Bounds.Bottom + button.Owner.Top));
// *Note: These will be "up values" that will exist beyond the scope of this function
int index = 1;
int lastIndex = 1;
list.Click += (sender, e) => { toolDrop.Close(); callback(index); };
list.MouseMove += (sender, e) =>
{
index = Math.Max(1, list.IndexFromPoint(e.Location) + 1);
if (lastIndex != index)
{
int topIndex = Math.Max(0, Math.Min(list.TopIndex + e.Delta, list.Items.Count - 1));
list.BeginUpdate();
list.ClearSelected();
for (int i = 0; i < index; ++i) { list.SelectedIndex = i; }
label.Text = String.Format("{0} {1} Action{2}", action, index, index == 1 ? "" : "s");
lastIndex = index;
list.EndUpdate();
list.TopIndex = topIndex;
}
};
list.Focus();
}
你可以设置它并像这样进行测试,假设你有一个空白表单(Form1),其中toolStrip添加了1个ToolStripSplitButton(toolStripSplitButton1):
public Form1()
{
InitializeComponent();
// Call DrawDropDown with:
// The clicked ToolStripSplitButton
// "Undo" as the action
// TestDropDown for the enumerable string source for the list box
// UndoCommands for the click callback
toolStripSplitButton1.DropDownOpening += (sender, e) => { DrawDropDown(
toolStripSplitButton1,
"Undo",
TestDropDown,
UndoCommands
); };
}
private IEnumerable<string> TestDropDown
{
// Provides a list of strings for testing the drop down
get { for (int i = 1; i < 1000; ++i) { yield return "test " + i; } }
}
private void UndoCommands(int count)
{
// Do something with the count when an action is clicked
Console.WriteLine("Undo: {0}", count);
}
以下是使用撤消/重做系统的更好示例:http://www.codeproject.com/KB/cs/AutomatingUndoRedo.aspx
public Form1()
{
InitializeComponent();
// Call DrawDropDown with:
// The Undo ToolStripSplitButton button on the Standard tool strip
// "Undo" as the action name
// The list of UndoCommands from the UndoRedoManager
// The Undo method of the UndoRedoManager
m_TSSB_Standard_Undo.DropDownOpening += (sender, e) => { DrawDropDown(
m_TSSB_Standard_Undo,
"Undo",
UndoRedoManager.UndoCommands,
UndoRedoManager.Undo
); };
}
*注意:我确实修改了撤消&amp; UndoRedoManager中的重做方法接受计数:
// Based on code by Siarhei Arkhipenka (Sergey Arhipenko) (http://www.codeproject.com/KB/cs/AutomatingUndoRedo.aspx)
public static void Undo(int count)
{
AssertNoCommand();
if (CanUndo == false) return;
for (int i = 0; (i < count) && CanUndo; ++i)
{
Command command = history[currentPosition--];
foreach (IUndoRedo member in command.Keys)
{
member.OnUndo(command[member]);
}
}
OnCommandDone(CommandDoneType.Undo);
}
答案 2 :(得分:0)
Vs 2010是一个WPF应用程序。如果您在此应用程序开发的开始,那么使用WPF作为核心技术。 WPF下拉按钮在WPF ribbon中实现。源代码可在CodePlex上获得。
答案 3 :(得分:0)
我建议单独从工具栏按钮实现弹出窗口。弹出窗口是单独的窗口,顶部标记在失去焦点或按下转义时自动关闭。如果你编写自己的弹出窗口代码,那么你就不必使你的行为适合预先存在的模型(在你的情况下会很难)。只需使用列表框和状态栏创建一个新的最顶层窗口,然后您可以根据需要在列表框中自由实现选择行为。