【问题标题】:Convert Characters to Twips for Access ColumnWidth将字符转换为缇以访问 ColumnWidth
【发布时间】:2014-03-27 03:02:57
【问题描述】:

我正在维护一些旧的 VB6 软件,需要根据要显示的字段字符的平均数量以编程方式设置 MS Access 列的宽度,就像在 Access 中的数据表视图中所做的那样。

然而,在 VB 中,该值必须以 Twips 指定,而且我在“字符数”和 Twips 之间进行转换时遇到了一些困难。

例如,如果字体是 Arial 10pt (96 DPI),并且我在 Access 中指定“50 个字符”,则该值通过 VB 中的Properties("ColumnWidth") 方法返回为“4530 Twips”。如果我在VB中通过CreateProperty("ColumnWidth")方法指定“4530”,Access中会显示“50”。

基于 Office 2010 的 Column Class 规范和 GetTextExtentPoint32 规范,我使用以下代码以 Twips 计算列宽,但对于上面的示例,返回值“5490”而是:

表格

'Identify normal style's font for Access
Dim Font As New StdFont
Font.Name = "Arial"
Font.Size = 10

'Calculate longest width of digits 0-9
Dim Digit As Integer
Dim MaxDigitWidth As Single
For Digit = 0 To 9
    Dim mdw As Single
    mdw = CalcTextWidth(Digit, Font)
    If mdw > MaxDigitWidth Then MaxDigitWidth = mdw
Next Digit

Dim MaxChars As Integer
Dim Width As Single, Pixels As Long, Twips As Long

'Identify number of characters to display horizontally
MaxChars = 50

'Adjust character value based on actual font metrics
Width = Int((MaxChars * MaxDigitWidth + 5) / MaxDigitWidth * 256) / 256

'Convert into screen resolution (TwipsPerPixelX = 1440 / 96 or 120 DPI)
Pixels = Int(((256 * Width + Int(128 / MaxDigitWidth)) / 256) * MaxDigitWidth)
Twips = Pixels * Screen.TwipsPerPixelX

模块

