Delphi 到 Excel - 使用自动过滤器删除未知数据元素

Delphi to Excel - Using Autofilter to delete unknown data elements

Delphi 东京,Excel 2016。通过使用 Delphi 程序,我控制了 Excel。我试图删除单个 sheet 中的所有行,除了在特定列中具有特定值的行...例如,我有一个企业列表,其中第 3 列是企业的城市。我需要删除除 City in ('Chicago'、'Denver'、'Columbus') 以外的所有行。我已经构建并查看了一个 Excel 宏,以了解 Excel 是如何做到的……具体来说,它会打开 AutoFilter,选择除这些城市之外的所有值,然后删除。我的问题是……我如何知道此列中的值……?

这是VBA宏代码...

 Selection.AutoFilter
    ActiveSheet.Range("$A:$AF").AutoFilter Field:=3, Criteria1:=Array( _
        "ADDISON", "CARROLLTON", "DALLAS", "DANBURY", "EDEN PRAIRIE", "EL PASO", "ELDRIDGE", _
        "FARMERS BRANCH", "Franklin", "GEISMAR", "GRAPEVINE", "HOUSTON", "KROTZ SPRINGS", _
        "MASON CITY", "NEW BRAUNFELS", "PALO ALTO", "PLANO", "POWAY", "PURCHASE", _
        "RICHARDSON", "SAINT PAUL", "SAN ANTONIO", "SOUTHLAKE", "SUNNYVALE", "THE WOODLANDS" _
        , "ULYSSES", "WEST LAKE HLS"), Operator:=xlFilterValues   
    Rows("2:62").Select
    Selection.Delete Shift:=xlUp
    ActiveSheet.ShowAllData

我需要做的是构建一个包含所有城市的数组,除了我要删除的城市。 AutoFilter 知道所有唯一值是什么...除了遍历所有行之外,我如何读取 Autofilter(也称为唯一值列表)?

The AutoFilter knows what all unique values are... How do I read the Autofilter

我认为您无法从自动筛选中读取唯一值。如果您查看 filter 的定义属性,唯一值不在其中,只有过滤器运行的单元格范围。

可能最接近的方法是确定过滤范围内的哪些行显示在屏幕上,然后删除未显示的行。 This code 展示了如何确定哪些行被隐藏。但这对我来说似乎 "around the houses" 并且在任何情况下都不是你特别要求的。

因此,您将不得不以某种方式自己迭代范围。

What I need to do is to build an array of ALL cities EXCEPT the ones I want to delete.

如果忽略 Excel AutoFilter,这实际上 far 在 Delphi 代码中更容易做到,因为找到唯一的单元格值几乎是微不足道的在 Excel 范围内使用 Delphi 代码。

下面,我将展示如何计算 Delphi 等同于 this answer 中的 Coderre 公式。随你喜欢。

开始一个新的 Delphi VCL 项目并将 TCheckListBox 拖放到它上面。然后编译和 执行下面的代码。该代码使用 A、B 和 C 等值填充 Excel 范围 然后提取唯一值并用它们填充 ChecklistBox。在那之后, 您可以使用各个复选框的状态来处理 Excel 范围 随心所欲。

vExcel,
vWorkBook,
vSheet,
vRange : OleVariant;

procedure GetUniqueValues(vRange : OleVariant; Strings : TStrings);
//  scans the Excel range represented by vRange and populates the Strings
//  variable with the unique values for in the range's Cells
var
  S : String;
  Row,
  Col : Integer;
begin
  Strings.BeginUpdate;
  Strings.Clear;
  try
    for Row := 2 to vRange.Rows.Count do begin
      for Col := 1 to vRange.Columns.Count do begin
        S := vRange.Cells[Row, Col].Value;
        if Strings.IndexOf(S) < 0 then
          Strings.Add(S);
      end;
    end;
  finally
    Strings.EndUpdate;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  i : Integer;
