Я настроил несколько шаблонов, в которых пользователи могут копировать из других источников и вставлять в документ, а вставленный текст по умолчанию всегда будет основным текстом, используя приведенный ниже сценарий, который отлично работает, за исключением случаев, когда они хотят вставить таблицу. Я также удалил вставьте параметры с ленты вместе со многими другими параметрами, чтобы контролировать, что пользователи могут изменить.
Когда таблицы вставляются, они хотели бы сохранить форматирование таблицы (в идеале, используя форматирование назначения, но по одному шагу за раз). В результате своего исследования я обнаружил, что невозможно узнать, является ли скопированный текст таблицей, поскольку буфер обмена не хранит эту конкретную информацию в виде слова (если только это не изменилось с тех пор, как я прочитал сообщения), и я ничего не нашел. это предполагает, что в событии вставки есть что-то, что идентифицировало бы его как таблицу, если только оно не было сначала вставлено в пустой документ. Мне интересно, можно ли определить, является ли это таблицей из источника, пока источник, очевидно, все еще открыт, каким-то образом используя VBA?
Ничто в моем исследовании до сих пор не указывало на возможность определить, что таблица копируется/вставляется, но я все равно решил спросить.
Dim newText As String
Dim clipboardText As String
Selection.TypeText vbCrLf
Selection.Style = "Body Text"
clipboardText = GetClipboardText()
newText = vbCrLf & clipboardText
' Applying formatting to the pasted text
With Selection
.Font.Bold = False
.Font.Size = 11
.Font.Color = wdColorBlack
.Font.Name = "Calibri"
.ParagraphFormat.Alignment = wdAlignParagraphLeft
.ParagraphFormat.SpaceAfter = 1
.Range.HighlightColorIndex = wdNoHighlight
End With
' Pasting text at the current selection
Selection.TypeText newText
Вы не копируете таблицу (только) из Excel? Если из Excel, то думаю можно (пусть и не так просто) определить, есть ли в буфере обмена таблица. Я, конечно, могу проверить, является ли это Range
, и я думаю, что должна быть возможность определить, является ли соответствующий диапазон таблицей или частью таблицы... Чего вы хотите с этой точки зрения? Если диапазон в буфере обмена является частью таблицы, вставить всю таблицу? Все по столу DataBodyRange
? Только скопированный диапазон (с сохранением исходного формата)? Или что? Кроме того, дело не в «буфере обмена Word», а в буфере обмена Windows.
@TimothyRylatt по какой-то причине текст не вставлялся как основной текст, поэтому я вставил настройки стиля в качестве уловки для них.
@FaneDuru чаще всего таблица будет взята из другого текстового документа.
Почему бы просто не использовать Selection.PasteAndFormat Type:=wdFormatPlainText
см.: Learn.microsoft.com/en-us/office/vba/api/… и Learn.microsoft.com/en-us/office/vba/api/word.wdrecoverytype
Чтобы форматирование таблицы работало правильно, базовый стиль должен быть «Обычный», который также должен соответствовать настройкам документа по умолчанию.
Вы не ответили на мой уточняющий вопрос, спустя некоторое время...
Итак, следующее решение проверяет, находится ли в буфере обмена (часть) таблицы Excel (listObject
). Его можно легко адаптировать для проверки того, вся ли таблица скопирована, таблица DataBodyRange
или что-то еще, если она логически/четко определена...
Код предназначен для работы в 64-битной версии VBA7. Его можно адаптировать и для работы в 32-битной версии.
Option Explicit
Private Declare PtrSafe Function lstrcpy Lib "kernel32" (ByVal lpString1 As Any, ByVal lpString2 As Any) As LongPtr
Private Declare PtrSafe Function GlobalLock Lib "kernel32" (ByVal hMem As LongPtr) As LongPtr
Private Declare PtrSafe Function GlobalUnlock Lib "kernel32" (ByVal hMem As LongPtr) As Long
Private Declare PtrSafe Function OpenClipboard Lib "user32" (ByVal hwnd As LongPtr) As Long
Private Declare PtrSafe Function CloseClipboard Lib "user32" () As Long
Private Declare PtrSafe Function GetClipboardData Lib "user32" (ByVal lngFormat As Long) As LongPtr 'modificat din Long in Any
Private Declare PtrSafe Function EnumClipboardFormats Lib "user32" (ByVal wFormat As Long) As Long
Private Declare PtrSafe Function GetClipboardFormatNameA Lib "user32" (ByVal wFormat As Long, ByVal lpString As String, ByVal nMaxCount As Long) As Long
Sub testIsTableInClipboard() 'place a table (part) in memory and TEST IF CLIPBOARD CONTAINS a table (part)
Debug.Print IsTableInClipboard
End Sub 'then copy something else (text, an ordinary range etc.) and test it again...
'or paste the clipboard content using Excel table format:
Sub testPasteExcelTable() 'paste the table formatted as Excel table:
If IsTableInClipboard Then
Selection.PasteExcelTable False, False, False 'Pastes and formats a Microsoft Excel table.
'expression. PasteExcelTable( _LinkedToExcel_ , _WordFormatting_ , _RTF_ ) 'method's parameters
End If
End Sub
Function IsTableInClipboard() As Boolean 'it checks if an EXCEL TABLE (
Dim dataHwnd As LongPtr 'pointer to clipboard data
Dim intOffset As Integer 'for parsing clipboard data
Dim strClipData As String 'clipboard data
Dim strGetClipRange As String 'return range address (wb and ws)
Dim rngFormatID As Long 'id number for link format (Range)
Dim strWorkbookName As String 'the range workbook to be used to set Excel Session!
Const intMaxSize As Integer = 256 'limit for r1c1 to a1 conversion
rngFormatID = getRangeFormatID("Link") 'extracting the id number for "link" format (Range)
If OpenClipboard(0&) = 0 Then GoTo safeExit 'go to code end to unlock and close clipboard and exit
dataHwnd = GetClipboardData(rngFormatID) 'pointer to clipboard data
If dataHwnd = CLngPtr(0) Then GoTo safeExit 'could not allocate memory
dataHwnd = GlobalLock(dataHwnd) 'lock clipboard memory, so we can reference
If IsNull(dataHwnd) Then GoTo safeExit 'if could not lock clipboard memory
intOffset = 0 'start parsing data
strClipData = Space$(intMaxSize) 'initialize string
Call lstrcpy(strClipData, dataHwnd + intOffset) 'copy pointer to string
If strClipData = Space$(intMaxSize) Then GoTo safeExit 'not excel range on clipboard
strClipData = Mid(strClipData, 1, InStr(1, strClipData, Chr$(0), 0) - 1) 'trim null character
If strClipData <> "Excel" Then GoTo safeExit 'not excel range on clipboard
intOffset = intOffset + 1 + Len(strClipData) 'next offset
strClipData = Space$(intMaxSize) 'reset string
Call lstrcpy(strClipData, dataHwnd + intOffset) 'book and sheet next to it
strWorkbookName = VBA.Replace(Split(strClipData, "]")(0), "[", "") 'the workbook name where from the range belongs!
'Debug.Print strWorkbookName: Stop
strClipData = Mid(strClipData, 1, InStr(1, strClipData, Chr$(0), 0) - 1) 'trim null character
strGetClipRange = "'" & strClipData & "'!" 'get workbookbook and sheet
intOffset = intOffset + 1 + Len(strClipData) 'next offset
strClipData = Space$(intMaxSize) 'initialize string
Call lstrcpy(strClipData, dataHwnd + intOffset) 'range next
strClipData = Mid(strClipData, 1, InStr(1, strClipData, Chr$(0), 0) - 1) 'trim null character
strGetClipRange = strGetClipRange & strClipData 'add range to extracted string (xlR1C1 style)
'Dim exApp As Excel.Application, ClipBRange As Excel.Range, tbl As Excel.ListObject 'i tneeds reference to Excel...
Dim exApp As Object, ClipBRange As Object, tbl As Object
Set exApp = GetObject(strWorkbookName).Application
strGetClipRange = exApp.ConvertFormula(strGetClipRange, xlR1C1, xlA1) 'convert the range style
Set ClipBRange = exApp.Range(strGetClipRange) 'range in a1 style
Set tbl = ClipBRange.ListObject 'check if ClipBRange is part of an Excel table
If Not tbl Is Nothing Then 'if tbl is not nothing
Call GlobalUnlock(dataHwnd) 'unlock clipboard memory
Call CloseClipboard 'close ClipBoard
Set exApp = Nothing 'release Excel app from memory
IsTableInClipboard = True: Exit Function 'make it True and exit function
End If
safeExit:
Call GlobalUnlock(dataHwnd) 'unlock clipboard memory
Call CloseClipboard 'close ClipBoard
End Function
Function getRangeFormatID(strFormatName As String) As Long 'it looks that for a range the ID is not a constant...
Dim FormatId As Long, retFormat As String, formatLength As Integer
If OpenClipboard(0&) = 0 Then Exit Function 'could not open clipboard
formatLength = Len(strFormatName) + 30 'we only need a couple extra to make sure there isn't more
FormatId = 0 'initialized at zero
Do
retFormat = Space(formatLength) 'initialize string
GetClipboardFormatNameA FormatId, retFormat, formatLength 'get the id name
retFormat = Trim(retFormat) 'trim spaces
If retFormat <> "" Then 'if some string remained
retFormat = Left(retFormat, Len(retFormat) - 1) 'get rid of terminal character
If retFormat = strFormatName Then 'if it matches strFormatName
getRangeFormatID = FormatId 'return the correspondent id number
Exit Do 'exit Loop
End If
End If
FormatId = EnumClipboardFormats(FormatId) 'get the next id number
Loop Until FormatId = 0 'exit the loop after last id number
Call CloseClipboard 'close clipboard
End Function
testIsTableInClipboard
. Оно вернется True
. Затем скопируйте что-нибудь еще (строки, файлы, обычный диапазон и т. д.) и запустите его снова. На этот раз оно вернется False
Каждая строка кода прокомментирована таким образом, чтобы ее было легко понять (я думаю). Если что-то не совсем понятно, не стесняйтесь обращаться за разъяснениями.
Пожалуйста, оставьте отзыв после тестирования.
@jonsson Разве вы не нашли время протестировать предложенное выше решение? Если проверить, не сделал ли он то, что вам нужно?
В вопросе нет ничего, что указывало бы на то, что Excel является источником таблицы.
Выполнение чего-то подобного не займет много времени, и вы, возможно, сможете выполнить частичное или все форматирование таблицы во временном документе, возможно (не проверяли), вставив его в реальный документ с помощью .FormattedText.
Но, возможно, слишком медленно для тех случаев, когда они не вставляют таблицу.
Function IncludesATable() As Boolean
Dim d As Word.Document
Application.ScreenUpdating = False
On Error Goto problem
With Application.Documents.Add(Visible:False)
.Content.Paste
IncludesATable = (.Content.Tables.Count > 0)
.Close SaveChanges:=wdDoNotSaveChanges
End With
problem:
Application.ScreenUpdating = True
End Function
' Test routine
Sub TableNoTable()
If IncludesATable Then
Selection.TypeText "There's a table."
Else
Selection.TypeText "There isn't a table."
End If
End Sub
Спасибо @jonsson, это решит проблему вставки таблиц независимо от источника. Кажется, он не слишком запаздывает, но посмотрим, получу ли я какие-либо жалобы, поскольку в настоящее время документ создается с использованием копирования и вставки.
@MelForsyth Что ж, если вам действительно нужно вернуться к чему-то, что проверяет содержимое буфера обмена, вы можете просто проверить клип формата HTML и найти элемент <TABLE>.
Почему вы применяете все это ручное форматирование поверх стиля основного текста? Если именно так должен выглядеть основной текст, почему вы не изменили стиль, чтобы иметь такие настройки?