Как работать с * многими * контекстными меню - PullRequest
2 голосов
/ 27 января 2011

Я переписываю в C # (с Winforms) старое приложение VB6, которое использует одно контекстное меню с несколькими пунктами, которые изменяют свои признаки Caption, Visible и Enabled на основе монолитной функции "InitControls"

Функция имеет длину 500 строк и состоит в основном из оператора switch, который решает, какие элементы управления включить на основе тега выбранного элемента (есть древовидное представление и представление списка; оно выбирает выбранный элемент из активного и получает его тег ). Затем он включает, отключает и изменяет текст видимых элементов и очищает любые бесполезные разделители. В оригинале используется ActiveBar (пользовательский элемент управления), который позволяет изменять текст в одном месте и отображать элемент одновременно в меню, контекстных меню и панелях инструментов.

В настоящее время я просто заново реализую логическую строку для строки в C #, но я ненавижу это, потому что я на самом деле ничего не исправляю, просто помещаю проблему в новый язык (и, возможно, путаю ее в процессе) , Я создал класс, который позволял мне изменять текст, включенные и видимые свойства любых «подписанных» пунктов меню в одном месте и даже добавлять / удалять обработчики событий для всех пунктов меню subscriBed. Это работает, и даже, кажется, кажется правильным, но я уверен, что должен быть лучший способ. Моя MainForm ОГРОМНАЯ.

Каков стандартный метод .NET для обработки сложной логики контекстного меню и панели инструментов?

1 Ответ

3 голосов
/ 27 января 2011

Из того, что я понимаю, вы в основном хотите провести рефакторинг большого метода переключения.Поиск в Google для «рефакторинга переключателя» должен дать вам несколько примеров, которые вы можете проверить, чтобы найти то, что вам больше подходит.из каждого блока case в новый класс, возможно, реализацию интерфейса, общего для всех случаев.Правильная реализация вашего класса будет зависеть от состояния отдельного утверждения case: это называется шаблон стратегии , потому что каждое условие требует отдельной стратегии.

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

[Редактировать]

Чтобы пояснить немного, я приведу простой пример.

Я упоминал, что отдельные реализации должны быть извлечены в классы, которые реализуют один и тот же интерфейс, который должен отвечать за изменение внешнего вида и состояния пунктов меню на основетекущее состояние.

interface IMenuStateManager
{
    // this method updates state of one or 
    // more menu elements, according to the 
    // specified selected node info
    void UpdateState(ISelectedNodeInfo info);   
}

Наша первая базовая реализация интерфейса IMenuStateManager не сделает ничего, кроме простого вызова реализаций других менеджеров.Это называется Шаблон составного объекта , поскольку он позволяет нам обрабатывать группу объектов как один объект:

// composite class for a list of menu managers
class CompositeMenuStateManager : IMenuStateManager
{
    private readonly IMenuStateManager[] _childManagers;

    // params keyword will allow as to pass a comma separated list
    // of managers, which is neat
    public CompositeMenuStateManager(params IMenuStateManager[] managers)
    {
        _childManagers = managers;
    }

    // this is where the job gets done, but composite
    // class doesn't do much work by itself
    public void UpdateState(ISelectedNodeInfo info)    
    {
        // allow each state manager to change its state
        foreach (IMenuStateManager mgr in _childManagers)
        {
            mgr.UpdateState(info);
        }
    }
}

Теперь у вас все еще есть огромный список возможных кандидатов меню, но теперь их логика разделена на разные классы, а затем обернута в один составной объект.

IMenuStateManager _menuManager = new CompositeMenuStateManager
(
    // note: each menu "manager" can manage one or more
    // items, if you find it useful. 
    // For example, ClipboardMenuStateManager can be
    // a composite manager itself (cut/copy/paste).

    new ClipboardMenuStateManager(some params),
    new SomeOtherMenuItemManager(various params),
    new YetAnotherMenuItemManager(various params),
    ...
);

Я предполагаю, что состояния меню обновляются при выборе узла, но этоэто то, что вы должны легко адаптировать к своему приложению.Этот конкретный обработчик событий передает всю ответственность нашему составному менеджеру меню:

void Node_Selected(sender object, EventArgs args)
{
    // find out which node was clicked
    Node node = object as Node;

    // get the data (model) node for this tree node
    INodeData data = node.Tag as INodeData;

    // create some info which will be passed to the manager.
    // you can pass information that might be useful,
    // or just simply pass the node data itself
    ISelectedNodeInfo info = new SelectedNodeInfo(data, some other stuff);

    // let the manager do the rest of the job
    _menuManager.UpdateState(info);
}

Поскольку у вас, вероятно, будет три элемента меню, выполняющих одну и ту же работу одновременно (главное меню, контекстное меню, панель инструментов), выВозможно, вы захотите, чтобы каждая реализация IMenuStateManager обновляла все три из них одновременно.Самый простой способ - передать массив из ToolStripItem объектов, который является базовым абстрактным классом для нескольких различных элементов меню:

class PrintMenuManager : IMenuStateManager
{
    private readonly ToolStripItem[] _items;

    // this constructor can accept several menu elements
    public PrintMenuManager(params ToolStripItem[] items)
    {
        _items = items;
    }

    public void UpdateState(ISelectedNodeInfo node)
    {
        foreach (ToolStripItem item in _items)
        {
            // if node is printable, enable
            // all "print" menu items and buttons
            item.Enabled = (node.IsPrintable);
        }
    }
}

При создании экземпляра PrintMenuManager вы можете передать все кнопкии пункты меню, которые связаны:

// (this should be one of the child managers in
// the composite menu manager, but you get it)
IMenuStateManager printMnuManaegr = new PrintMenuManager
(
    this.printMenuItem,
    this.printContextMenuItem,
    this.printToolbarButton,
);

Вот так, это оказалось длинным в конце.:)

Хорошо, вот и все для начала.

...