删除行后更改选择吗?

时间:2018-07-12 12:43:08

标签: excel vba excel-vba

想要

 Loop through multiple selection areas (r = 1 to n)
   Delete rows in area r
 Next area

注释
选择可以是不连续的,也可以是任何(垂直)顺序。即区域1可以是8-10行,区域2可以是2-3行,区域3可以是14-18行。所选单元格区域不应重叠以防止错误。

问题
在一个区域中执行行删除会导致下面的所有数据上移。下面的选定区域不会移动。因此,新数据(您不想删除)将滚动到选定区域。

示例

Row 1: A B C
Row 2: D E F  (Select R2C1:R2C3 first)
Row 3: G H I
Row 4: J K L  (CTRL Select R4C1:R4C3 next)
Row 5: M N O

已选择2个区域。我们希望删除第2行和第4行。

代码

For aCounter = 1 to Selection.Areas.Count
   Selection.Areas(aCounter).EntireRow.Delete
Next

结果

Row 1: A B C
Row 2: G H I  (This row is selected)
Row 3: J K L
Row 4: (blank) (This row is also selected)
Row 5: (blank)

发生了什么
选择的第一个区域(Areas(1))是第2行,已被删除。 3-5行汇总,但第2行仍保​​留在第4行。这意味着JKL进入第3行,而MNO进入第4行。在下一个循环中,Areas(2)仍设置为第4行,因此MNO为删除。

应该怎么办
Areas(2)应该已被删除的行数向上移动。

问题
有没有一种方法可以轻松地对此进行编码,而无需遍历所有区域,检查它们是否在已删除的行之下,以及每次删除一行时都将它们向上移动到已删除的行数上?

3 个答案:

答案 0 :(得分:1)

从底部开始,并在上移时删除行。

例如:

<rich:dataTable value="#{capitalsBean.capitals}" var="cap" id="table">
<f:facet name="header">
    <rich:columnGroup>
        <rich:column>
            <h:outputText value="State Name" />
        </rich:column>
        <rich:column>
            <h:outputText value="State Time Zone" />
        </rich:column>
        <rich:column>
            <h:outputText value="Marked" />
        </rich:column>
    </rich:columnGroup>
</f:facet>
<rich:column filter="#{filteringBean.stateFilter}">
    <f:facet name="header">
        <h:inputText value="#{filteringBean.stateFilterValue}" id="input">
            <a4j:ajax event="keyup" render="table@body">
                <a4j:attachQueue  requestDelay="700"
                    ignoreDupResponses="true" />
            </a4j:ajax>
        </h:inputText>
    </f:facet>
    <h:outputText value="#{cap.state}" />
</rich:column>
<rich:column filterExpression="#{fn:containsIgnoreCase(cap.timeZone, filteringBean.zoneFilterValue)}">
    <f:facet name="header">
        <h:selectOneMenu value="#{filteringBean.zoneFilterValue}">
            <f:selectItems value="#{filteringBean.zoneList}" />
            <a4j:ajax event="change" render="table@body" />
        </h:selectOneMenu>
    </f:facet>
    <h:outputText value="#{cap.timeZone}" />
</rich:column>
<rich:column>
    <h:selectBooleanCheckbox value="#{filteringBean.marked}" disabled="false"/>
</rich:column>

答案 1 :(得分:1)

使用Union一次删除:(无重叠区域)

Sub try()
Dim MyRng As Range

For aCounter = 1 To Selection.Areas.Count
    If Not MyRng Is Nothing Then
        Set MyRng = Application.Union(MyRng, Selection.Areas(aCounter))
    Else
        Set MyRng = Selection.Areas(aCounter)
    End If
Next
MyRng.EntireRow.Delete
End Sub

具有重叠的区域:

Sub try2()
Dim MyRng As Range, MyRow As Range

For aCounter = 1 To Selection.Areas.Count
    If Not MyRng Is Nothing Then
        For Each MyRow In Selection.Areas(aCounter).Rows
        If Intersect(MyRow, MyRng) Is Nothing Then
        Set MyRng = Application.Union(MyRng, MyRow)
        End If
        Next MyRow
    Else
        Set MyRng = Selection.Areas(aCounter)
    End If
Next
MyRng.EntireRow.Delete
End Sub

答案 2 :(得分:1)

如果选择了多个区域,并且2行或更多行重叠,则VBA会引发重叠错误。要解决此问题,您必须手动进行合并并记录行(请确保不要记录重复的行)。

这是一个涉及使用System.Collections.ArrayList容器对象的解决方案。它不是标准VBA库的一部分。如果您不打算共享文件,则可以添加对\ WINDOWS \ Microsoft.NET \ Framework \ v4.0.30319 \ mscorlib.tlb或mscorlib.dll的引用。然后代码如下:

Dim col As New Collection
Dim i As Long, j As Long, r As Long
CreateObject ("System.Collections.ArrayList")

Dim alist As New ArrayList

For i = 1 To Selection.Areas.Count
    For j = 1 To Selection.Areas(i).Rows.Count
        r = Selection.Areas(i).Rows(j).Row
        If Not alist.Contains(r) Then alist.Add r
    Next
Next

alist.Sort
alist.Reverse

For i = 0 To alist.Count - 1
    Rows(alist(i)).EntireRow.Delete
Next

这是创建一个ArrayList容器对象,然后针对每个区域检查该行是否已在列表中。如果没有,它将添加它。

这将创建一个行列表,这些行的顺序取决于选择区域的顺序。

ArrayList具有内置的.sort和.reverse方法以及也很方便的.contains方法。

现在,结果ArrayList包含要以相反顺序删除的行,因此您只需单步浏览列表并删除。此方法将解决重叠的选择区域问题。


除了@Evr用户的想法外,这是一个可移植的模块,用于处理不同列中重叠区域的情况:

Dim myrng As Range, arng As Range, offrng As Range
Dim acounter As Long

For acounter = 1 To Selection.Areas.Count
    If Not myrng Is Nothing Then
            Set arng = Selection.Areas(acounter)
            Set offrng = arng.Offset(, -arng.Columns(1).Column + 1).Resize(, 1)
            Set myrng = Application.Union(myrng, offrng)
    Else
        Set myrng = Selection.Areas(1).Offset(, -Selection.Areas(1).Columns(1).Column + 1).Resize(, 1)
    End If
Next
myrng.EntireRow.Delete

这是它的工作方式:

第一个选定区域存储在myrng中,但它又返回到列A。因此,负列值+1的原因。例如,如果区域为$ D9:$ F12,我们将不在乎D到F列...我们只关心第9-12行。因此,我们抵消了范围。第一列是4,因此我们将-4 + 1 = -3偏移。结果范围为$ A9:$ F12。然后,resize命令将其返回到一列:$ A9:$ A12。

范围“ arng”是“区域”列表中的下一个选定范围。它也移回A列并调整大小。结果被放入offrng中。

然后将yrg与offrng合并,结果是一个范围内的唯一行地址数组,然后可以使用.EntireRow.Delete将其删除。