使用 Excel Interop 在受保护的 sheet 上排序 Excel table (ListObject)

Sort Excel table (ListObject) on protected sheet using Excel Interop

在受保护的工作表上不允许对 Excel 表 (ListObjects) 进行排序。您将看到以下错误消息:

我花了数周时间寻找解决方案,但没有成功。 Excel 2007 代码示例中的所有内容都已过时。没有关于如何规避此限制的教程或指南。

这是我最终克服的方法..

从 table 的 Excel 过滤器下拉菜单中排序时没有可捕获的事件。但是,当从功能区的主页和数据选项卡调用升序、降序或排序对话框命令时,您可以捕获事件。

使用 Excel 2016 Interop(文档级自定义)、Visual Studio 2015 和 C#:


  1. Right-click on your project -> Add -> New Item -> Ribbon (XML)

  2. On your Ribbon.xml:

<?xml version="1.0" encoding="UTF-8"?>
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui" onLoad="Ribbon_Load">
  <commands>  
    <command idMso="SortAscendingExcel" onAction="SortNoAlerts" />
    <command idMso="SortDescendingExcel" onAction="SortNoAlerts" />
    <command idMso="SortCustomExcel" onAction="SortDialogNoAlerts" /><!--TabHome-->
    <command idMso="SortDialog" onAction="SortDialogNoAlerts" /><!--TabData-->
  </commands>
</customUI>

接下来,添加事件的回调函数。 SortNoAlerts 取消对 sheet 的升序/降序按钮点击的保护。但是如果用户选择'Custom Sort'(主页选项卡) - 或 - 'Sort'(数据选项卡),肯定会出现一个对话框如果按下 OK,它将解除对 sheet 的保护并立即保护它,但如果用户取消,ThisWorkbook_SheetCalculate 将永远不会触发 sheet 不受保护。所以我们添加了 SortDialogNoAlerts 来取消对 sheet 的保护,但也启动了一个计时器,该计时器使用 p/Invoke FindWindow 来查找排序对话框 window。当 Window 不再被找到时,它会保护它(如果尚未受到保护)。

  1. On your Ribbon.cs callbacks:
    public void SortNoAlerts(Office.IRibbonControl control, ref bool cancelDefault)
    {
        Excel.Worksheet ws = null;
        try
        {
            ws = (Excel.Worksheet)Globals.ThisWorkbook.ActiveSheet;
            ws.Unprotect("your password");
            cancelDefault = false;
        }
        catch (Exception) { }
        finally
        {
            if (ws != null) Marshal.ReleaseComObject(ws); ws = null;
        }
    }

    public void SortDialogNoAlerts(Office.IRibbonControl control, ref bool cancelDefault)
    {
        Excel.Worksheet ws = null;
        try
        {
            ws = (Excel.Worksheet)Globals.ThisWorkbook.ActiveSheet;
            ws.Unprotect("your password");
            Globals.ThisWorkbook._myActionPane.tmrWaitSortWinClose.Enabled = true;
            cancelDefault = false;
        }
        catch (Exception) {
            Globals.ThisWorkbook._myActionPane.tmrWaitSortWinClose.Enabled = false;
        }
        finally
        {
            if (ws != null) Marshal.ReleaseComObject(ws); ws = null;
        }
    }
  1. On ThisWorkbook.cs -> InternalStartup() add this:
this.SheetCalculate += new Excel.WorkbookEvents_SheetCalculateEventHandler(ThisWorkbook_SheetCalculate);
  1. On ThisWorkbook.cs -> add this:
public bool sortDialogVisible;

private void ThisWorkbook_SheetCalculate(object sh)
{
  Excel.Worksheet ws = (Excel.Worksheet)sh;
  ws.EnableOutlining = true;
  ws.Protect("your password", true, Type.Missing, Type.Missing, true, true, true, Type.Missing, Type.Missing, true, Type.Missing, Type.Missing, true, true, true, Type.Missing);
  Marshal.ReleaseComObject(ws); ws = null;
}
  1. Add a timer named tmrWaitSortWinClose and set Interval = 750:
private void tmrWaitSortWinClose_Tick(object sender, EventArgs e)
{
    Globals.ThisWorkbook.sortDialogVisible = Native.FindWindow("NUIDialog", "Sort") == IntPtr.Zero;

    if (Globals.ThisWorkbook.sortDialogVisible)
    {
        Excel.Worksheet ws = null;
        try
        {
            ws = (Excel.Worksheet)Globals.ThisWorkbook.ActiveSheet;

            if (!ws.ProtectContents)
            {
               ws.Protect("your password", true, Type.Missing, Type.Missing, true, true, true, Type.Missing, Type.Missing, true, Type.Missing, Type.Missing, true, true, true, Type.Missing);
            }
            tmrWaitSortWinClose.Enabled = false;
        }
        catch (Exception) { tmrWaitSortWinClose.Enabled = false; }
        finally
        {
            if (ws != null) Marshal.ReleaseComObject(ws); ws = null;
        }
    }
}
  1. Add a class named Native.cs:
public class Native
{
    [DllImport("user32.dll", SetLastError = true)]
    public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
}

这将允许在受保护的 sheet 上排序 table。不要混淆,worksheet.Protect()AllowSort 选项仅适用于 sheet 中不属于 table (ListObject) 的单元格。