begin

  vExcel := CreateOleObject('Excel.Application');
  vExcel.Visible := True;

  //  Create a new workbox and populate two columns in it with random
  //  character data
  vWorkBook := vExcel.WorkBooks.Add;
  vSheet := vWorkBook.ActiveSheet;
  vSheet.Range['A1'].Value := 'City';
  for i := 1 to 20 do begin
    vSheet.Range['A' + IntToStr(1 + i)].Value := Chr(Random(3) + Ord('A'));
    vSheet.Range['B' + IntToStr(1 + i)].Value := Chr(Random(4) + Ord('A'));
  end;

  //  Extract the range A2..B21
  vRange := vSheet.Range['A2', 'B21'];

  //  Set CheckListBox1 to sort its contents
  CheckListBox1.Sorted := True;

  //  Now get the unique values
  GetUniqueValues(vRange, CheckListBox1.Items);

  //  Do whatever you like with tthe results
  for i := 0 to CheckListBox1.Items.Count - 1 do begin
    if not (CheckListBox1.State[i] = cbChecked) then
      ;
  end;
end;

顺便说一句,如果您查看 Excel 的自动化对象的导入单元之一,例如Excel2000.Pas,您会看到其中定义了 IAutoFilter 的接口,因此如果您确实需要,可以从 Delphi 代码访问它。

此外,如果我自己这样做,我可能会通过使用像 TAdoQuery 这样的 ADO 对象访问电子表格来在 SQL 中进行数据操作,因为像 "delete all rows where some column value is not in a list of values" 这样的操作非常重要对于那种治疗。

如您所知,AutoFilter 不提供唯一值,模仿您试图实现的手动过滤设置的功能会变得相当复杂。

我的建议是使用 Range.AdvancedFilter 而不是 Range.AutoFilter。它有一种更简单的方法来定义过滤条件。

您将从 Delphi 调用的宏如下所示:

Public Sub UseAdvancedFilter()
    Dim MyRange As Range
    Set MyRange = Range("A1").CurrentRegion

    Range(MyRange.Address).AdvancedFilter _
        Action:=xlFilterInPlace, _
        CriteriaRange:=Range("A28:C29")

    Rows("2:" & MyRange.Rows.Count).Select
    Selection.Delete Shift:=xlUp

    ActiveSheet.ShowAllData

End Sub

这与您在 MyRange 中的数据和您在 CriteriaRange 中的标准一致。

(我使用拆分视图来缩小图像尺寸。4 到 19 之间的所有行都有数据)

您还有更多列,但我们可以演示文件管理器如何处理这三列。数据范围 (MyRange) 必须包含数据的标题。 City 字段的标题 CityCriteriaRange.

的标题匹配

CriteriaRange 很有趣。我们有三个标题单元格,都带有 City 字段名称。然后我们有三个条件:<>Denver<>Chicago<>Dallas。同一行的条件,它们之间形成一个逻辑AND函数,<>当然意味着not equal to。因此,选择标准变为 select records where City is not Denver and City is not Chicago and City is not Dallas.

结果与您的手动 AutoFilter 测试非常相似。

如果Public UseAdvancedFilter过程放在public模块中,可以直接从Delphi中调用,如:

Excel.Run('UseAdvancedFilter');

前提是安全设置不禁止它。


上述解决方案遵循您删除所有不需要的数据行的想法。如果你真的不想/不需要删除不需要的数据,而是隐藏它,为了复制想要的数据,你可以按如下方式更改上面的内容:

将条件范围更改为

City
Denver
Chicago
Dallas

是的,那只是一栏。不同行上的条件,在它们之间形成一个逻辑 OR 函数。因此选择变成

`select records where City is Denver OR City is Chicago OR City is Dallas`.

然后,我们需要更改宏对选择的操作,f.ex。

'Selection.Delete Shift:=xlUp
Selection.Copy Destination:=Range("A40")

这会将所选记录复制到目标以供进一步处理。