在VB6中中断的应用程序标题
平台:Windows XP
开发平台:VB6
当尝试通过"生成"选项卡上的"项目属性"对话框设置应用程序标题时,它似乎以一定数量的字符无声地切断了标题。还通过App.Title属性尝试了此操作,它似乎也遇到了同样的问题。我对此并不在乎,但质量检查部门坚持认为,我们需要显示整个标题。
有人对此有解决方法或者解决方法吗?
编辑:对于那些回答了大约40个字符限制的人,这就是我的怀疑-因此,我对可能的解决方法:-)提出了疑问。
实际上,我发布了这个问题来尝试帮助同一个开发人员,因此,当我周一见到她时,我将向她指出所有出色建议,看看是否有任何建议可以帮助她弄清楚这一点。我确实知道,由于某种原因,应用程序显示的某些对话框似乎从App.Title设置中拾取了字符串,这就是为什么她问我有关字符串长度的限制的原因。
我只是希望我能从Microsoft找到确定的内容(例如某种KB注释),以便她可以向我们的质量检查部门展示它,以便他们意识到这仅仅是VB的局限性。
解决方案
似乎VB6将App.Title属性限制为40个字符。不幸的是,我无法在MSDN上找到任何详细说明此行为的文档。 (不幸的是,我没有将文档加载到仍保留我的VB6副本的计算机上。)
我进行了一个长标题的实验,这就是观察到的行为。如果标题超过40个字符,则会被截断。
我刚刚在IDE中创建了一个标准EXE项目,并在"项目属性"选项卡下的应用程序标题字段中键入了文本,直到我填写了该字段。通过此快速测试,似乎App.Title限制为40个字符。接下来,我通过将以下代码放入为项目创建的默认格式(Form1)中,在代码中进行了尝试:
Private Sub Form_Load() App.Title = String(41, "X") MsgBox Len(App.Title) End Sub
此快速测试确认了40个字符的限制,因为即使代码尝试将App.Title设置为41个字符的字符串,MsgBox也会显示40。
如果让完整的字符串显示在Form的标题栏中确实很重要,那么我唯一能确保确保显示整个标题的唯一方法就是获取标题栏文本的宽度并使用该宽度来增加宽度表格的格式,以便可以容纳完整的标题字符串。如果可以找到正确的API实例,我可能会回来并为此发布代码,但是在Form_Load事件中可能看起来像这样:
Dim nTitleBarTextWidth As Long Dim nNewWidth As Long Me.Caption = "My really really really really really long app title here" ' Get titlebar text width (somehow) ' nTitleBarTextWidth = GetTitleBarTextWidth() ' Compute the new width for the Form such that the title will fit within it ' ' (May have to add a constant to this to make sure the title fits correctly) ' nNewWidth = Me.ScaleX(nTitleBarTextWidth, vbPixels, Me.ScaleMode) ' If the new width is bigger than the forms current size, use the new width ' If nNewWidth > Me.Width Then Form.Width = nNewWidth End If
使用Windows API的一种解决方案
免责声明:恕我直言,这似乎只是为了满足问题中所述的要求,但已过分解决了,但是本着为问题提供一个(希望)完整答案的精神,这无济于事...
这是我在MSDN中浏览了一段时间后想出的一个工作版本,直到我终于找到关于vbAccelerator的文章,使我的工作转向。
- 请参阅vbAccelerator页面以获取原始文章(与问题没有直接关系,但是我有足够的机会来制定答案)
基本前提是首先计算表单标题文本的宽度,然后使用GetSystemMetrics获取窗口各个位的宽度,例如边框和窗口框架的宽度,"最小化","最大化"和"关闭"按钮的宽度。 ,依此类推(为了可读性/清晰度,我将它们分成了自己的函数)。我们需要考虑窗口的这些部分,以便为表单计算出准确的新宽度。
为了准确计算表单标题的宽度("范围"),我们需要获取系统标题字体,因此需要调用SystemParametersInfo和CreateFontIndirect以及相关的优点。
所有这些工作的最终结果是GetRecommendedWidth函数,该函数计算所有这些值并将它们加在一起,外加一些额外的填充,以便在标题的最后一个字符和控制按钮之间留出一定的空间。如果此新宽度大于窗体的当前宽度,则GetRecommendedWidth将返回此(更大)宽度,否则,它将返回窗体的当前宽度。
我只是简短地测试了一下,但看起来工作正常。但是,由于它使用Windows API函数,因此我们可能要格外小心,尤其是因为它正在复制内存。我也没有添加健壮的错误处理。
顺便说一句,如果有人这样做的方式更简洁,使用更少,或者如果我错过了自己的代码中的某些内容,请告诉我。
要尝试,请将以下代码粘贴到新模块中
Option Explicit Private Type SIZE cx As Long cy As Long End Type Private Const LF_FACESIZE = 32 'NMLOGFONT: This declaration came from vbAccelerator (here is what he says about it):' ' ' ' For some bizarre reason, maybe to do with byte ' ' alignment, the LOGFONT structure we must apply ' ' to NONCLIENTMETRICS seems to require an LF_FACESIZE ' ' 4 bytes smaller than normal: ' Private Type NMLOGFONT lfHeight As Long lfWidth As Long lfEscapement As Long lfOrientation As Long lfWeight As Long lfItalic As Byte lfUnderline As Byte lfStrikeOut As Byte lfCharSet As Byte lfOutPrecision As Byte lfClipPrecision As Byte lfQuality As Byte lfPitchAndFamily As Byte lfFaceName(LF_FACESIZE - 4) As Byte End Type Private Type LOGFONT lfHeight As Long lfWidth As Long lfEscapement As Long lfOrientation As Long lfWeight As Long lfItalic As Byte lfUnderline As Byte lfStrikeOut As Byte lfCharSet As Byte lfOutPrecision As Byte lfClipPrecision As Byte lfQuality As Byte lfPitchAndFamily As Byte lfFaceName(LF_FACESIZE) As Byte End Type Private Type NONCLIENTMETRICS cbSize As Long iBorderWidth As Long iScrollWidth As Long iScrollHeight As Long iCaptionWidth As Long iCaptionHeight As Long lfCaptionFont As NMLOGFONT iSMCaptionWidth As Long iSMCaptionHeight As Long lfSMCaptionFont As NMLOGFONT iMenuWidth As Long iMenuHeight As Long lfMenuFont As NMLOGFONT lfStatusFont As NMLOGFONT lfMessageFont As NMLOGFONT End Type Private Enum SystemMetrics SM_CXBORDER = 5 SM_CXDLGFRAME = 7 SM_CXFRAME = 32 SM_CXSCREEN = 0 SM_CXICON = 11 SM_CXICONSPACING = 38 SM_CXSIZE = 30 SM_CXEDGE = 45 SM_CXSMICON = 49 SM_CXSMSIZE = 52 End Enum Private Const SPI_GETNONCLIENTMETRICS = 41 Private Const SPI_SETNONCLIENTMETRICS = 42 Private Declare Function GetTextExtentPoint32 Lib "gdi32" Alias "GetTextExtentPoint32A" _ (ByVal hdc As Long, _ ByVal lpszString As String, _ ByVal cbString As Long, _ lpSize As SIZE) As Long Private Declare Function GetSystemMetrics Lib "user32" (ByVal nIndex As SystemMetrics) As Long Private Declare Function SystemParametersInfo Lib "user32" Alias "SystemParametersInfoA" ( _ ByVal uAction As Long, _ ByVal uParam As Long, _ lpvParam As Any, _ ByVal fuWinIni As Long) As Long Private Declare Function SelectObject Lib "gdi32" (ByVal hdc As Long, ByVal hObject As Long) As Long Private Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As Long Private Declare Function CreateFontIndirect Lib "gdi32" Alias "CreateFontIndirectA" (lpLogFont As LOGFONT) As Long Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long) Private Function GetCaptionTextWidth(ByVal frm As Form) As Long '-----------------------------------------------' ' This function does the following: ' ' ' ' 1. Get the font used for the forms caption ' ' 2. Call GetTextExtent32 to get the width in ' ' pixels of the forms caption ' ' 3. Convert the width from pixels into ' ' the scaling mode being used by the form ' ' ' '-----------------------------------------------' Dim sz As SIZE Dim hOldFont As Long Dim hCaptionFont As Long Dim CaptionFont As LOGFONT Dim ncm As NONCLIENTMETRICS ncm.cbSize = LenB(ncm) If SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, ncm, 0) = 0 Then ' What should we do if we the call fails? Change as needed for your app,' ' but this call is unlikely to fail anyway' Exit Function End If CopyMemory CaptionFont, ncm.lfCaptionFont, LenB(CaptionFont) hCaptionFont = CreateFontIndirect(CaptionFont) hOldFont = SelectObject(frm.hdc, hCaptionFont) GetTextExtentPoint32 frm.hdc, frm.Caption, Len(frm.Caption), sz GetCaptionTextWidth = frm.ScaleX(sz.cx, vbPixels, frm.ScaleMode) 'clean up, otherwise bad things will happen...' DeleteObject (SelectObject(frm.hdc, hOldFont)) End Function Private Function GetControlBoxWidth(ByVal frm As Form) As Long Dim nButtonWidth As Long Dim nButtonCount As Long Dim nFinalWidth As Long If frm.ControlBox Then nButtonCount = 1 'close button is always present' nButtonWidth = GetSystemMetrics(SM_CXSIZE) 'get width of a single button in the titlebar' ' account for min and max buttons if they are visible' If frm.MinButton Then nButtonCount = nButtonCount + 1 If frm.MaxButton Then nButtonCount = nButtonCount + 1 nFinalWidth = nButtonWidth * nButtonCount End If 'convert to whatever scale the form is using' GetControlBoxWidth = frm.ScaleX(nFinalWidth, vbPixels, frm.ScaleMode) End Function Private Function GetIconWidth(ByVal frm As Form) As Long Dim nFinalWidth As Long If frm.ControlBox Then Select Case frm.BorderStyle Case vbFixedSingle, vbFixedDialog, vbSizable: 'we have an icon, gets its width' nFinalWidth = GetSystemMetrics(SM_CXSMICON) Case Else: 'no icon present, so report zero width' nFinalWidth = 0 End Select End If 'convert to whatever scale the form is using' GetIconWidth = frm.ScaleX(nFinalWidth, vbPixels, frm.ScaleMode) End Function Private Function GetFrameWidth(ByVal frm As Form) As Long Dim nFinalWidth As Long If frm.ControlBox Then Select Case frm.BorderStyle Case vbFixedSingle, vbFixedDialog: nFinalWidth = GetSystemMetrics(SM_CXDLGFRAME) Case vbSizable: nFinalWidth = GetSystemMetrics(SM_CXFRAME) End Select End If 'convert to whatever scale the form is using' GetFrameWidth = frm.ScaleX(nFinalWidth, vbPixels, frm.ScaleMode) End Function Private Function GetBorderWidth(ByVal frm As Form) As Long Dim nFinalWidth As Long If frm.ControlBox Then Select Case frm.Appearance Case 0 'flat' nFinalWidth = GetSystemMetrics(SM_CXBORDER) Case 1 '3D' nFinalWidth = GetSystemMetrics(SM_CXEDGE) End Select End If 'convert to whatever scale the form is using' GetBorderWidth = frm.ScaleX(nFinalWidth, vbPixels, frm.ScaleMode) End Function Public Function GetRecommendedWidth(ByVal frm As Form) As Long Dim nNewWidth As Long ' An abitrary amount of extra padding so that the caption text ' ' is not scrunched up against the min/max/close buttons ' Const PADDING_TWIPS = 120 nNewWidth = _ GetCaptionTextWidth(frm) _ + GetControlBoxWidth(frm) _ + GetIconWidth(frm) _ + GetFrameWidth(frm) * 2 _ + GetBorderWidth(frm) * 2 _ + PADDING_TWIPS If nNewWidth > frm.Width Then GetRecommendedWidth = nNewWidth Else GetRecommendedWidth = frm.Width End If End Function
然后将以下内容放入Form_Load事件中
Private Sub Form_Load() Me.Caption = String(100, "x") 'replace this with your caption' Me.Width = GetRecommendedWidth(Me) End Sub
+1大卫。
我们确定是标题吗?标题是Windows任务栏中显示的内容。使用标题在表单的标题栏中设置文本。
MsgBox-Function采用标题参数。如果我们不想更改对MsgBox-Function的每次调用,则可以"覆盖"默认行为:
Function MsgBox(Prompt, Optional Buttons As VbMsgBoxStyle = vbOKOnly, Optional Title, Optional HelpFile, Optional Context) As VbMsgBoxResult If IsMissing(Title) Then Title = String(40, "x") & "abc" MsgBox = Interaction.MsgBox(Prompt, Buttons, Title, HelpFile, Context) End Function
编辑:正如Mike Spross指出的那样:这只会隐藏正常的MsgBox-Function。如果要从另一个项目访问自定义MsgBox,则必须对其进行限定。