【问题标题】:Save userform value for next time保存用户表单值以备下次使用
【发布时间】:2021-03-25 09:54:04
【问题描述】:

我希望用户在设置页面的文本框中输入路径,并在每次加载用户表单时使用此路径字符串。用户不必每次打开用户表单时都输入路径。

我在互联网上找不到任何关于保存用户表单的输入值以供下次使用的信息,它们已加载。

【问题讨论】:

  • 由于用户窗体无法记住一个值,您必须将其存储在 Excel 文件中的某个位置:单元格中。甚至可能在(非常)隐藏的工作表上。然后,下次用户打开此表单时,您可以将该单元格中的值“加载”到您的表单中。如果您不想将该路径保存在 Excel 文件中,那么您也可以将其保存在外部 txt 文件或 ini 文件中,甚至(如果您愿意/喜欢)将其保存在 Windows 注册表中。但是(基本上)你必须将它保存在其他地方(除了用户窗体)。
  • 正如 Ralph 所说,您有很多可能保留路径以供将来重用,如 excel 表、文本文件、二进制文件或 xml 文件或 Windows 注册表等外部文件。甚至可以modify the vba code 所以它包含路径。
  • 您可以使用 CustomXMLParts 在更改事件、某些保存按钮单击或工作簿关闭事件时将必要的值存储在 Excel 文件中,并在稍后在表单初始化时检索。
  • 最简单的方法是在一个单元格中。我不会推荐附加文件、注册表、公共变量或隐藏方法。因为 excel 可以重新启动、代码中断、文件丢失或遗忘、磁盘受到保护、或者用户在网络上……您还可以将其存储在形状的字符串属性(标签、标题、替代文本)中,隐藏在评论、数据验证...

标签: excel vba textbox userform


【解决方案1】:

我推荐使用SaveSetting appname, section, key, setting 来存储数据 和GetSetting appname , section, key [, default ] 检索值。

以你为例:

Private Sub UserForm_Terminate()
    '
    ' Save Setting to the windows registry
    ' usually values are stored at the following path:
    ' Computer\HKEY_USERS\{user-guid}\Software\VB and VBA Program Settings
    '
    SaveSetting "YourApplication", "UserFormXYZ", "TextBox1", TextBox1.Text
End Sub

Private Sub UserForm_Initialize()
    dim defaultPath   As String
    dim userPath      As String
    ' set defaultPath as you require

    ' get settings from Registry
    userPath = GetSetting("YourApplication", "UserFormXYZ", "TextBox1", defaultPath)
    TextBox1.Text = userPath
End Sub

