首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >VBE的状态栏

VBE的状态栏
EN

Code Review用户
提问于 2016-04-14 15:29:10
回答 1查看 298关注 0票数 8

VBE (VBA的IDE)最烦人的地方之一是它没有状态栏。

Rubber达克2.0引入了RubberduckCommandBar,ab使用msoControlButton控件就好像它们是标签一样--命令栏会随着文本的变化而不断调整大小,但至少我们可以显示有关当前选择的上下文敏感信息:

由于Rubber鸭加载所有引用的COM类型并解析引用,而不管标识符是否内置,因此“状态栏”还在选择的标识符引用上显示内置声明的类型信息,例如Excel.ListObject

...or甚至还内置了VBA标准库中的函数:

如果这是一个很棒的特性,那么它的代码就不那么“精彩”了。下面是整个RubberduckCommandBar类,我对SetSelectionText实现尤其不感兴趣--如何编写得更好呢?

代码语言:javascript
复制
public class RubberduckCommandBar
{
    private readonly RubberduckParserState _state;
    private readonly VBE _vbe;
    private readonly IShowParserErrorsCommand _command;

    private CommandBarButton _refreshButton;
    private CommandBarButton _statusButton;
    private CommandBarButton _selectionButton;

    public RubberduckCommandBar(RubberduckParserState state, VBE vbe, IShowParserErrorsCommand command)
    {
        _state = state;
        _vbe = vbe;
        _command = command;
        _state.StateChanged += State_StateChanged;
        Initialize();
    }

    private void _statusButton_Click(CommandBarButton Ctrl, ref bool CancelDefault)
    {
        if (_state.Status == ParserState.Error)
        {
            _command.Execute(null);
        }
    }

    public void SetStatusText(string value = null)
    {
        UiDispatcher.Invoke(() => _statusButton.Caption = value ?? RubberduckUI.ResourceManager.GetString("ParserState_" + _state.Status));
    }

    public void SetSelectionText(Declaration declaration)
    {
        if (declaration == null && _vbe.ActiveCodePane != null)
        {
            var selection = _vbe.ActiveCodePane.GetSelection();
            SetSelectionText(selection);
        }
        else if (declaration != null && !declaration.IsBuiltIn && declaration.DeclarationType != DeclarationType.Class && declaration.DeclarationType != DeclarationType.Module)
        {
            _selectionButton.Caption = string.Format("{0} ({1}): {2} ({3})", 
                declaration.QualifiedName.QualifiedModuleName,
                declaration.QualifiedSelection.Selection,
                declaration.IdentifierName,
                RubberduckUI.ResourceManager.GetString("DeclarationType_" + declaration.DeclarationType));
        }
        else if (declaration != null)
        {
            var selection = _vbe.ActiveCodePane.GetSelection();
            _selectionButton.Caption = string.Format("{0}: {1} ({2}) {3}",
                declaration.QualifiedName.QualifiedModuleName,
                declaration.IdentifierName,
                RubberduckUI.ResourceManager.GetString("DeclarationType_" + declaration.DeclarationType),
                selection.Selection);
        }
    }

    private void SetSelectionText(QualifiedSelection selection)
    {
        UiDispatcher.Invoke(() => _selectionButton.Caption = selection.ToString());
    }

    private void State_StateChanged(object sender, EventArgs e)
    {
        Debug.WriteLine("RubberduckCommandBar handles StateChanged...");
        SetStatusText(RubberduckUI.ResourceManager.GetString("ParserState_" + _state.Status));
    }

    public event EventHandler Refresh;

    private void OnRefresh()
    {
        var handler = Refresh;
        if (handler != null)
        {
            handler.Invoke(this, EventArgs.Empty);
        }
    }

    public void Initialize()
    {
        var commandbar = _vbe.CommandBars.Add("Rubberduck", MsoBarPosition.msoBarTop, false, true);

        _refreshButton = (CommandBarButton)commandbar.Controls.Add(MsoControlType.msoControlButton);
        ParentMenuItemBase.SetButtonImage(_refreshButton, Resources.arrow_circle_double, Resources.arrow_circle_double_mask);
        _refreshButton.Style = MsoButtonStyle.msoButtonIcon;
        _refreshButton.Tag = "Refresh";
        _refreshButton.TooltipText =RubberduckUI.RubberduckCommandbarRefreshButtonTooltip;
        _refreshButton.Click += refreshButton_Click;

        _statusButton = (CommandBarButton)commandbar.Controls.Add(MsoControlType.msoControlButton);
        _statusButton.Style = MsoButtonStyle.msoButtonCaption;
        _statusButton.Tag = "Status";
        _statusButton.Click += _statusButton_Click;

        _selectionButton = (CommandBarButton)commandbar.Controls.Add(MsoControlType.msoControlButton);
        _selectionButton.Style = MsoButtonStyle.msoButtonCaption;
        _selectionButton.BeginGroup = true;
        _selectionButton.Enabled = false;

        commandbar.Visible = true;
    }

    private void refreshButton_Click(CommandBarButton Ctrl, ref bool CancelDefault)
    {
        OnRefresh();
    }
}

还有什么地方不对劲吗?

在鼠标和键盘钩子事件处理程序中,从App类调用该代码:

代码语言:javascript
复制
private void HandleMouseMessage()
{
    RefreshSelection();
}

private void HandleKeyboardMessage()
{
    RefreshSelection();
}

private void RefreshSelection()
{
    _stateBar.SetSelectionText(_parser.State.FindSelectedDeclaration(_vbe.ActiveCodePane));
    _appMenus.EvaluateCanExecute(_parser.State);
}

下面是FindSelectedDeclaration实现,可以在RubberduckParserState类中找到:

