Выпадающая кнопка отмены в стиле Visual Studio - настраиваемая ToolStripSplitButton - PullRequest
3 голосов
/ 10 ноября 2011

Я хочу реализовать раскрывающуюся кнопку отмены в стиле Visual Studio:

Undo drop-down

Я просмотрел весь интернет и, похоже, не могу найти реальных реализаций этого.

Я начал с вывода из ToolStripSplitButton, но на самом деле не знаю, куда идти дальше. Его DropDown свойство имеет значение ToolStripDropDown, но, похоже, ничего не говорит о нескольких выбранных элементах, гораздо меньше прокрутки и о тексте внизу.

Так что вместо стандартного ToolStripDropDown я думаю, что, возможно, вся раскрывающаяся часть должна быть пользовательским элементом управления, основанным на комбинированном списке. Тогда возникает вопрос: как заставить правую кнопку (стрелка вниз) сделать что-то, кроме показа раскрывающегося по умолчанию раскрывающегося списка?

Я на правильном пути? Спасибо!

Ответы [ 4 ]

4 голосов
/ 19 июня 2012

Дополнительное спасибо LarsTech!(Я не знал о ToolStripControlHost несколько часов назад)

Вот моя реализация, которая действительно близка к выпадающему списку VS ...

UndoRedoDropDown

Вы должны иметь возможность просто добавить этот делегат и функцию в вашу форму:

    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);
    }
4 голосов
/ 21 ноября 2011

Да, я думаю, вы на правильном пути.И в этом случае 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));
}

Вот результат:

enter image description here

Для вашего приложения вам необходимо заменитьListBox со своим собственным UserControl, так что вы можете содержать в нем все, что захотите.ToolStripControlHost может содержать только один элемент управления, и важно установить свойство MinimumSize, в противном случае удаленный элемент управления имеет неправильный размер.

0 голосов
/ 17 ноября 2011

Я бы предложил реализовать всплывающее окно отдельно от кнопки панели инструментов.Всплывающие окна - это отдельные окна с верхним флажком, которые автоматически закрываются при потере фокуса или нажатии escape.Если вы кодируете свое собственное всплывающее окно, которое освобождает вас от необходимости приспосабливать ваше поведение к уже существующей модели (что будет сложно в вашем случае).Просто создайте новое самое верхнее окно со списком и строкой состояния, и тогда вы сможете реализовать поведение выбора в списке так, как вам нужно.

0 голосов
/ 17 ноября 2011

Vs 2010 - это приложение WPF.Если вы только начинаете разработку приложения, используйте WPF в качестве основной технологии.Раскрывающаяся кнопка WPF реализована на ленте WPF .Исходный код доступен на CodePlex.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...