【问题标题】:Sort Data Validation dropdown list within cell对单元格内的数据验证下拉列表进行排序
【发布时间】:2020-06-20 07:52:01
【问题描述】:

我已经实现了一个单元格内的数据验证下拉列表,我用它来在一列单元格中保留多个值。目前,您可以按任何顺序从下拉列表中进行选择,并且单元格将按该顺序填充。有没有办法强制订单与作为我的下拉列表来源的列表保持一致?

例如:我的下拉列表是:

  • 吉姆
  • 汤姆
  • 鲍勃
  • 亚伦

选择按以下顺序进行:

  • 鲍勃
  • 吉姆
  • 汤姆

我希望单元格显示:

  • Jim, Tom, Bob

以下是我当前用于数据验证下拉列表的 VBA 代码:

Option Explicit

Private Sub Worksheet_Change(ByVal Target As Range)

    ' To allow multiple selections in a Drop Down List
    Dim Oldvalue As String
    Dim Newvalue As String

    Application.EnableEvents = True

    On Error GoTo Exitsub
    If Target.Column = 13 Then
        If Target.SpecialCells(xlCellTypeAllValidation) Is Nothing Then
            GoTo Exitsub
        Else
            If Target.Value = "" Then 
                GoTo Exitsub 
            Else
                Application.EnableEvents = False
                Newvalue = Target.Value
                Application.Undo
                Oldvalue = Target.Value
                If Oldvalue = "" Then
                    Target.Value = Newvalue
                Else
                    If InStr(1, Oldvalue, Newvalue) = 0 Then
                        Target.Value = Oldvalue & ", " & Newvalue
                    Else
                        Target.Value = Oldvalue
                    End If
                End If
            End If
        End If
    End If
Exitsub:
    Application.EnableEvents = True
End Sub

所以,下面是一个简单的示例截图:

基本上,上面的代码(由前同事给我,不是我自己发明的)让我可以从单元格中的列表中保留多个选择,用逗号分隔。这很好用,但是列表中的选择会按照它们被选择的顺序显示在单元格中。

我需要它们按照它们在列表中的顺序显示。从示例中,如果有人选择Bob,然后选择Tom,然后选择Ryan,则当前代码显示Bob, Tom, Ryan。我需要代码来重新排序选择以显示为Tom, Bob, Ryan

【问题讨论】:

  • 所以,如果我正确阅读了这些回复,它们都是从小到大排序的。我需要根据原始源列表的顺序进行排序。
  • @JWelsh 您能否编辑这篇文章并包含一些屏幕截图或您面临的问题的示例?它会让你更容易接近:)
  • 验证列表是来自链接范围,还是直接输入到验证设置中?
  • 列表是在数据验证中选择的链接范围。

标签: excel vba


【解决方案1】:

试试这个 - 对您的原始版本进行一些更改,包括如果您选择了已选择的内容,则会将其从选择中删除。

Private Sub Worksheet_Change(ByVal Target As Range)

    ' To allow multiple selections in a Drop Down List
    Dim Oldvalue As String
    Dim Newvalue As String
    Dim rng As Range, rngToCheck As Range, listVals

    'run some checks
    If rng.Cells.Count > 1 Then Exit Sub '<< this first!

    Set rngToCheck = Me.Range("A1,C1,D1,M1").EntireColumn '<< checking columns A,C,D, M

    Set rng = Application.Intersect(Target, _
               rngToCheck.SpecialCells(xlCellTypeAllValidation))
    If rng Is Nothing Then Exit Sub


    If rng.Value <> "" Then
        On Error GoTo Exitsub
        Application.EnableEvents = False
        Newvalue = rng.Value
        Application.Undo
        Oldvalue = rng.Value
        If Oldvalue = "" Then
            rng.Value = Newvalue
        Else
            listVals = Application.Evaluate(rng.Validation.Formula1).Value
            rng.Value = SortItOut(listVals, Oldvalue, Newvalue) '<< call function
        End If
    End If

Exitsub:
    If Err.Number > 0 Then Debug.Print Err.Description
    Application.EnableEvents = True
End Sub


'Figure out what gets added (or removed) and keep
'  it all in the same order as the validation source range
Private Function SortItOut(listVals, oldVal, newVal)
    Const THE_SEP As String = ", "
    Dim i As Long, arr, s, sep, t, listed, removeNewVal
    s = ""
    sep = ""
    arr = Split(oldVal, THE_SEP)
    'new value already listed?
    removeNewVal = Not IsError(Application.Match(newVal, arr, 0))

    For i = 1 To UBound(listVals, 1)
        t = listVals(i, 1)
        listed = Not IsError(Application.Match(t, arr, 0))
        If listed Or newVal = t Then
            If Not (removeNewVal And newVal = t) Then
                s = s & sep & t
                sep = THE_SEP
            End If
        End If
    Next i

    SortItOut = s
End Function

【讨论】:

  • 你好,蒂姆。我想知道,如何设置这个宏将在几列上工作?在这种情况下,rng 适用于一列。我尝试再添加一些,但它不起作用。谢谢
  • 查看我上面的编辑 - 添加了 rngToCheck,它定义了要监控的列。
  • 谢谢。现在我正在尝试将新行分隔符添加到 Const THE_SEP。我想变成这样:"; " &amp; Chr(10).
  • Const THE_SEP As String = ", " &amp; vbLf 你不能使用像Chr() 这样的方法来定义一个常量,但是你可以使用其他常量。或者只是让它成为一个常规变量。
  • 其实我用If Target.Count &gt; 1 Then GoTo exitHandler替换if rng.Cells.Count &gt; 1 Then Exit Sub,在Exitsub:之前加上exitHandler: Application.EnableEvents = True,否则我会报错:>Rutime error '91: Object variable or With block variable not set error跨度>
【解决方案2】:

您可以在顶部添加:

Dim nameArray() As String
Dim sortedArray() As Variant: sortedArray = Array("Tom", "Bob", "Ryan") 'etc whatever order you need
Dim finalArray() As Variant
Dim spot1 As Integer
Dim spot2 As Integer: spot2 = 0
Dim name as String

并在Target.Value = Oldvalue &amp; ", " &amp; Newvalue 下添加此权限:

Target.Value = Replace(Target.Value, ",", "")
nameArray = Split(Target.Value)

For spot1 = 0 To UBound(nameArray) 
    For Each name in nameArray
        If name = sortedArray(spot1)
            finalArray(spot2) = name
            spot2 = spot2 + 1
        End If
    Next
Next

Target.Value = ""
For spot1 = 0 To UBound(finalArray)
    If spot1 <> UBound(finalArray) Then
        Target.Value = Target.Value & finalArray(spot1) & ", "
    Else
        Target.Value = finalArray(spot1)
    End If
Next

无法自己测试,因此请确保在测试前保存文件。

祝你好运

【讨论】:

  • 感谢您的回复,我会在星期一早上回到办公室时尝试一下
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-02
  • 1970-01-01
  • 2016-10-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多