代码语言:javascript
复制
private QualifiedSelection _lastSelection;
private Declaration _selectedDeclaration;

public Declaration FindSelectedDeclaration(CodePane activeCodePane)
{
    var selection = activeCodePane.GetSelection();
    if (selection.Equals(_lastSelection))
    {
        return _selectedDeclaration;
    }

    _lastSelection = selection;
    _selectedDeclaration = null;

    if (!selection.Equals(default(QualifiedSelection)))
    {
        var matches = AllDeclarations
            .Where(item => item.DeclarationType != DeclarationType.Project &&
                           item.DeclarationType != DeclarationType.ModuleOption &&
                           item.DeclarationType != DeclarationType.Class &&
                           item.DeclarationType != DeclarationType.Module &&
                           (IsSelectedDeclaration(selection, item) ||
                            item.References.Any(reference => IsSelectedReference(selection, reference))));
        try
        {
            var match = matches.SingleOrDefault() ?? AllUserDeclarations
                .SingleOrDefault(item => (item.DeclarationType == DeclarationType.Class || item.DeclarationType == DeclarationType.Module)
                        && item.QualifiedName.QualifiedModuleName.Equals(selection.QualifiedName));
            _selectedDeclaration = match;
        }
        catch (InvalidOperationException exception)
        {
            Debug.WriteLine(exception);
        }
    }

    if (_selectedDeclaration != null)
    {
        Debug.WriteLine("Current selection ({0}) is '{1}' ({2})", selection, _selectedDeclaration.IdentifierName, _selectedDeclaration.DeclarationType);
    }

    return _selectedDeclaration;
}

private static bool IsSelectedDeclaration(QualifiedSelection selection, Declaration declaration)
{
    return declaration.QualifiedSelection.QualifiedName.Equals(selection.QualifiedName)
           && (declaration.QualifiedSelection.Selection.ContainsFirstCharacter(selection.Selection));
}

private static bool IsSelectedReference(QualifiedSelection selection, IdentifierReference reference)
{
    return reference.QualifiedModuleName.Equals(selection.QualifiedName)
           && reference.Selection.ContainsFirstCharacter(selection.Selection);
}
EN

回答 1

Code Review用户

回答已采纳

发布于 2016-04-18 05:59:23

通过在第一个if语句中添加一个小嵌套,您可以跳过对declaration != null的多次检查,这将使代码更具可读性,如下所示

代码语言:javascript
复制
public void SetSelectionText(Declaration declaration)
{
    if (declaration == null)
    {
        if(_vbe.ActiveCodePane == null)
        {
            return;
        }

        var selection = _vbe.ActiveCodePane.GetSelection();
        SetSelectionText(selection);
    }
    else if (!declaration.IsBuiltIn && declaration.DeclarationType != DeclarationType.Class && declaration.DeclarationType != DeclarationType.Module)
    {
        _selectionButton.Caption = string.Format("{0} ({1}): {2} ({3})",
            declaration.QualifiedName.QualifiedModuleName,
            declaration.QualifiedSelection.Selection,
            declaration.IdentifierName,
            RubberduckUI.ResourceManager.GetString("DeclarationType_" + declaration.DeclarationType));
    }
    else
    {
        var selection = _vbe.ActiveCodePane.GetSelection();
        _selectionButton.Caption = string.Format("{0}: {1} ({2}) {3}",
            declaration.QualifiedName.QualifiedModuleName,
            declaration.IdentifierName,
            RubberduckUI.ResourceManager.GetString("DeclarationType_" + declaration.DeclarationType),
            selection.Selection);
    }
}  

可以将else if的条件检查提取为有意义的名称的方法。虽然它只使用一次,但可以提高当前方法的可读性。

FindSelectedDeclaration()类的RubberduckParserState方法也可以使用剪裁。如果您使用这里的条件,if (!selection.Equals(default(QualifiedSelection))),您可以提前返回并保存一个缩进级别,如下所示

代码语言:javascript
复制
public Declaration FindSelectedDeclaration(CodePane activeCodePane)
{
    var selection = activeCodePane.GetSelection();
    if (selection.Equals(_lastSelection))
    {
        return _selectedDeclaration;
    }

    _lastSelection = selection;
    _selectedDeclaration = null;

    if (selection.Equals(default(QualifiedSelection)))
    {
        return null;
    }

    var matches = AllDeclarations
        .Where(item => item.DeclarationType != DeclarationType.Project &&
                       item.DeclarationType != DeclarationType.ModuleOption &&
                       item.DeclarationType != DeclarationType.Class &&
                       item.DeclarationType != DeclarationType.Module &&
                       (IsSelectedDeclaration(selection, item) ||
                        item.References.Any(reference => IsSelectedReference(selection, reference))));
    try
    {
        var match = matches.SingleOrDefault() ?? AllUserDeclarations
            .SingleOrDefault(item => (item.DeclarationType == DeclarationType.Class || item.DeclarationType == DeclarationType.Module)
                    && item.QualifiedName.QualifiedModuleName.Equals(selection.QualifiedName));
        _selectedDeclaration = match;
    }
        catch (InvalidOperationException exception)
        {
            Debug.WriteLine(exception);
        }

    if (_selectedDeclaration != null)
    {
        Debug.WriteLine("Current selection ({0}) is '{1}' ({2})", selection, _selectedDeclaration.IdentifierName, _selectedDeclaration.DeclarationType);
    }

    return _selectedDeclaration;
}

但是,如果我阅读var matchesvar match,我总是会考虑正则表达式,所以我倾向于将这些变量重命名为foundDeclarationsfoundDeclaration

票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/125685

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档