【讨论】:

    【解决方案2】:

    一种方法是这样...

    像这样在标准模块上声明一个公共变量...

    Public Path As String
    

    然后在 UserForm Module 上有以下 TextBox AfterUpdate 事件的代码...

    Private Sub TextBox1_AfterUpdate()
    Path = TextBox1.Value
    End Sub
    

    Path 变量将保存当前会话的 TextBox1.Value,用户无需在 TextBox1 中再次提供 Path。

    【讨论】:

    • 为什么这么复杂?如果 OP 想保留 此会话的路径,那么他/她也可以简单地 .Hide 表单(而不是关闭或 UnLoad-ing 表单)。完成。
    • 你真的是说将变量声明为公共变量很复杂?
    • 您的解决方案是 (1) 声明一个变量,(2) 为该变量分配 TextBox1 的值 (3) 关闭表单 (4) 在某些时候重新加载 UserForm (5) 将变量写回TextBox1,然后(6) 显示表格。另一种方法是 .Hide 表单和(在某些时候).Show 再次表单。完毕。所以,是的,在这种情况下,我发现您的解决方案过于复杂。也许我失败了,所以看到一些东西。但是为什么要使用变量呢?已经有一个带有TextBox 的表单(只要 Excel 文件保持打开状态,它就可以存储值)。那么,为什么要使用 public 变量?为什么要多出几行代码?
    • 我不认为你的逻辑是正确的,所以这里没有任何辩论的好处。 :)
    【解决方案3】:

    自定义文档属性怎么样?

    除了@Ralph 的有效评论之外,我还想展示一种经常未被考虑的方式:将值直接存储到自定义文档属性 (CDP):

    UserForm代码部分(一)-事件过程

    • UserForm_Activate() 只是在用户窗体的任何激活时重新显示存储的值。
    • TextBox1_Exit() 将任何路径值更改立即存储到 CDP MyPath
    Option Explicit
    
    Private Sub UserForm_Activate()
    'Purp.: Display (stored) MyPath value on activation
        'MsgBox "Current value of MyPath = " & MyPath
        'display current value in textbox
        Me.TextBox1 = MyPath
    End Sub
    
    Private Sub TextBox1_Exit(ByVal Cancel As MSForms.ReturnBoolean)
        '(only for comparison)
        Dim OldPath As String
        OldPath = MyPath
        '~~~~~~~~~~~~~~~~~~~~~~~~
        'store changed Path value
        '~~~~~~~~~~~~~~~~~~~~~~~~
        MyPath = Me.TextBox1
        MsgBox "Changed MyPath " & vbNewLine & _
               "from: " & OldPath & vbNewLine & _
               "to:   " & MyPath
    End Sub
    
    

    用户表单代码部分 (2) - CDP 相关

    由于任何 UserForm 只代表一种特殊的,您甚至可以在 Userform 代码模块中使用 Get/Let 属性以及检查有效性的布尔函数。

    以下代码并不打算显示最好的,而只是显示进一步通往罗马的道路

    'Get-/Let-Properties
    Private Property Get MyPath() As Variant
        Const STOREDPATH As String = "MyPath"
        Dim cdps As DocumentProperties
        Set cdps = ThisWorkbook.CustomDocumentProperties
        If CDPExists(STOREDPATH) Then MyPath = cdps.Item(STOREDPATH)
    End Property
    
    Private Property Let MyPath(ByVal CDPValue)
        Const STOREDPATH As String = "MyPath"
        Dim cdps As DocumentProperties
        Set cdps = ThisWorkbook.CustomDocumentProperties
    
        If Not CDPExists(STOREDPATH) Then
            cdps.Add Name:=STOREDPATH, LinkToContent:=False, Type:=msoPropertyTypeString, Value:=CDPValue
        Else
            cdps.Item(STOREDPATH) = CDPValue
        End If
    End Property
    
    Private Function CDPExists(CDPName As String) As Boolean
    ' Purp.: return True|False if Custom Document Property (CDP) name exists
    ' Meth.: loop thru CustomDocumentProperties and check for existing sCDPName parameter
    ' Site : https://stackoverflow.com/questions/41766268/check-if-builtindocumentproperty-is-set-without-error-trapping
        Dim cdps As DocumentProperties
        Set cdps = ThisWorkbook.CustomDocumentProperties
        Dim boo  As Boolean                               ' boolean value showing element exists
        Dim cdp  As DocumentProperty                      ' element of CustomDocumentProperties Collection
        For Each cdp In cdps
            If LCase(cdp.Name) = LCase(CDPName) Then
                boo = True                                ' heureka
                Exit For                                  ' exit loop
            End If
        Next
        CDPExists = boo                                   ' return value to function
    End Function
    

    相关链接

    类似于上一个函数检查custom doc props,相关的老帖对待Check if built-in doc property is set without error trapping

    【讨论】:

      【解决方案4】:

      如果您喜欢冒险,这里有一个可以让您感到愉悦的解决方案。从标准代码模块开始。默认情况下,它将被称为Module1。将以下两个过程放在该模块中。模块叫什么无关紧要,但一定不要使用ThisWorkbook 代码模块,也不要使用任何以工作表命名的代码模块。

      函数“SavedDataFileName”是您声明要存储要记住的数据的位置的地方。您可以使用任何您喜欢的位置。该代码指定了一个类似C:\Users\[Your Name] 的位置。您也可以更改文件名。关键是所有这些都存储在一个地方,当文件被读取和写入时,它会被检索到。

      Option Explicit
          
      Function SavedDataFileName() As String
          ' create this as a function to be called by various parts of your code
          ' so that you don't have to repeat it in many places in case of future change
          
          SavedDataFileName = Environ("USERPROFILE") & "\SavedPath.txt"
      End Function
      

      下一个函数读取刚刚指定的文本文件。实际上,这是我准备好的代码。因此,它具有读取许多数据的能力。您只想阅读一个 - 路径。它会这样做。

      Function TextFile(Ffn As String, _
                        MaxLines As Integer) As String()
          ' 17 Oct 2016
          
          Dim Fun() As String                             ' Function return
          Dim i As Integer
          Dim Fid As Integer                              ' File ID
          
          If Len(Dir(Ffn)) Then
              ReDim Fun(MaxLines)                         ' arbitrary maximum
              Fid = FreeFile()
              Open Ffn For Input As #Fid
              While Not EOF(Fid)
                  Line Input #Fid, Fun(i)
                  Fun(i) = Trim(Fun(i))
                  i = i + 1
              Wend
              Close #Fid
              ReDim Preserve Fun(i - 1)
          End If
          TextFile = Fun
      End Function
      

      现在请转到您希望保留数据的表单的代码表。以下所有程序都必须在该表格上。不要将它们安装在其他任何地方。它们只有在安装在该特定代码表上时才能工作。

      第一个过程在表单初始化时运行,这意味着它第一次创建时 - 在启动时。

      Option Explicit
      
      Private Sub UserForm_Initialize()
          ' 13 May 2017
          
          Dim SavedData() As String
          
          On Error GoTo EndRetrieval
          SavedData = TextFile(SavedDataFileName, 10)
          TextBox1.Text = SavedData(0)
          ' you can pre-load more controls in your form here
      EndRetrieval:    
      End Sub
      

      在此子文件中打开文本文件并从中导入数据。预计最多有 10 个数据。您可以将此数字设置得更高或更低。设置得越高,临时使用的内存空间就越多。您只需要 1 个数据项。这将具有索引号 0(零),并将其分配给 TextBox1。当然,你可以改变它。如果遇到错误,将不执行任何操作,文本框保持空白。

      下一个过程将在您退出 TextBox1 时运行。当然,您可以更改其名称以引用另一个文本框。其目的是确保文本框包含有效的路径名。如果用户输入了无效的名称,他会被告知。

      Private Sub TextBox1_Exit(ByVal Cancel As MSForms.ReturnBoolean)
          ' 13 May 2017
          
          Dim Ffn As String                       ' Full file name
          
          Ffn = Trim(TextBox1.Text)
          On Error Resume Next
          ' For testing MsgMe is a sub containing a MsgBox.
          ' MsgMe Dir(Ffn & "\", vbDirectory) & ", " & Len(Dir(Ffn & "\", vbDirectory))
          Cancel = (Len(Ffn) = 0) Or (Len(Dir(Ffn & "\", vbDirectory)) = 0)
          If Not Cancel Then Cancel = CBool(Err.Number)    ' in case Dir(Ffn) caused an error
      
          If Cancel Then
              MsgBox "The path you entered isn't valid." & vbCr & _
                     "Please enter a valid path."
          Else
              TextBox1.Text = Ffn                ' removed leading/trailing blanks
          End If
      End Sub
      

      最后的过程在表单关闭时运行。它将 TextBox1 的当前设置写入文本文件,下次加载表单时将从该文件中检索。

      Private Sub UserForm_Terminate()
          ' 12 May 2017
          
          Open SavedDataFileName For Output As #1
          Print #1, TextBox1.Text
          ' you can write more data to be  remembered here
          Close #1
      End Sub
      

      此过程仅将一行写入文本文件。如果要保存更多项目,只需将更多值打印到文件中。请注意未检查保存的数据的质量。这是在输入之后完成的。如果用户设法将错误路径偷偷带入文本框,则该错误路径将在第二天早上再次困扰他。

      【讨论】:

      • 嗯,MsgMe 是怎么回事?错字?你能解决它 - 有人在问......? MsgMe Dir(Ffn & "\", vbDirectory) & ", " & Len(Dir(Ffn & "\", vbDirectory))
      • 对不起,我引用了 CindyMeister 的这个旧评论:是否有可能澄清她关于 MsgMe 代码粒子的问题,因为这可能是 intruder 打扰了一个好的答案? @Variatus - 仅供参考,您可能对我对这篇旧帖的其他方法感兴趣。
      • 请原谅我忘记了我 4 年前编写的废话,感谢您认真对待它:-)。 MsgMe 是我当时保留的一个小程序的名称,用于发出一个带有参数的 MsgBox。在这里,我用它来测试要在代码的下一行中使用的两个值。在发布到这里之前应该已经删除了测试。现在我把它注释掉了。我当时没有看到 Cindy 的问题,否则我会回答的。对不起,辛迪。
      猜你喜欢
      • 2019-06-10
      • 2018-03-06
      • 2019-05-25
      • 2012-11-22
      • 2023-03-08
      • 2018-06-03
      • 1970-01-01
      • 1970-01-01
      • 2021-11-20
      相关资源
      最近更新 更多