0

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

在此处输入图像描述

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

这就是我最终克服的方法。

4

1 回答 1

0

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

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


  1. 右键单击您的项目 -> 添加 -> 新项目 -> 功能区 (XML)

  2. 在您的 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取消对升序/降序按钮单击的工作表的保护。但是,如果用户选择“自定义排序”(主页选项卡)-或-“排序”(数据选项卡),则会出现一个对话框,如果按下确定,它将取消保护工作表并立即保护它,但如果用户取消,ThisWorkbook_SheetCalculate永远不会触发使工作表不受保护。因此,我们添加了SortDialogNoAlerts取消保护工作表的选项,但还启动了一个计时器,该计时器使用 p/InvokeFindWindow来查找“排序”对话框窗口。当不再找到 Window 时,如果尚未受到保护,它会保护它。

  1. 在您的 Ribbon.cs 回调中:
    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. 在 ThisWorkbook.cs -> InternalStartup() 添加:
this.SheetCalculate += new Excel.WorkbookEvents_SheetCalculateEventHandler(ThisWorkbook_SheetCalculate);
  1. 在 ThisWorkbook.cs -> 添加:
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. 添加一个名为 tmrWaitSortWinClose 的计时器并设置 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. 添加一个名为 Native.cs 的类:
public class Native
{
    [DllImport("user32.dll", SetLastError = true)]
    public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
}

这将允许在受保护的工作表上对表格进行排序。不要混淆,它的AllowSort选项worksheet.Protect()仅适用于不属于表格 (ListObject) 的工作表单元格。

于 2016-11-02T01:36:16.880 回答