Home » excel » c# – Excel Automation: Protect single worksheet from deletion by user

c# – Excel Automation: Protect single worksheet from deletion by user

Posted by: admin May 14, 2020 Leave a comment

Questions:

VSTO 4.0 / Office 2007

In an Excel document-level automation project, I have a worksheet that must not be deleted from the workbook. I am afraid that a careless user might delete it by accident, which currently causes a lot of grief (exceptions galore).

I can not protect the entire workbook, because the user must be able to create, delete and otherwise modify this file. Exactly one sheet needs to be protected from deletion, but I may be overlooking something, so if there exists such solution I’m all ears. For example I could imagine that I can Protect() and Unprotect() the workbook when the sheet is visible, but this solution seems messy.

Googling yielded the following VBA code:

Private Sub Worksheet_Activate()
Dim CB As CommandBar
Dim Ctrl As CommandBarControl
For Each CB In Application.CommandBars
Set Ctrl = CB.FindControl(ID:=847, recursive:=True)
If Not Ctrl Is Nothing Then
Ctrl.OnAction = "RefuseToDelete"
Ctrl.State = msoButtonUp
End If
Next
End Sub

I’m not familiar with VBA, but I tried running this from the VSTO generated startup method:

private void Sheet1_Startup(object sender, System.EventArgs e)
{
    //Is there a neater way to iterate through all Office Collections?
    for (var i = 1; i <= Application.CommandBars.Count; i++)
    {
        var commandBar = Application.CommandBars[i];
        //847 is a magical constant that any fule no has something to do with sheet deletion
        var control = commandBar.FindControl(Id: 847, Recursive: true);
        if (control != null) control.OnAction = null;
    }
}

This code seems to do exactly nothing. You may ask “Hey, Gleno, why are you setting OnAction to null” , well I don’t know what to set it to… The linked VBA solution attaches to Activate and Deactivate events, so there’s more code where that came from.

Thanks in advance.

How to&Answers:

I had to do something very similar today. I would just disable the sheet delete buttons whenever your one “undeleteable” sheet is active. If there’s a keyboard shortcut to delete a sheet, I can’t find one. (If there is, you could disable that too.)

This would go in your ThisWorkbook class:

    private void ThisWorkbook_Startup(object sender, System.EventArgs e)
    {
        this.SheetActivate += (sh) =>
            {
                this.ribbon.InvalidateBuiltinControl("SheetDelete");
            };
    }

    public bool CanDeleteActiveSheet()
    {
        if (this.ActiveSheet == null)
            return true;

        // Replace Sheet1 with your sheet's CodeName
        return ((Excel.Worksheet)this.ActiveSheet).CodeName != "Sheet1";
    }

    // Keep a local reference to the ribbon in your ThisWorkbook class
    // so you can call InvalidateControl() from it.
    Ribbon ribbon;
    protected override IRibbonExtensibility CreateRibbonExtensibilityObject()
    {
        this.ribbon = new Ribbon();
        return this.ribbon;
    }

This would go in your ribbon code behind:

    public void InvalidateBuiltinControl(string controlID)
    {
        this.ribbon.InvalidateControlMso(controlID);
    }

    public bool deleteButton_GetEnabled(IRibbonControl control)
    {
        return Globals.ThisWorkbook.CanDeleteActiveSheet();
    }

And this would go in your ribbon xml:

<commands>
    <command idMso="SheetDelete" getEnabled="deleteButton_GetEnabled" />
</commands>

I’m still a little leery of holding on to that ribbon reference in ThisWorkbook, but so far no one has mentioned a better way in the question I posted earlier. Hope this helps!

Answer:

I’m having a similar problem where I know how to protect the worksheet but I need to turn protection on after the sheet has been populated with external data from a SQL connection. I cannot locate the correct event to do this.

This should help you put it in the startup event for the worksheet:

Me.Protect(password:=”password”, allowFiltering:=True, allowSorting:=True, allowUsingPivotTables:=True)