アレ的な猫
VbのフォームをなんとHTMLに出せるのだ!!🎊🎊
Option Compare Database
Option Explicit
' DPI設定
Const DEFAULT_DPI As Integer = 96
' デフォルト設定
Const DEFAULT_FONT_NAME As String = "Arial"
Const DEFAULT_FONT_SIZE As Integer = 10
Const DEFAULT_FONT_BOLD As Boolean = False
Const DEFAULT_FONT_ITALIC As Boolean = False
Const DEFAULT_FORE_COLOR As Long = vbBlack ' #000000
Const DEFAULT_BACK_COLOR As Long = vbWhite ' #FFFFFF
Const DEFAULT_BORDER_STYLE As Integer = 1 ' 実線
' システムカラー定数
Const COLOR_SCROLLBAR = 0
Const COLOR_BACKGROUND = 1
Const COLOR_ACTIVECAPTION = 2
Const COLOR_INACTIVECAPTION = 3
Const COLOR_MENU = 4
Const COLOR_WINDOW = 5
Const COLOR_WINDOWFRAME = 6
Const COLOR_MENUTEXT = 7
Const COLOR_WINDOWTEXT = 8
Const COLOR_CAPTIONTEXT = 9
Const COLOR_ACTIVEBORDER = 10
Const COLOR_INACTIVEBORDER = 11
Const COLOR_APPWORKSPACE = 12
Const COLOR_HIGHLIGHT = 13
Const COLOR_HIGHLIGHTTEXT = 14
Const COLOR_BTNFACE = 15
Const COLOR_BTNSHADOW = 16
Const COLOR_GRAYTEXT = 17
Const COLOR_BTNTEXT = 18
Const COLOR_INACTIVECAPTIONTEXT = 19
Const COLOR_BTNHIGHLIGHT = 20
Const COLOR_2NDACTIVECAPTION = 27 ' Win98以降
Const COLOR_2NDINACTIVECAPTION = 28 ' Win98以降
' Windows API関数の宣言(PtrSafe追加)
Private Declare PtrSafe Function GetSysColor Lib "user32" (ByVal nIndex As Long) As Long
Private Declare PtrSafe Function SetSysColors Lib "user32" (ByVal nChanges As Long, lpSysColor As Long, lpColorValues As Long) As Long
' コントロールの構造を保持するカスタム型
Type ControlInfo
Name As String
TypeName As String
Left As Long
Top As Long
Width As Long
Height As Long
FontName As String
FontSize As Integer
FontBold As Boolean
FontItalic As Boolean
ForeColor As Long
BackColor As Long
BorderStyle As Integer
Caption As String
Parent As String ' 親コンテナの名前
End Type
' Twipsをピクセルに変換する関数
Private Function TwipsToPixels(twips As Long, dpi As Integer) As Long
TwipsToPixels = twips * dpi / 1440
End Function
' 16進数カラーコードに変換する関数(システムカラー対応、αはオプション)
Private Function LongToHexColor(color As Long, Optional alpha As Byte = 255) As String
Dim r As Long, g As Long, b As Long
Dim isSystemColor As Boolean
isSystemColor = ((color And &HFF000000) = &H80000000)
If isSystemColor Then
Dim sysColorIndex As Long
sysColorIndex = color And &HFFFFFF
Dim sysColor As Long
sysColor = GetSysColor(sysColorIndex)
r = sysColor Mod 256
g = (sysColor \ 256) Mod 256
b = (sysColor \ 65536) Mod 256
Else
r = color Mod 256
g = (color \ 256) Mod 256
b = (color \ 65536) Mod 256
End If
If alpha = 255 Then
LongToHexColor = "#" & Right("0" & Hex(r), 2) & Right("0" & Hex(g), 2) & Right("0" & Hex(b), 2)
Else
LongToHexColor = "rgba(" & r & ", " & g & ", " & b & ", " & (alpha / 255) & ")"
End If
End Function
' フォームレイアウトをHTMLとして出力
Public Sub ExportFormToHTML(formName As String, Optional customDPI As Integer = DEFAULT_DPI)
On Error GoTo ErrorHandler
Dim frm As Form
Dim ctrl As Control
Dim html As String
Dim errorLog As String
Dim dpi As Integer
Dim controls() As ControlInfo
Dim i As Integer, j As Integer
dpi = customDPI
DoCmd.OpenForm formName, acDesign, , , , acHidden
Set frm = Forms(formName)
' コントロール情報を収集
ReDim controls(frm.Controls.Count - 1)
i = 0
For Each ctrl In frm.Controls
On Error Resume Next
With controls(i)
.Name = ctrl.Name
.TypeName = TypeName(ctrl)
.Left = TwipsToPixels(ctrl.Left, dpi)
.Top = TwipsToPixels(ctrl.Top, dpi)
.Width = TwipsToPixels(ctrl.Width, dpi)
.Height = TwipsToPixels(ctrl.Height, dpi)
.FontName = IIf(HasProperty(ctrl, "FontName") And Len(ctrl.FontName) > 0, ctrl.FontName, DEFAULT_FONT_NAME)
.FontSize = IIf(HasProperty(ctrl, "FontSize"), ctrl.FontSize, DEFAULT_FONT_SIZE)
.FontBold = IIf(HasProperty(ctrl, "FontBold"), ctrl.FontBold, DEFAULT_FONT_BOLD)
.FontItalic = IIf(HasProperty(ctrl, "FontItalic"), ctrl.FontItalic, DEFAULT_FONT_ITALIC)
.ForeColor = IIf(HasProperty(ctrl, "ForeColor"), ctrl.ForeColor, DEFAULT_FORE_COLOR)
.BackColor = IIf(HasProperty(ctrl, "BackColor"), ctrl.BackColor, DEFAULT_BACK_COLOR)
.BorderStyle = IIf(HasProperty(ctrl, "BorderStyle"), ctrl.BorderStyle, DEFAULT_BORDER_STYLE)
If HasProperty(ctrl, "Caption") Then .Caption = ctrl.Caption
.Parent = ""
' TabControl内のPageの子を特定
If HasProperty(ctrl, "Parent") And Not IsNull(ctrl.Parent) Then
If TypeName(ctrl.Parent) = "Page" Then
.Parent = ctrl.Parent.Name ' Pageの名前
End If
End If
End With
If Err.Number <> 0 Then
errorLog = errorLog & "Error in control '" & ctrl.Name & "': " & Err.Description & vbCrLf
Err.Clear
End If
On Error GoTo ErrorHandler
i = i + 1
Next ctrl
' PageをTabControlに紐づけ
For i = 0 To UBound(controls)
If controls(i).TypeName = "Page" Then
For j = 0 To UBound(controls)
If controls(j).TypeName = "TabControl" Then
If controls(i).Left >= controls(j).Left And controls(i).Top >= controls(j).Top And _
controls(i).Left + controls(i).Width <= controls(j).Left + controls(j).Width And _
controls(i).Top + controls(i).Height <= controls(j).Top + controls(j).Height Then
controls(i).Parent = controls(j).Name
End If
End If
Next j
End If
Next i
' OptionGroupやSubformの座標ベース親子関係(TabControl/Pageの子を除外)
For i = 0 To UBound(controls)
If controls(i).Parent = "" And controls(i).TypeName <> "Page" And controls(i).TypeName <> "TabControl" Then
For j = 0 To UBound(controls)
If i <> j And (controls(j).TypeName = "OptionGroup" Or controls(j).TypeName = "Subform") Then
With controls(i)
' TabControl領域外のコントロールのみを対象
Dim inTabControl As Boolean
inTabControl = False
For k = 0 To UBound(controls)
If controls(k).TypeName = "TabControl" Then
If .Left >= controls(k).Left And .Top >= controls(k).Top And _
.Left + .Width <= controls(k).Left + controls(k).Width And _
.Top + .Height <= controls(k).Top + controls(k).Height Then
inTabControl = True
Exit For
End If
End If
Next k
If Not inTabControl And _
.Left >= controls(j).Left And .Top >= controls(j).Top And _
.Left + .Width <= controls(j).Left + controls(j).Width And _
.Top + .Height <= controls(j).Top + controls(j).Height Then
.Parent = controls(j).Name
End If
End With
End If
Next j
End If
Next i
' HTMLヘッダー
html = "<!DOCTYPE html>" & vbCrLf
html = html & "<html lang=""ja"">" & vbCrLf
html = html & "<head>" & vbCrLf
html = html & " <meta charset=""UTF-8"">" & vbCrLf
html = html & " <title>" & frm.Name & "</title>" & vbCrLf
html = html & " <style>" & vbCrLf
html = html & " body { margin: 0; padding: 0; font-family: " & DEFAULT_FONT_NAME & "; }" & vbCrLf
html = html & " .form-container { position: relative; width: " & TwipsToPixels(frm.Width, dpi) & "px; height: " & TwipsToPixels(frm.InsideHeight, dpi) & "px; border: 1px solid #000; background-color: " & LongToHexColor(frm.Section(acDetail).BackColor) & "; }" & vbCrLf
html = html & " .control, .container { position: absolute; box-sizing: border-box; }" & vbCrLf
html = html & " .container { background-color: transparent; }" & vbCrLf
html = html & " input[type=""text""] { border: 1px solid #000; }" & vbCrLf
html = html & " button { border: 1px solid #000; }" & vbCrLf
html = html & " label { display: block; }" & vbCrLf
html = html & " select { border: 1px solid #000; }" & vbCrLf
html = html & " </style>" & vbCrLf
html = html & "</head>" & vbCrLf
html = html & "<body>" & vbCrLf
html = html & " <div class=""form-container"">" & vbCrLf
' 最上位コントロールを再帰的に出力
For i = 0 To UBound(controls)
If controls(i).Parent = "" Then
html = html & GenerateControlHTML(controls, i, dpi, 4, 0, 0)
End If
Next i
' HTMLフッター
html = html & " </div>" & vbCrLf
If Len(errorLog) > 0 Then
html = html & " <pre style=""color: red;"">=== Error Log ===<br>" & errorLog & "</pre>" & vbCrLf
End If
html = html & "</body>" & vbCrLf
html = html & "</html>"
' UTF-8でファイル出力
Dim filePath As String
filePath = CurrentProject.Path & "\Form_" & formName & ".html"
Dim stream As Object
Set stream = CreateObject("ADODB.Stream")
With stream
.Type = 2 ' adTypeText
.Charset = "UTF-8"
.Open
.WriteText html
.SaveToFile filePath, 2 ' adSaveCreateOverWrite
.Close
End With
Set stream = Nothing
DoCmd.Close acForm, formName, acSaveNo
MsgBox "Form exported to HTML (UTF-8): " & filePath, vbInformation
Exit Sub
ErrorHandler:
MsgBox "An error occurred: " & Err.Description, vbCritical
DoCmd.Close acForm, formName, acSaveNo
If Not stream Is Nothing Then stream.Close
End Sub
' コントロールのHTMLを再帰的に生成(親の座標を引数に追加)
Private Function GenerateControlHTML(controls() As ControlInfo, index As Integer, dpi As Integer, indentLevel As Integer, parentLeft As Long, parentTop As Long) As String
Dim html As String
Dim style As String
Dim i As Integer
Dim indent As String
Dim relativeLeft As Long
Dim relativeTop As Long
indent = String(indentLevel, " ")
With controls(index)
' 親コンテナの座標を引いて相対座標に変換
relativeLeft = .Left - parentLeft
relativeTop = .Top - parentTop
style = "style=""left: " & relativeLeft & "px; top: " & relativeTop & "px; width: " & .Width & "px; height: " & .Height & "px; "
style = style & "font-family: " & .FontName & "; font-size: " & .FontSize & "pt; "
If .FontBold Then style = style & "font-weight: bold; "
If .FontItalic Then style = style & "font-style: italic; "
style = style & "color: " & LongToHexColor(.ForeColor) & "; "
style = style & "background-color: " & LongToHexColor(.BackColor) & "; "
Select Case .BorderStyle
Case 0: style = style & "border: none; "
Case 1: style = style & "border: 1px solid #000; "
Case Else: style = style & "border: 1px dashed #000; "
End Select
style = style & """"
Select Case .TypeName
Case "TabControl"
html = indent & "<div class=""container tab-control"" " & style & ">" & vbCrLf
For i = 0 To UBound(controls)
If controls(i).Parent = .Name Then ' Pageを処理
html = html & GenerateControlHTML(controls, i, dpi, indentLevel + 2, .Left, .Top)
End If
Next i
html = html & indent & "</div>" & vbCrLf
Case "Page"
html = indent & "<div class=""container page"" " & style & ">" & vbCrLf
For i = 0 To UBound(controls)
If controls(i).Parent = .Name Then
html = html & GenerateControlHTML(controls, i, dpi, indentLevel + 2, .Left, .Top)
End If
Next i
html = html & indent & "</div>" & vbCrLf
Case "OptionGroup"
html = indent & "<div class=""container option-group"" " & style & ">" & vbCrLf
html = html & indent & " <legend>" & .Name & "</legend>" & vbCrLf
For i = 0 To UBound(controls)
If controls(i).Parent = .Name Then
html = html & GenerateControlHTML(controls, i, dpi, indentLevel + 2, .Left, .Top)
End If
Next i
html = html & indent & "</div>" & vbCrLf
Case "Subform"
html = indent & "<div class=""container subform"" " & style & ">" & vbCrLf
For i = 0 To UBound(controls)
If controls(i).Parent = .Name Then
html = html & GenerateControlHTML(controls, i, dpi, indentLevel + 2, .Left, .Top)
End If
Next i
html = html & indent & "</div>" & vbCrLf
Case "Label"
html = indent & "<label class=""control"" " & style & ">" & .Caption & "</label>" & vbCrLf
Case "TextBox"
html = indent & "<input type=""text"" class=""control"" " & style & " value=""" & .Name & """ name=""" & .Name & """>" & vbCrLf
Case "CommandButton"
html = indent & "<button class=""control"" " & style & ">" & .Caption & "</button>" & vbCrLf
Case "ComboBox"
html = indent & "<select class=""control"" " & style & "><option>" & .Name & "</option></select>" & vbCrLf
Case Else
html = indent & "<div class=""control"" " & style & ">[" & .TypeName & ": " & .Name & "]</div>" & vbCrLf
End Select
End With
GenerateControlHTML = html
End Function
' プロパティの存在を確認する補助関数
Private Function HasProperty(obj As Object, propName As String) As Boolean
On Error Resume Next
Call VBA.CallByName(obj, propName, VbGet)
HasProperty = (Err.Number = 0)
Err.Clear
On Error GoTo 0
End Function
' 使用例(テスト用メイン、変更なし)
Public Sub TestExportToHTML()
ExportFormToHTML "フォーム1", 96
End Sub
オマケ
Sub SplitTextByLineBreakWithColorAndInsertRows()
Dim ws As Worksheet
Dim rng As Range, cell As Range
Dim inputRange As Range
Dim textArray() As String
Dim i As Long, j As Long, k As Long
Dim maxLines As Long
Dim charColors() As Long '文字ごとの色を記録する配列
'ワークシートを設定
Set ws = ActiveSheet
'処理する範囲をユーザーに選択させる
On Error Resume Next
Set inputRange = Application.InputBox("範囲を選択してください", Type:=8)
On Error GoTo 0
If inputRange Is Nothing Then
MsgBox "範囲が選択されていません。終了します。"
Exit Sub
End If
'範囲内で最大の改行数を調べる
maxLines = 0
For Each cell In inputRange
If Not IsEmpty(cell) Then
textArray = Split(cell.Value, vbLf)
If UBound(textArray) > maxLines Then
maxLines = UBound(textArray)
End If
End If
Next cell
'必要に応じて行を挿入
If maxLines > 0 Then
ws.Rows(inputRange.Row + 1 & ":" & inputRange.Row + maxLines).Insert Shift:=xlDown
End If
'範囲をループして処理
For Each cell In inputRange
If Not IsEmpty(cell) Then
'改行で文字列を分割
textArray = Split(cell.Value, vbLf)
'文字ごとの色を記録する配列を準備
ReDim charColors(1 To Len(cell.Value))
For j = 1 To Len(cell.Value)
If Not IsNull(cell.Characters(j, 1).Font.Color) Then
charColors(j) = cell.Characters(j, 1).Font.Color
Else
charColors(j) = vbBlack 'デフォルトは黒
End If
Next j
'元のセルと挿入した行に文字列を配置
For i = LBound(textArray) To UBound(textArray)
If Len(textArray(i)) > 0 Then
'出力先セルに文字列を書き込み
ws.Cells(cell.Row + i, cell.Column).Value = textArray(i)
'文字ごとの色を適用
Dim startPos As Long
startPos = 1
For k = LBound(textArray) To i - 1
startPos = startPos + Len(textArray(k)) + 1 '改行分を加算
Next k
For j = 1 To Len(textArray(i))
Dim srcPos As Long
srcPos = startPos + j - 1
If srcPos <= UBound(charColors) Then
ws.Cells(cell.Row + i, cell.Column).Characters(j, 1).Font.Color = charColors(srcPos)
End If
Next j
End If
Next i
End If
Next cell
MsgBox "処理が完了しました!"
End Sub
おしまい。🙇♂️🙇♂️🙇♂️🙇♂️
