Дополнительное спасибо LarsTech!(Я не знал о ToolStripControlHost несколько часов назад)
Вот моя реализация, которая действительно близка к выпадающему списку VS ...

Вы должны иметь возможность просто добавить этот делегат и функцию в вашу форму:
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, который имеет 1ToolStripSplitButton (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);
}
Вот лучший пример использования системы Undo / Redo из: 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
); };
}
* Примечание: я изменил Undo & Redoметоды в 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);
}