如何以编程方式创建 Excel "sort/Filter combobox"?
How can I programmatically create an Excel "sort/Filter combobox"?
我正在对手动创建的电子表格进行逆向工程以进行动态创建。大多数单元格都填充了简单数据,但有几个是 "sort/Filter" 下拉列表,如下所示:
如何动态创建这样的控件?
有没有办法在 Excel 电子表格中 "view source" 查看生成这些控件可能需要什么样的代码?
更新
适配 MacroMark 的代码,编译:
var rangeMonthYears = _xlSheet.Range[_xlSheet.Cells[7, 3], _xlSheet.Cells[7, 15]];
object sortFilterCombobox = (object)rangeMonthYears.AutoFilter(1, System.Reflection.Missing.Value, XlAutoFilterOperator.xlAnd, System.Reflection.Missing.Value, true);
_xlSheet.Cells["6", "C"] = sortFilterCombobox; //MonthLabel;
...但它崩溃了,向我展示了这个责备的字条:
我怎么误入歧途了?
更新 2
为了在下面的评论中回答 MacroMarc,这里是 legacy/model 电子表格(我正在对其进行逆向工程)中过滤器控件的屏幕截图:
在这种情况下,我从列表中取消选择 "November",这样它就被删除了,如您所见。因此,用户选择的内容会影响下面各列的可见性。
更新 3
由于旧电子表格中包含了所有这些花哨的内裤,我现在正在考虑将其另存为模板并根据需要简单地替换单元格内容。这有什么不可行的原因吗?
如果没有,要完成此操作,我是否应该保存现有的电子表格 "As Excel Macro-Enabled Workbook"?
更新 4
我试过像这样调整 MacroMarc 的答案:
Range monthYearCells = _xlSheet.Range[_xlSheet.Cells[COLUMN_HEADING_ROW, MONTH1_COL], _xlSheet.Cells[COLUMN_HEADING_ROW, MONTH13_COL]];
object monthFilter = (object)monthYearCells.AutoFilter(1, System.Reflection.Missing.Value, XlAutoFilterOperator.xlAnd, System.Reflection.Missing.Value, true);
var monthFilterCell = (Range)_xlSheet.Cells[6, 3];
monthFilterCell.Value = monthFilter;
...但出现运行时异常:
是否是最后一行(将 monthFilter 分配给范围的值 属性)导致了问题?如果是这样,我应该 将 monthFilter 分配给什么,或者我应该用它做什么?
不,您不能在 Excel 电子表格上 "view source"。但是,您可以打开 VBA 编辑器并查看其中写入了哪些代码。在 Excel 的现代版本中,如何做到这一点可能并不那么明显。在 Excel window 的顶部,找到小箭头,单击它,然后会出现一个 drop-down。
在列表底部,您会看到标题为 "more commands" 的选项。单击此处,将打开以下 window。
然后您可以过滤命令列表。选择 "developer tab",在右侧您将看到标题为 "Visual Basic" 的命令。您还有其他用于录制宏的命令,因此请添加整个列表。正如您从我的第一个屏幕截图中看到的那样,我已经将这些添加到我的 Excel.
副本中
请注意,您可能还需要调整安全设置。
现在你知道如何打开VBA编辑器,并且你已经有了宏命令,你可以查看创建过滤器的代码,或者你可以自己创建一个过滤器并录制一个宏来查看它是如何完成的。生成的宏将在 VBA 中,您可以随意修改它。
首先点击"record macro"的快速访问工具栏图标,然后在table的顶行选择要过滤的单元格添加过滤器,然后点击数据选项卡;点击过滤器。
录制宏后,您应该会在 VBA 编辑器中看到如下内容:
Sub Macro1()
'
' Macro1 Macro
'
'
Selection.AutoFilter
ActiveSheet.Range("$A:$D").AutoFilter Field:=3, Criteria1:="1023123"
Selection.AutoFilter
End Sub
既然我们已经涵盖了整个 "open source" 问题,下面是互操作问题的解决方案。我已经获取了您的代码,对其进行了更改,并对其进行了测试以重现错误并发现了问题所在。该过程必须仅选择 table 的一部分或电子表格的完全空白区域。当 Excel 试图找到要过滤的范围的角时,它遇到了一个问题,因为没有可识别的角可以找到。以此为例:
Excel.Application app = new Excel.Application();
app.Visible = true;
Excel.Workbook wk = app.Workbooks.Add();
Excel.Worksheet sh = wk.Sheets[1];
sh.Cells[1, 1] = "col1";
sh.Cells[1, 2] = "col2";
sh.Cells[1, 3] = "col3";
sh.Cells[1, 4] = "col4";
sh.Cells[2, 1] = "data";
sh.Cells[2, 2] = "data";
sh.Cells[2, 3] = "data";
sh.Cells[2, 4] = "data";
sh.Cells[3, 1] = "data2";
sh.Cells[3, 2] = "data2";
sh.Cells[3, 3] = "data2";
sh.Cells[3, 4] = "data2";
//Excel.Range r1 = sh.Range[sh.Cells[1, 1], sh.Cells[3, 4]]; //this works
Excel.Range r2 = sh.Range[sh.Cells[7, 3], sh.Cells[7, 15]]; //this fails
//object sortFilterCombobox1 = (object)r1.AutoFilter(1, System.Reflection.Missing.Value, Excel.XlAutoFilterOperator.xlAnd, System.Reflection.Missing.Value, true);
object sortFilterCombobox2 = (object)r2.AutoFilter(1, System.Reflection.Missing.Value, Excel.XlAutoFilterOperator.xlAnd, System.Reflection.Missing.Value, true);
在下面的屏幕截图中,我突出显示了代码试图放置过滤器的行,但它是空的。所以,这里发生的事情是代码出于某种原因没有引用您正在使用的电子表格的正确区域。
解决此问题的一种方法是引用顶行中的第一个单元格而不是整行。只要 table 没有空隙,那么 Excel 就会找到整个 table.
因此请尝试在此范围而不是整个范围上应用自动筛选:
_xlSheet.Cells[7, 3]
另外,我应该指出,如果您不知道,当您将 "sortFilterCombobox" 分配给一个单元格时,您实际上是在将一个布尔值弹出到单元格中。 AutoFilter 函数 returns 一个布尔值,而不是过滤器 object 本身。
过滤器由 Excel table 中的范围本地创建,或者可能通过 Excel 界面上的过滤器按钮创建。正如 Joshua 所说,您可以尝试在 VBA 中编写脚本,但从您的问题标签看来,您正在使用 C# 和 Excel-Interop(??)
尝试在 C# 代码中使用范围对象句柄并应用 .Autofilter 方法。
object result = (object)oRange.AutoFilter(1, System.Reflection.Missing.Value,ExApp.XlAutoFilterOperator.xlAnd,System.Reflection.Missing.Value, true);
ExApp 上方是 Interop.Excel 命名空间的别名。
至于“开发人员”选项卡,请查看 google 上适合您的 Excel 版本的“自定义功能区”选项。右键单击功能区,或者文件-->选项--等等
更新
好的,要修改数据透视字段的排序和筛选属性,您需要获取数据透视字段的句柄,例如,如果您的 'Month' 筛选器位于单元格 D5 中,您可以执行以下操作:
Range oRange = oSheet.get_Range("D5", "D5");
PivotField pf = oRange.PivotField;
pf.AutoSort((int)XlSortOrder.xlDescending, "Month"); //this sorts in reverse order
pf.PivotItems(2).Visible = false; //this makes the second item deselected in filter
我正在对手动创建的电子表格进行逆向工程以进行动态创建。大多数单元格都填充了简单数据,但有几个是 "sort/Filter" 下拉列表,如下所示:
如何动态创建这样的控件?
有没有办法在 Excel 电子表格中 "view source" 查看生成这些控件可能需要什么样的代码?
更新
适配 MacroMark 的代码,编译:
var rangeMonthYears = _xlSheet.Range[_xlSheet.Cells[7, 3], _xlSheet.Cells[7, 15]];
object sortFilterCombobox = (object)rangeMonthYears.AutoFilter(1, System.Reflection.Missing.Value, XlAutoFilterOperator.xlAnd, System.Reflection.Missing.Value, true);
_xlSheet.Cells["6", "C"] = sortFilterCombobox; //MonthLabel;
...但它崩溃了,向我展示了这个责备的字条:
我怎么误入歧途了?
更新 2
为了在下面的评论中回答 MacroMarc,这里是 legacy/model 电子表格(我正在对其进行逆向工程)中过滤器控件的屏幕截图:
在这种情况下,我从列表中取消选择 "November",这样它就被删除了,如您所见。因此,用户选择的内容会影响下面各列的可见性。
更新 3
由于旧电子表格中包含了所有这些花哨的内裤,我现在正在考虑将其另存为模板并根据需要简单地替换单元格内容。这有什么不可行的原因吗?
如果没有,要完成此操作,我是否应该保存现有的电子表格 "As Excel Macro-Enabled Workbook"?
更新 4
我试过像这样调整 MacroMarc 的答案:
Range monthYearCells = _xlSheet.Range[_xlSheet.Cells[COLUMN_HEADING_ROW, MONTH1_COL], _xlSheet.Cells[COLUMN_HEADING_ROW, MONTH13_COL]];
object monthFilter = (object)monthYearCells.AutoFilter(1, System.Reflection.Missing.Value, XlAutoFilterOperator.xlAnd, System.Reflection.Missing.Value, true);
var monthFilterCell = (Range)_xlSheet.Cells[6, 3];
monthFilterCell.Value = monthFilter;
...但出现运行时异常:
是否是最后一行(将 monthFilter 分配给范围的值 属性)导致了问题?如果是这样,我应该 将 monthFilter 分配给什么,或者我应该用它做什么?
不,您不能在 Excel 电子表格上 "view source"。但是,您可以打开 VBA 编辑器并查看其中写入了哪些代码。在 Excel 的现代版本中,如何做到这一点可能并不那么明显。在 Excel window 的顶部,找到小箭头,单击它,然后会出现一个 drop-down。
在列表底部,您会看到标题为 "more commands" 的选项。单击此处,将打开以下 window。
然后您可以过滤命令列表。选择 "developer tab",在右侧您将看到标题为 "Visual Basic" 的命令。您还有其他用于录制宏的命令,因此请添加整个列表。正如您从我的第一个屏幕截图中看到的那样,我已经将这些添加到我的 Excel.
副本中请注意,您可能还需要调整安全设置。
现在你知道如何打开VBA编辑器,并且你已经有了宏命令,你可以查看创建过滤器的代码,或者你可以自己创建一个过滤器并录制一个宏来查看它是如何完成的。生成的宏将在 VBA 中,您可以随意修改它。
首先点击"record macro"的快速访问工具栏图标,然后在table的顶行选择要过滤的单元格添加过滤器,然后点击数据选项卡;点击过滤器。
录制宏后,您应该会在 VBA 编辑器中看到如下内容:
Sub Macro1()
'
' Macro1 Macro
'
'
Selection.AutoFilter
ActiveSheet.Range("$A:$D").AutoFilter Field:=3, Criteria1:="1023123"
Selection.AutoFilter
End Sub
既然我们已经涵盖了整个 "open source" 问题,下面是互操作问题的解决方案。我已经获取了您的代码,对其进行了更改,并对其进行了测试以重现错误并发现了问题所在。该过程必须仅选择 table 的一部分或电子表格的完全空白区域。当 Excel 试图找到要过滤的范围的角时,它遇到了一个问题,因为没有可识别的角可以找到。以此为例:
Excel.Application app = new Excel.Application();
app.Visible = true;
Excel.Workbook wk = app.Workbooks.Add();
Excel.Worksheet sh = wk.Sheets[1];
sh.Cells[1, 1] = "col1";
sh.Cells[1, 2] = "col2";
sh.Cells[1, 3] = "col3";
sh.Cells[1, 4] = "col4";
sh.Cells[2, 1] = "data";
sh.Cells[2, 2] = "data";
sh.Cells[2, 3] = "data";
sh.Cells[2, 4] = "data";
sh.Cells[3, 1] = "data2";
sh.Cells[3, 2] = "data2";
sh.Cells[3, 3] = "data2";
sh.Cells[3, 4] = "data2";
//Excel.Range r1 = sh.Range[sh.Cells[1, 1], sh.Cells[3, 4]]; //this works
Excel.Range r2 = sh.Range[sh.Cells[7, 3], sh.Cells[7, 15]]; //this fails
//object sortFilterCombobox1 = (object)r1.AutoFilter(1, System.Reflection.Missing.Value, Excel.XlAutoFilterOperator.xlAnd, System.Reflection.Missing.Value, true);
object sortFilterCombobox2 = (object)r2.AutoFilter(1, System.Reflection.Missing.Value, Excel.XlAutoFilterOperator.xlAnd, System.Reflection.Missing.Value, true);
在下面的屏幕截图中,我突出显示了代码试图放置过滤器的行,但它是空的。所以,这里发生的事情是代码出于某种原因没有引用您正在使用的电子表格的正确区域。
解决此问题的一种方法是引用顶行中的第一个单元格而不是整行。只要 table 没有空隙,那么 Excel 就会找到整个 table.
因此请尝试在此范围而不是整个范围上应用自动筛选:
_xlSheet.Cells[7, 3]
另外,我应该指出,如果您不知道,当您将 "sortFilterCombobox" 分配给一个单元格时,您实际上是在将一个布尔值弹出到单元格中。 AutoFilter 函数 returns 一个布尔值,而不是过滤器 object 本身。
过滤器由 Excel table 中的范围本地创建,或者可能通过 Excel 界面上的过滤器按钮创建。正如 Joshua 所说,您可以尝试在 VBA 中编写脚本,但从您的问题标签看来,您正在使用 C# 和 Excel-Interop(??)
尝试在 C# 代码中使用范围对象句柄并应用 .Autofilter 方法。
object result = (object)oRange.AutoFilter(1, System.Reflection.Missing.Value,ExApp.XlAutoFilterOperator.xlAnd,System.Reflection.Missing.Value, true);
ExApp 上方是 Interop.Excel 命名空间的别名。
至于“开发人员”选项卡,请查看 google 上适合您的 Excel 版本的“自定义功能区”选项。右键单击功能区,或者文件-->选项--等等
更新
好的,要修改数据透视字段的排序和筛选属性,您需要获取数据透视字段的句柄,例如,如果您的 'Month' 筛选器位于单元格 D5 中,您可以执行以下操作:
Range oRange = oSheet.get_Range("D5", "D5");
PivotField pf = oRange.PivotField;
pf.AutoSort((int)XlSortOrder.xlDescending, "Month"); //this sorts in reverse order
pf.PivotItems(2).Visible = false; //this makes the second item deselected in filter