Private Declare Function CreateCompatibleDC Lib "Gdi32" (ByVal hDC 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 DeleteDC Lib "Gdi32" (ByVal hDC As Long) As Long
Private Declare Function GetTextExtentPoint32 Lib "Gdi32" Alias "GetTextExtentPoint32A" (ByVal hDC As Long, ByVal lpsz As String, ByVal cbString As Long, lpSize As SIZE) As Long

Private Type SIZE
    cx As Long
    cy As Long
End Type

'Calculate width in pixels of screen text
Public Function CalcTextWidth(ByVal Source As String, ByVal Font As StdFont) As Single
    Dim myFont As IFont
    Dim hFont As Long
    Dim mySize As Size
    Dim hDC As Long

    'Clone font
    Set myFont = New StdFont
    myFont.Name = Font.Name

    'Increase precision since GetTextExtentPoint32 returns a Long
    myFont.Size = Font.Size * 1000

    'Set device context as screen display for font metrics
    hDC = CreateCompatibleDC(0)

    'Calculate string width in pixels
    hFont = SelectObject(hDC, myFont.hFont)
    GetTextExtentPoint32 hDC, Source, Len(Source), mySize
    SelectObject hDC, hFont
    DeleteObject hFont
    DeleteDC hDC

    'Restore precision
    CalcTextWidth = mySize.cx / 1000
End Function

我还看到了一篇MS Knowledge Base 文章,介绍了如何在 Excel 中计算列宽,但提供的示例似乎与为 Office 公开的算法相冲突。

关于我做错了什么有什么想法吗?

谢谢。

** 3/20 更新 **

下面是代码块的简化版本,它使用之前定义的 FontTwips 设置 ColumnWidth 属性:

Dim db As Database
Dim td As TableDef
Dim prop As Property

Set db = CreateDatabase("db1.mdb", dbLangGeneral)
Set td = db.CreateTableDef("Table1")
td.Fields.Append td.CreateField("Field1", dbMemo)
db.TableDefs.Append td

Set prop = td.CreateProperty("DatasheetFontName", dbText, Font.Name): td.Properties.Append prop
Set prop = td.CreateProperty("DatasheetFontHeight", dbInteger, Font.Size): td.Properties.Append prop
Set prop = td.Fields("Field1").CreateProperty("ColumnWidth", dbInteger, Twips): td.Fields("Field1").Properties.Append prop

db.Close

另外,不确定是否重要,但我正在使用 VB6 Service Pack 6 和以下参考:

  • Visual Basic 应用程序
  • Visual Basic 运行时对象和过程
  • OLE 自动化
  • Microsoft DAO 3.6 对象库
  • Microsoft ActiveX 数据对象 2.5 库
  • Microsoft VBScript 正则表达式 5.5

【问题讨论】:

  • 这些是文本框吗?可以设置columnwidth to -2吗?
  • 我有兴趣在 Access 中将字段视为 Table 对象时设置它们的宽度。我考虑将它们指定为自动适应,但我更喜欢硬编码值以获得一致的显示。
  • 如果你想要一个硬编码的值,你可以使用设计器,不是吗?硬编码似乎有点矫枉过正
  • 数据库由VB创建。
  • 这是标记为 VB6。实际上是访问 VBA 吗?由于 UI 框架不同,因此有所不同。

标签: ms-access vb6


【解决方案1】:

在尝试了几个 API 来计算列宽后,包括 GetTextExtentPoint32GetCharABCWidths 和其他,以及内置的 TextWidth() 函数,由于四舍五入,这些都没有产生精确的测量值,我想出了一个方法使用 Excel 对象 精确地做到这一点。

在 Excel 中,一列有两个宽度属性:ColumnWidthWidth

ColumnWidth 是基于 Normal 样式字体的数字 0-9 的最大宽度水平显示的字符单位数。因此,如果定义了值“50”并在单元格 A1 中输入了 50 个零,则所有数字都将可见。

Width 是将 ColumnWidth 值重新转换为 Normal 样式字体的像素。

值得注意的是,通过VB6为Excel电子表格设置ColumnWidth可以以字符为单位进行;相反,Access 表的ColumnWidth 必须以缇为单位。

此外,可以更改 Excel 的 Normal 样式字体,而 Access 似乎没有办法这样做 - 可以为 Datasheet 设置默认字体,但这与Normal 风格的字体。

例如,如果定义了 Arial 10 pt 并且在 50 个字符处发生换行,那么应该指定 50 个字符单位的值似乎是合乎逻辑的;但是,58.8333 必须在 Access 中实际定义。此外,即使将注册表项 Default Font NameDefault Font Size 设置为所需的字体,Access 似乎也在使用硬编码的字体指标作为其计算的基础。

下面的代码显示了如何使用 Excel 对象通过让 Excel 执行字符单位到像素的转换来计算 Access 的 ColumnWidth。它还展示了如何计算单元格的行高,以便在不裁剪的情况下显示一定数量的行。

请注意,Excel 必须与 VB6 安装在同一工作站上,并且引用必须包含 Microsoft DAO 3.6 对象库

Option Explicit

Private Declare Function CreateCompatibleDC Lib "gdi32" (ByVal hDC As Long) As Long
Private Declare Function DeleteDC Lib "gdi32" (ByVal hDC As Long) As Long
Private Declare Function GetDeviceCaps Lib "gdi32.dll" (ByVal hDC As Long, ByVal nIndex As Long) As Long

Private Const LOGPIXELSX As Long = 88

'########################################################################

'Calculate height for an MS Access cell
Private Function AccessHeight(ByVal MaxLines As Long) As Single

    'Set number of lines to display without clipping (based on leading of Normal style font)
    Dim i As Integer
    Dim txt As String
    For i = 1 To MaxLines: txt = txt & "H" & IIf(i < MaxLines, vbCrLf, ""): Next

    'Measure height of lines (in twips); compensate for gridline padding
    '(note that device context is screen display since TextHeight a property of Form1)
    AccessHeight = TextHeight(txt) + 30
End Function

'########################################################################

'Calculate width for an MS Access cell
Private Function AccessWidth(ByVal MaxChars As Long) As Long
    Dim hDC, dpiX As Long
    Dim Excel, Workbook, Worksheet As Object
    Dim pixelsX As Single

    'Set device context as screen display and calculate horizontal DPI (96 or 120)
    hDC = CreateCompatibleDC(0)
    dpiX = GetDeviceCaps(hDC, LOGPIXELSX)
    DeleteDC hDC

    '#-------------------------------------------------------------------

    'Launch Excel as a system process
    Set Excel = CreateObject("Excel.Application")
    Set Workbook = Excel.Workbooks.Add
    Set Worksheet = Workbook.Worksheets.Add

    'Initialize Normal style so that one unit of column width equals width of one character
    With Workbook.Styles("Normal").Font
        .Name = Font.Name
        .Size = Font.Size
        .Bold = Font.Bold
        .Italic = Font.Italic
        .Underline = Font.Underline
        .Strikethrough = Font.Strikethrough
    End With

    '#-------------------------------------------------------------------

    'Set number of characters to display horizontally without wrapping (based on maximum width of digits 0-9)
    Worksheet.Cells(1, 1).ColumnWidth = MaxChars

    'Instruct Excel to convert from character units into screen pixels
    pixelsX = Worksheet.Cells(1, 1).Width * dpiX / 72

    'Convert screen pixels into twips
    AccessWidth = Int(pixelsX * 1440 / dpiX)

    'Kill system process
    Workbook.Close SaveChanges:=False
    Excel.Quit
End Function

'########################################################################

Private Sub Form_Load()

    'Identify Normal style font
    With Font
        .Name = "Arial"
        .Size = 10
        .Bold = False
        .Italic = False
        .Underline = False
        .Strikethrough = False
    End With

    '#-------------------------------------------------------------------

    Dim db As Database
    Dim td As TableDef
    Dim rs As Recordset
    Dim prop As Property
    Dim i As Integer

    'Create database
    ChDrive App.Path: ChDir App.Path
    Set db = CreateDatabase("db1.mdb", dbLangGeneral)
    Set td = db.CreateTableDef("Table1")
    td.Fields.Append td.CreateField("Field1", dbMemo)
    db.TableDefs.Append td

    'Set font
    Set prop = td.CreateProperty("DatasheetFontName", dbText, Font.Name): td.Properties.Append prop
    Set prop = td.CreateProperty("DatasheetFontHeight", dbInteger, Font.Size): td.Properties.Append prop

    'Set row height
    Dim MaxLines As Long
    MaxLines = 9
    Set prop = td.CreateProperty("RowHeight", dbInteger, AccessHeight(MaxLines)): td.Properties.Append prop

    'Set column width
    Dim MaxChars As Long
    MaxChars = 50
    Set prop = td.Fields("Field1").CreateProperty("ColumnWidth", dbInteger, AccessWidth(MaxChars)): td.Fields("Field1").Properties.Append prop

    'Add a record
    Set rs = db.OpenRecordset("Table1")
    rs.AddNew
    For i = 1 To MaxLines: rs!Field1 = rs!Field1 & String$(MaxChars, CStr(i)) & IIf(i < MaxLines, vbCrLf, ""): Next
    rs.Update
    rs.Close

    db.Close
    End
End Sub

【讨论】:

    猜你喜欢
    • 2011-03-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多