Insert line break in wrapped cell via code - vba
Is it possible to insert line break in a wrapped cell through VBA code? (similar to doing Alt-Enter when entering data manually)
I have set the cell's wrap text property to True via VBA code, and I am inserting data into it also through VBA code.
Yes. The VBA equivalent of AltEnter is to use a linebreak character:
ActiveCell.Value = "I am a " & Chr(10) & "test"
Note that this automatically sets WrapText to True.
Proof:
Sub test()
Dim c As Range
Set c = ActiveCell
c.WrapText = False
MsgBox "Activcell WrapText is " & c.WrapText
c.Value = "I am a " & Chr(10) & "test"
MsgBox "Activcell WrapText is " & c.WrapText
End Sub
You could also use vbCrLf which corresponds to Chr(13) & Chr(10). As Andy mentions in the comment below, you might be better off using ControlChars.Lf instead though.
Yes there are two ways to add a line feed:
Use the existing constant from VBA (click here for a list of existing vba constants) vbLf in the string you want to add a line feed, as such:
Dim text As String
text = "Hello" & vbLf & "World!"
Worksheets(1).Cells(1, 1) = text
Use the Chr() function and pass the ASCII character 10 in order to add a line feed, as shown bellow:
Dim text As String
text = "Hello" & Chr(10) & "World!"
Worksheets(1).Cells(1, 1) = text
In both cases, you will have the same output in cell (1,1) or A1.
Have a look at these two threads for more information:
What is the difference between a "line feed" and a "carriage return"?
Differences Between vbLf, vbCrLf & vbCr Constants
I know this question is really old, but as I had the same needs, after searching SO and google, I found pieces of answers but nothing usable. So with those pieces and bites I made my solution that I share here.
What I needed
Knowing the column width in pixels
Be able to measure the length of a string in pixels in order to cut it at the dimension of the column
What I found
About the width in pixels of a column, I found this in Excel 2010 DocumentFormat :
To translate the value of width in the file into the column width value at runtime (expressed in terms of pixels), use this calculation:
=Truncate(((256 * {width} + Truncate(128/{Maximum Digit Width}))/256)*{Maximum Digit Width})
Even if it's Excel 2010 format, it's still working in Excel 2016. I'll be able to test it soon against Excel 365.
About the width of a string in pixels, I used the solution proposed by #TravelinGuy in this question, with small corrections for typo and an overflow. By the time I'm writing this the typo is already corrected in his answer, but there is still the overflow problem. Nevertheless I commented his answer so there is everything over there for you to make it works flawlessly.
What I've done
Code three recursive functions working this way :
Function 1 : Guess the approximate place where to cut the sentence so if fits in the column and then call Function 2 and 3 in order to determine the right place. Returns the original string with CR (Chr(10)) characters in appropriate places so each line fits in the column size,
Function 2 : From a guessed place, try to add some more words in the line while this fit in the column size,
Function 3 : The exact opposite of function 2, so it retrieves words to the sentence until it fits in the column size.
Here is the code
Sub SplitLineTest()
Dim TextRange As Range
Set TextRange = FeuilTest.Cells(2, 2)
'Take the text we want to wrap then past it in multi cells
Dim NewText As String
NewText = SetCRtoEOL(TextRange.Value2, TextRange.Font.Name, TextRange.Font.Size, xlWidthToPixs(TextRange.ColumnWidth) - 5) '-5 to take into account 2 white pixels left and right of the text + 1 pixel for the grid
'Copy each of the text lines in an individual cell
Dim ResultArr() As String
ResultArr() = Split(NewText, Chr(10))
TextRange.Offset(2, 0).Resize(UBound(ResultArr) + 1, 1).Value2 = WorksheetFunction.Transpose(ResultArr())
End Sub
Function xlWidthToPixs(ByVal xlWidth As Double) As Long
'Fonction to convert the size of an Excel column width expressed in Excel unit(Range.ColumnWidth) in pixels
'Parameters : - xlWidth : that is the width of the column Excel unit
'Return : - The size of the column in pixels
Dim pxFontWidthMax As Long
'Xl Col sizing is related to workbook default string configuration and depends of the size in pixel from char "0". We need to gather it
With ThisWorkbook.Styles("Normal").Font
pxFontWidthMax = pxGetStringW("0", .Name, .Size) 'Get the size in pixels of the '0' character
End With
'Now, we can make the calculation
xlWidthToPixs = WorksheetFunction.Floor_Precise(((256 * xlWidth + WorksheetFunction.Floor_Precise(128 / pxFontWidthMax)) / 256) * pxFontWidthMax) + 5
End Function
Function SetCRtoEOL(ByVal Original As String, ByVal FontName As String, ByVal FontSize As Variant, ByVal pxAvailW) As String
'Function aiming to make a text fit into a given number of pixels, by putting some CR char between words when needed.
'If some words are too longs to fit in the given width, they won't be cut and will get out of the limits given.
'The function works recursively. Each time it find an End Of Line, it call itself with the remaining text until.
'The recursive process ends whent the text fit in the given space without needing to be truncated anymore
'Parameters : - Original : The text to fit
' - FontName : Name of the font
' - FontSize : Size of the font
' - pxAvailW : Available width in pixels in wich we need to make the text fit
'Return : - The orignal text with CR in place of spaces where the text needs to be cut to fit the width
'If we got a null string, there is nothing to do so we return a null string
If Original = vbNullString Then Exit Function
Dim pxTextW As Long
'If the text fit in, may be it's the original or this is end of recursion. Nothing to do more than returne the text back
pxTextW = pxGetStringW(Original, FontName, FontSize)
If pxTextW < pxAvailW Then
SetCRtoEOL = Original
Exit Function
End If
'The text doesn't fit, we need to find where to cut it
Dim WrapPosition As Long
Dim EstWrapPosition As Long
EstWrapPosition = Len(Original) * pxAvailW / pxTextW 'Estimate the cut position in the string given to a proportion of characters
If pxGetStringW(Left(Original, EstWrapPosition), FontName, FontSize) < pxAvailW Then
'Text to estimated wrap position fits in, we try to see if we can fits some more words
WrapPosition = FindMaxPosition(Original, FontName, FontSize, pxAvailW, EstWrapPosition)
End If
'If WrapPosition = 0, we didn't get a proper place yet, we try to find the previous white space
If WrapPosition = 0 Then
WrapPosition = FindMaxPositionRev(Original, FontName, FontSize, pxAvailW, EstWrapPosition)
End If
'If WrapPosition is still 0, we are facing a too long word for the pxAvailable. We'll cut after this word what ever. (Means we must search for the first white space of the text)
If WrapPosition = 0 Then
WrapPosition = InStr(Original, " ")
End If
If WrapPosition = 0 Then
'Words too long to cut, but nothing more to cut, we return it as is
SetCRtoEOL = Original
Else
'We found a wrap position. We recurse to find the next EOL and construct our response by adding CR in place of the white space
SetCRtoEOL = Left(Original, WrapPosition - 1) & Chr(10) & SetCRtoEOL(Right(Original, Len(Original) - WrapPosition), FontName, FontSize, pxAvailW)
End If
End Function
Function FindMaxPosition(ByVal Text As String, ByVal FontName As String, ByVal FontSize As Variant, ByVal pxAvailW, ByVal WrapPosition As Long) As Long
'Function that finds the maximum number of words fitting in a given space by adding words until it get out of the maximum space
'The function is inteded to work on text with a "guessed" wrap position that fit in the space allowed
'The function is recursive. Each time it guesses a new position and the word still fits in the space, it calls itself with a further WrapPosition
'Parameters : - Text : The text to fit
' - FontName : Name of the font
' - FontSize : Size of the font
' - pxAvailW : Available width in pixels in wich we need to make the text fit
' - WrapPosition : The initial wrap position, positionned someware in the text (WrapPosition < len(Text)) but inside pxAvailW
'Return : - The position were the text must be wraped to put as much words as possible in pxAvailW, but without getting outside of it. If no position can be found, returns 0
Dim NewWrapPosition As Long
Static isNthCall As Boolean
'Find next Whitespace position
NewWrapPosition = InStr(WrapPosition, Text, " ")
If NewWrapPosition = 0 Then Exit Function 'We can't find a wrap position, we return 0
If pxGetStringW(Left(Text, NewWrapPosition - 1), FontName, FontSize) < pxAvailW Then '-1 not to take into account the last white space
'It still fits, we can try on more word
isNthCall = True
FindMaxPosition = FindMaxPosition(Text, FontName, FontSize, pxAvailW, NewWrapPosition + 1)
Else
'It doesnt fit. If it was the first call, we terminate with 0, else we terminate with previous WrapPosition
If isNthCall Then
'Not the first call, we have a position to return
isNthCall = False 'We reset the static to be ready for next call of the function
FindMaxPosition = WrapPosition - 1 'Wrap is at the first letter of the word due to the function call FindMax...(...., NewWrapPosition + 1). The real WrapPosition needs to be minored by 1
Else
'It's the first call, we return 0 | Strictly speaking we can remove this part as FindMaxPosition is already 0, but it make the algo easier to read
FindMaxPosition = 0
End If
End If
End Function
Function FindMaxPositionRev(ByVal Text As String, ByVal FontName As String, ByVal FontSize As Variant, ByVal pxAvailW, ByVal WrapPosition As Long) As Long
'Function working backward of FindMaxPosition. It finds the maximum number of words fitting in a given space by removing words until it fits the given space
'The function is inteded to work on text with a "guessed" wrap position that fit in the space allowed
'The function is recursive. Each time it guesses a new position and the word still doesn't fit in the space, it calls itself with a closer WrapPosition
'Parameters : - Text : The text to fit
' - FontName : Name of the font
' - FontSize : Size of the font
' - pxAvailW : Available width in pixels in wich we need to make the text fit
' - WrapPosition : The initial wrap position, positionned someware in the text (WrapPosition < len(Text)), but outside of pxAvailW
'Return : - The position were the text must be wraped to put as much words as possible in pxAvailW, but without getting outside of it. If no position can be found, returns 0
Dim NewWrapPosition As Long
NewWrapPosition = InStrRev(Text, " ", WrapPosition)
'If we didn't found white space, we are facing a "word" too long to fit pxAvailW, we leave and return 0
If NewWrapPosition = 0 Then Exit Function
If pxGetStringW(Left(Text, NewWrapPosition - 1), FontName, FontSize) >= pxAvailW Then '-1 not to take into account the last white space
'It still doesnt fits, we must try one less word
FindMaxPositionRev = FindMaxPositionRev(Text, FontName, FontSize, pxAvailW, NewWrapPosition - 1)
Else
'It fits, we return the position we found
FindMaxPositionRev = NewWrapPosition
End If
End Function
Known limitations
This code will work as long as the text in the cell has only one font and one font size. Here I assume that the font is not Bold nor Italic, but this can be easily handled by adding few parameters as the function measuring the string length in pixels is already able to do it.
I've made many test and I always got the same result than the autowrap function of Excel worksheet, but it may vary from one Excel version to an other. I assume it works on Excel 2010, and I tested it with success in 2013 and 2016. Fo others I don't know.
If you need to handle cases where fonts type and/or attributs vary inside a given cell, I assume it's possible to achieve it by testing the text in the cell character by character by using the range.caracters property. It should be really slower, but for now, even with texts to split in almost 200 lines, it takes less than one instant so maybe it's viable.
Just do Ctrl + Enter inside the text box
Related
How to change the color of text from a certain char to the end of the line
I have a WinForm application that reads a text file. It reads the whole file line by line and generates a RadioButton when there is a delimiter at the start of the line. So, "|A Topic" produces a RadioButton called A Topic. During runtime, a user chooses a RadioButton, the application finds that line in the text file then reads all the text until the next delimiter and puts it into a RichTextBox. The contents of the text file is actual Visual Basic code and I am looking for a way to delineate comments from the code. I am trying to use .Find() to locate all of the apostrophes which signify a comment. With a comment found, how can I expand the selection to the end of that line? Dim index As Integer = 0 While index < RichTxtOut.Text.LastIndexOf("'") RichTxtOut.Find("'", index, RichTxtOut.TextLength, RichTextBoxFinds.None) RichTxtOut.SelectionColor = Color.Green index = RichTxtOut.Text.IndexOf("'", index) + 1 End While This makes the apostrophe green, but how do I make the rest of the that line green. Here is an update code _________________________________________________ Dim indexx As Integer = 0 Dim lines() As String = System.IO.File.ReadAllLines(FILE_NAME) Dim numOfChars As Integer While indexx < RichTxtOut.Text.LastIndexOf("'") numOfChars = 0 RichTxtOut.Find("'", indexx, RichTxtOut.TextLength, RichTextBoxFinds.None) 'count the number of characters after the apostrophe For li As Integer = 0 To Lines(RichTxtOut.GetLineFromCharIndex(RichTxtOut.Find("'", indexx, RichTxtOut.TextLength, RichTextBoxFinds.None))).Count - 1 numOfChars += 1 Next RichTxtOut.Select(RichTxtOut.Find("'", indexx, RichTxtOut.TextLength, RichTextBoxFinds.None), numOfChars) RichTxtOut.SelectionColor = Color.Green numOfChars = 0 indexx = RichTxtOut.Text.IndexOf("'", indexx) + 1 End While However It is still not working correctly. It is not finding the correct number of characters to in the line after the apostrophe. Any suggestions??
RichTextBoxes can be complicated, as they are quite powerful and have lots of ways to do things. There's an example here that sets colours by adding words one at a time, and setting the selection colour for each one (presumably making use of the fact that the most recently added word is selected). You could add your text one line at a time, and set the colour to green if the first (non-space) character is an apostrophe. Using the selection is a bit of a hack though. The WPF RTB uses Paragraph and InLine objects, similar to DIVs and SPANs in HTML. You can set a font for each Inline. I'm not sure if the winforms one is the same.
Find and highlight word in Outlook email
How to find text (in this case -1.00 (some negative number) or +1.50 (some positive number) in the email body (compose part) and change font color of these words.
Even when you reduce your question to just the editing, I am confused. Do you really want to font the number but not the sign? The result looks very strange to me. I have created a subroutine that does nothing but the editing. You can either put code around it to achieve your objective or you can pick ideas from my subroutine and incorporate them in your own routine. The parameters of this subroutine are: Prefix and Text You could have Prefix = “+” and Text = “1.50” or Prefix = “” and Text = “+1.50”. With the first, only the number is formatted; with the second the sign is formatted as well. The length of these strings can be anything not just the 1 and 4 you request. StartFormat and EndFormat There are a variety of possible techniques for formatting bits of Html text but I cannot think of one that does not involve placing a start string before the text and an end string after the text. For my testing I created a html document with some black, some red and some blue text. I them placed “+1.50”s in each section. The values I passed for Start Format and EndFormat were "<span style=""color:#00FF00"">" and "</span>". The calls of EditHtml set all the “+1.50”s or all the “1.50”s green. Html This is the Html document to be amended. It is passed “By Reference” so the original string is updated. I hope this helps. Sub EditHtml(ByVal Prefix As String, ByVal Text As String, _ ByVal StartFormat As String, ByVal EndFormat As String, _ ByRef Html As String) Dim Pos As Long Pos = 1 ' Loop for each occurrence of Prefix & Text. Do While True Pos = InStr(Pos, Html, Prefix & Text) If Pos = 0 Then Exit Do End If Pos = Pos + Len(Prefix) ' Step over prefix ' Insert string that starts format change Html = Mid(Html, 1, Pos - 1) & StartFormat & Mid(Html, Pos) Pos = Pos + Len(StartFormat) ' Step over string that starts format change Pos = Pos + Len(Text) ' Step over text whose format is to be changed ' Insert string that ends format change Html = Mid(Html, 1, Pos - 1) & EndFormat & Mid(Html, Pos) Pos = Pos + Len(EndFormat) ' Step over string that ends format change Loop End Sub
VBA Find function incorrect highlighting
Can somebody help me fix this code, I have two textbox where when you paste the text on the 1st Textbox and click the search button, it highlighted the exact text on the 2nd textbox if it is present on the 2nd textbox. But when the string on 2nd textbox contains linefeed/newline, it added one character from the start of the text. Here is the code: Private Sub FindText(ByVal start_at As Integer) Dim pos As Integer Dim target As String target = Textbox1.Text pos = InStr(start_at, Textbox2.Text, target) If pos > 0 Then ' We found it. TargetPosition = pos Textbox2.SelStart = TargetPosition - 1 Textbox2.SelLength = Len(target) Textbox2.SetFocus Else ' We did not find it. MsgBox "Not found." Textbox2.SetFocus End If End Sub ' Search button Private Sub cmdSearch_Click() FindText 1 End Sub
I believe the problem is that Textbox is treating newline as a single character while Len() counts as CRLF as two. You should probably count the number of times CRLF appears in the text preceding the match target and adjust SelStart accordingly. Try this line instead: Textbox2.SelStart = Len(Replace(Left(target, pos - 1), vbCrLf, "^")) - 1 'Textbox2.SelLength = Len(Replace(target, vbCrLf, "^")) If target can include line breaks you may have a similar problem with SelLength which is why I've left the second, commented line. This works by substituting two-character line-break sequences into a single-character string. It's completely arbitrary what value to use for replacement since the result is discarded and only the length ultimately matters.
Sampling StringName.SubString(p,1) for paragraph formatting character
please try to specifically answer my question and not offer alternative approaches as I have a very specific problem that needs this ad-hoc solution. Thank you very much. Automatically my code opens Word through VB.NET, opens the document, finds the table, goes to a cell, moves that cells.range.text into a String variable and in a For loop compares character at position p to a String. I have tried Strings: "^p", "^013", "U+00B6" My code: Dim nextString As String 'For each cell, extract the cell's text. For p = 17 To word_Rng.Cells.Count nextString = word_Rng.Cells(p).Range.Text 'For each text, search for structure. For q = 0 To nextString.Length - 1 If (nextString.Substring(q, 1) = "U+00B6") Then Exit For End If Next Next Is the structural data lost when assigning the cells text to a String variable. I have searched for formatting marks like this in VBA successfully in the past.
Assuming that your string contains the character, you can use ChrW to create the appropriate character from the hex value, and check for that: If nextString.Substring(q, 1) = ChrW(&h00B6) Then Exit For End If UPDATE Here's a complete example: Dim nextString = "This is a test " & ChrW(&H00B6) & " for that char" Console.WriteLine(nextString) For q = 0 To nextString.Length - 1 If nextString(q) = ChrW(&H00B6) Then Console.WriteLine("Found it: {0}", q) End If Next This outputs: This is a test ¶ for that char Found it: 15
PDF to Text using PDFSharp
I wrote the following function to read the text out of a PDF file. It is pretty close, but I'm just not familiar enough with all the op codes to get the line spacing right. For example, I'm currently inserting a new line when I see "ET" but that doesn't seem quite right since it may just be the end of a text run, mid line. Could someone help me adjust the parsing? My goal is something similar to Adobe Reader's "Save as other" --> "Text" Public Function ReadPDFFile(filePath As String, Optional maxLength As Integer = 0) As String Dim sbContents As New StringBuilder Dim cArrayType As Type = GetType(CArray) Dim cCommentType As Type = GetType(CComment) Dim cIntegerType As Type = GetType(CInteger) Dim cNameType As Type = GetType(CName) Dim cNumberType As Type = GetType(CNumber) Dim cOperatorType As Type = GetType(COperator) Dim cRealType As Type = GetType(CReal) Dim cSequenceType As Type = GetType(CSequence) Dim cStringType As Type = GetType(CString) Dim opCodeNameType As Type = GetType(OpCodeName) Dim ReadObject As Action(Of CObject) = Sub(obj As CObject) Dim objType As Type = obj.GetType Select Case objType Case cArrayType Dim arrObj As CArray = DirectCast(obj, CArray) For Each member As CObject In arrObj ReadObject(member) Next Case cOperatorType Dim opObj As COperator = DirectCast(obj, COperator) Select Case System.Enum.GetName(opCodeNameType, opObj.OpCode.OpCodeName) Case "ET", "Tx" sbContents.Append(vbNewLine) Case "Tj", "TJ" For Each operand As CObject In opObj.Operands ReadObject(operand) Next Case "QuoteSingle", "QuoteDbl" sbContents.Append(vbNewLine) For Each operand As CObject In opObj.Operands ReadObject(operand) Next Case Else 'Do Nothing End Select Case cSequenceType Dim seqObj As CSequence = DirectCast(obj, CSequence) For Each member As CObject In seqObj ReadObject(member) Next Case cStringType sbContents.Append(DirectCast(obj, CString).Value) Case cCommentType, cIntegerType, cNameType, cNumberType, cRealType 'Do Nothing Case Else Throw New NotImplementedException(obj.GetType().AssemblyQualifiedName) End Select End Sub Using pd As PdfDocument = PdfReader.Open(filePath, PdfDocumentOpenMode.ReadOnly) For Each page As PdfPage In pd.Pages ReadObject(ContentReader.ReadContent(page)) If maxLength > 0 And sbContents.Length >= maxLength Then If sbContents.Length > maxLength Then sbContents.Remove(maxLength - 1, sbContents.Length - maxLength) End If Exit For End If sbContents.Append(vbNewLine) Next End Using Return sbContents.ToString End Function
Your code is ignoring almost all operations which change the line. You do consider ' and " which most often imply a change of line but which in the wild are seldom used. Inside a text object (BT .. ET) you, therefore, should also look out for tx ty Td Move to the start of the next line, offset from the start of the current line by (tx, ty). tx ty TD Move to the start of the next line, offset from the start of the current line by (tx, ty). As a side effect, this operator shall set the leading parameter in the text state. a b c d e f Tm Set the text matrix, Tm, and the text line matrix, Tlm. T* Move to the start of the next line. To interpret ', " and T* correctly, you should also look out for leading TL Set the text leading, Tl, to leading. If you find multiple text objects (BT .. ET .. BT .. ET), the second one is not necessarily on a new line. You should look out for the special graphics state operators between them: a b c d e f cm Modify the current transformation matrix (CTM) by concatenating the specified matrix q Save the current graphics state Q Restore the graphics state Your code is ignoring all numeric arguments to the operations. You should not ignore them, especially: You should check the parameters of the operators listed above; e.g. while 0 -20 Td starts a new line 20 units down, 20 0 Td remains on the same line and merely starts drawing text 20 units right of the former line start. You should check the numeric elements of the array parameter of TJ as they may (or may not!) indicate space between two words. Your code is assuming the Value of CString instances to already contain Unicode encoded character data. This assumption in general is incorrect, the encoding used in PDF strings drawn in text drawing operations is ruled by the font. Thus, you furthermore should also look out for font size Tf Set the text font, Tf, to font and the text font size, Tfs, to size. font shall be the name of a font resource in the Font subdictionary of the current resource dictionary. For details you should first and foremost study the PDF specification ISO-32000-1, especially chapter 9 Text with a solid background from chapter 8 Graphics.