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

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

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

如果这是一个很棒的特性,那么它的代码就不那么“精彩”了。下面是整个RubberduckCommandBar类,我对SetSelectionText实现尤其不感兴趣--如何编写得更好呢?
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类调用该代码:
private void HandleMouseMessage()
{
RefreshSelection();
}
private void HandleKeyboardMessage()
{
RefreshSelection();
}
private void RefreshSelection()
{
_stateBar.SetSelectionText(_parser.State.FindSelectedDeclaration(_vbe.ActiveCodePane));
_appMenus.EvaluateCanExecute(_parser.State);
}下面是FindSelectedDeclaration实现,可以在RubberduckParserState类中找到:
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);
}发布于 2016-04-18 05:59:23
通过在第一个if语句中添加一个小嵌套,您可以跳过对declaration != null的多次检查,这将使代码更具可读性,如下所示
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))),您可以提前返回并保存一个缩进级别,如下所示
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 matches或var match,我总是会考虑正则表达式,所以我倾向于将这些变量重命名为foundDeclarations和foundDeclaration。
https://codereview.stackexchange.com/questions/125685
复制相似问题