How to create command menu item with checkbox?

2019-07-19 17:32发布

问题:

I'm writing a VSPackage and I need to have menu item with checkbox, just like on this sample image below:

I went through this msdn reference regarding .vsct files, bud didn't fine any information explaining how to do it. What I have now is standard menu item with icon and text (code sample from MyPackage.vsct file):

<Buttons>     
  <Button guid="guidMyPackageCmdSet" id="cmdidMyPackage" type="Button">
    <Icon guid="guidImages" id="myPackageBitmap" />
    <CommandFlag>TextChanges</CommandFlag>
    <CommandFlag>DontCache</CommandFlag>
    <CommandFlag>FixMenuController</CommandFlag>
    <Strings>
      <ButtonText>MyPackage</ButtonText>
    </Strings>
  </Button>      
</Buttons>

I need this additional checkbox. How to do it?

回答1:

The properties like Checked, Visible, Enabled or Supported can´t be defined via the VSCT file. You need a command handler that controls the command´s state. I´ve created a base class that wraps the creation of the OleMenuCommand instance and handles the command´s BeforeQueryStatus event. This is a slimmed version of my implementation, but it will give you an idea how to solve it...

internal abstract class CommandHandler : IDisposable
{
    private readonly OleMenuCommand command;

    protected CommandHandler(Guid group, int id)
    {
        var commandid = CommandID(group, id);
        this.command = new OleMenuCommand(this.Invoke, commandId);
        this.command.BeforeQueryStatus += this.OnBeforeQueryStatus;
    }

    protected virtual void OnExecute() { } 

    protected virtual void OnQueryStatus(QueryStatusEventArgs e) { }

    private void Invoke(object sender, EventArgs e)
    {
        this.OnExecute();
    }

    private void OnBeforeQueryStatus(object sender, EventArgs e)
    {
        OleMenuCommand command;
        if ((command = sender as OleMenuCommand) != null)
        {
            var e = new QueryCommandEventArgs
            {
                Checked = command.Checked,
            }

            this.OnQueryStatus(e);

            command.Checked = e.Checked;
        }
    }

    public void Dispose()
    {
        this.command.BeforeQueryStatus -= this.OnBeforeQueryStatus;
    }
}

public class QueryCommandEventArgs : EventArgs
{
    public bool Checked { get; set; }
}

The CommandHandler class allows to control the state of any menu command. Just derive new handler implementations from it and override the OnExecute and OnQueryStatus methods, like...

internal sealed class MyCommand : CommandHandler
{
    private bool checked;

    public MyCommand() : base(GuidCmdSet, MyCommandId) { }

    protected override void OnExecute()
    {
        this.checked = !this.checked; // toggle checked state
    }

    protected override void OnQueryStatus(QueryStatusEventArgs e)
    {
        e.Checked = this.checked;
    }
}