Link to home
Start Free TrialLog in
Avatar of Clif
ClifFlag for United States of America

asked on

RichTextBox - Setting Font and Copying RTF Not Working

I have an application that contains a RTB where I allow the user (through a button) the ability to bold selected text.

The code to bold is this:
Private Sub btnBold_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnBold.Click
    rtbComments.SelectionFont = New Font(rtbComments.SelectionFont.FontFamily, rtbComments.Font.Size, rtbComments.SelectionFont.Style Xor FontStyle.Bold)
End Sub

Open in new window


Later on in the code, I pass the RTF value in the RTB to an instance of an RTB in a class like this:
Private m_rtbTarget As New RichTextBox
Public WriteOnly Property rtbTarget() As RichTextBox Implements IReport.rtbTarget
    Set(ByVal value As RichTextBox)
        m_rtbTarget.Rtf = value.Rtf
    End Set
End Property

Open in new window


The problem is the bolded font isn't being passed.

If I paste text in the original RTB from a Word document, and it has bolded text, the bolding will be pasted as well.  Then, when the rtf is passed, the bolding does get passed as well, only the bolding that gets set with the button doesn't get passed.

What gives?
Avatar of Cimperiali
Cimperiali
Flag of Italy image

Private m_rtbTarget As New RichTextBox
Public WriteOnly Property rtbTarget() As RichTextBox Implements IReport.rtbTarget
    Set(ByVal value As RichTextBox)
        m_rtbTarget.Rtf = value.Rtf
    End Set
End Property
you're assigning the rtf to a  New RichTextBox, wihch probably is not the one user sees. You then reassign it back to the RichTextBox you display?
Avatar of Clif

ASKER

I'm assigniong the rtf of the visible RTB to an RTB object in a class so that I can write out the text (with formatting) to a Word document.  The value goes one way, it never gets set back to the RTB on the form.

The purpose of the class is to contain code for reuse.
beg your pardon if I ask this, but do you keep your class alive avoiding to have a new m_rtbTarget each time? To test for sure, change the declaration
(Private m_rtbTarget As New RichTextBox) in Private m_rtbTarget As  RichTextBox and initialize it in a method that requires explicit call (this only for testing purpose, to be sure you retain the same object through the lifetime of your "reused code"...
Avatar of Clif

ASKER

No, the class is disposed of when the document is complete to allow for the possibility of another class to be created.  I have (currently) three classes, one of which will be instantiated depending upon choices made by the user.  Each class has it's own RTB object.

I'm not entirely sure I understand what you're asking me to do.  Can you fomat it a little better?

Ok, point is (I am quite sure you already know this, I am telling you only if it might be you forgot to ensure it) : if you lose data you might  have

instantiate the class
you assign values to a new object
...
you assign values to a new object (this is not the same of before - you losed values assigned to previous object)

so, as you have a Private m_rtbTarget As New RichTextBox, you might not be aware you are making a new m_rtbTarget  somewhere else in your code, thus losing values assigned to a previous instance. To ensure this is not happening, for testing purpose you could make a routine to "manual" instantiate the m_rtbTarget , so you have to call it to make a "New  RichTextBox" and you can see debugging if you're stepping in there more than once in between.
 
Avatar of Clif

ASKER

I've been doing some investigating and have narrowed it down to a couple of lines of code.  Here is the total code black that references the RTB instance:
'Point 1
m_rtbTarget.SelectAll()
m_rtbTarget.SelectionFont = New Font("Arial Narrow", 11)

'Point 2
m_rtbTarget.Rtf = m_rtbTarget.Rtf.Replace("<<CUSTOMERNAME>>", m_strCustomer.CustomerName)
m_rtbTarget.Rtf = m_rtbTarget.Rtf.Replace("<<ISSUENAME>>", sIssueNameList)
m_rtbTarget.Rtf = m_rtbTarget.Rtf.Replace("<<STANDARD>>", m_strStandards.Number)
Application.DoEvents()

'Point 3
If m_rtbTarget.Find("Recommendations") >= 0 Then
  m_rtbTarget.Select(m_rtbTarget.Find("Recommendations"), Len("Recommendations"))
  m_rtbTarget.SelectionFont = New Font(m_rtbTarget.SelectionFont, FontStyle.Bold)
End If
If m_rtbTarget.Find("Results") >= 0 Then
  m_rtbTarget.Select(m_rtbTarget.Find("Results"), Len("Results"))
  m_rtbTarget.SelectionFont = New Font(m_rtbTarget.SelectionFont, FontStyle.Bold)
End If
If m_rtbTarget.Find(m_strStandards.Number) >= 0 Then
  m_rtbTarget.Select(m_rtbTarget.Find(m_strStandards.Number), Len(m_strStandards.Number))
  m_rtbTarget.SelectionFont = New Font(m_rtbTarget.SelectionFont, FontStyle.Italic)
End If

'Point 4
Dim oClipboard As Object = Clipboard.GetDataObject()
If m_rtbTarget.Text.EndsWith(vbLf) Then
  sText = vbCrLf
Else
  sText = vbCrLf & vbCrLf 'Add an extra one to keep the spacing correct.
End If
m_rtbTarget.SelectAll()
m_rtbTarget.Copy()
m_oWord.Selection.Paste()
Try
  Clipboard.SetDataObject(oClipboard)
Catch ex As Exception
  'Do nothing. Just prevent the app from stopping.
End Try

Open in new window


The problem appears to be at Point 1.  Irrespective of what's bold and not, I need the text to be 11 pt "Arial Narrow".  Setting this, however, causes all formatted font to be reset back to standard (not bold/italic).  How do I change the font name/size only and keep whatever may be bolded/italics?
Avatar of Nasir Razzaq
You can not do that at the end because you are RESETTING the font to same font without bold/italics.
There's no way to change the font for all text at once and keep any existing bold formatting.

You have to loop thru, character by character, select it, and then set the font based on whether the current selection is bolded or not.
Avatar of Clif

ASKER

I ain't buying it.

You're telling me that, if I were to write a WP using the RTF control, if someone created a ten page document, fully formatted, and then wanted to change the font for the entire document from Times New Roman to Arial, my code would have to go through character by character for each character of those ten pages and look at the bold/italic and then individually set the font name and include the bolding/italic?

That just ain't right, and I refuse to believe that's my only option.
Instead of

m_rtbTarget.SelectAll()
m_rtbTarget.SelectionFont = New Font("Arial Narrow", 11)

to

m_rtbTarget.Font = New Font("Arial Narrow", 11)
Avatar of Clif

ASKER

No Joy.  Same effect.
No, I do believe that will not work: you substitute the font in any case. You spotted it correctly before: to preserve  formatting, the formatting must be reapplyed, thus it must be made for each single char in (rtf) text when the formatting changes
that is: loop through chars and select them while the fontStyle  is the same.
Then, when you spot it changes, apply the new family and the new size and the "old" style from first char to the one before it changes, then set the subsequent char as the new starting point till the end of selection

SOLUTION
Avatar of Mike Tomlinson
Mike Tomlinson
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
indeed you could try pasing directly rtf.
an example is here: http://www.codeproject.com/KB/cs/RTFSyntaxColour.aspx
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
for example, I wrote in a rich text box "Hello, man how are you" and set only Hello as bold and the wole text as Courier new
then I grabbed the rtf in a string variable :
{\rtf1\ansi\deff0{\fonttbl{\f0\fnil\fcharset0 Courier New;}{\f1\fnil\fcharset0 Microsoft  San serif;}}
\viewkind4\uc1\pard\lang1040\b\fs25 hello\b0\f1\fs20 , man how are you\par
}
then substitute (replace)  Courier New with Arial and
fs + number (ie fs25) with fs+ number+5 (ie: fs30)
then reassigned the string to the RTF property of the richTextBox
give it a try.
Idle Mind, you got it before me. I do believe this is the correct direction for the quester
Avatar of Clif

ASKER

Ok, let's try that direction (looping through one character at a time isn't quite working yet).

My problem with the solution of modifying the rtf directly is how to find every possible font name to replace with the correct one?  Then there is the same issue with the font size (including partial sizes like 11.5)

I'm sure the answer would have something to do with Regular Expressions, but I was sick the day they talked about that back in college.
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Regular Expressions of course, would be nice. But me too was sick that day, so I do RegEx only when really I cannot avoid. A tip: search for "Expresso Regular Expression" : it is a free tool that could help you write RegEx. ...But I was sick...
Avatar of Clif

ASKER

Your example:
{\rtf1\ansi\deff0{\fonttbl{\f0\fnil\fcharset0 Courier New;}{\f1\fnil\fcharset0 Microsoft  San serif;}}
\viewkind4\uc1\pard\lang1040\b\fs25 hello\b0\f1\fs20 , man how are you\par
}

I need to convert *all* font names to "Arial Narrow", so I would need to replace both "Courier New" and "Microsoft  San serif" with "Arial Narrow".

I also need to convert all sizes to 11, so I would ned to replace fs25 and fs20 to fs11.

Thanks for the tool.  One of these months, when I have nothing to do, I might learn how to use it.  :)  It looks like you need to know regular expressions first, though.
Did you explore my idea? http:#36357847
Avatar of Clif

ASKER

No, I didn't.  I'm not entirely sure how to do what you suggest.
Avatar of Clif

ASKER

Ok, I took what C did and expanded on it till I got to here:
Function fixRTF(ByVal RTF As String) As String
    Dim sRetVal As String = RTF
    Dim nPtr1 As Integer = -1
    Dim nPtr2 As Integer = 0
    Dim arrReplace() As String
    Dim nArrCount As Integer = 0

    ReDim arrReplace(nArrCount)

    nPtr1 = sRetVal.IndexOf("fcharset")
    Do Until nPtr1 = -1
        nPtr1 = sRetVal.IndexOf("fcharset", nPtr1)
        If nPtr1 > -1 Then
            nPtr1 = sRetVal.IndexOf(" ", nPtr1)
            If nPtr1 > -1 Then
                nPtr1 += 1
                nPtr2 = sRetVal.IndexOf(";", nPtr1)
                If nPtr2 > -1 Then
                    nArrCount += 1
                    ReDim Preserve arrReplace(nArrCount)
                    arrReplace(nArrCount) = sRetVal.Substring(nPtr1, nPtr2 - nPtr1)
                End If
            End If
        End If
    Loop
    For i As Integer = 1 To UBound(arrReplace)
        sRetVal = sRetVal.Replace(arrReplace(i), "Arial Narrow")
    Next

    nArrCount = 0
    ReDim arrReplace(nArrCount)
    nPtr1 = sRetVal.IndexOf("fs")
    Do Until nPtr1 = -1
        nPtr1 = sRetVal.IndexOf("fs", nPtr1)
        If nPtr1 > -1 Then
            nPtr1 += 2
            nPtr2 = sRetVal.IndexOf(" ", nPtr1)
            If nPtr2 > -1 Then
                nArrCount += 1
                ReDim Preserve arrReplace(nArrCount)
                arrReplace(nArrCount) = sRetVal.Substring(nPtr1, nPtr2 - nPtr1)
            End If
        End If
    Loop
    For i As Integer = 1 To UBound(arrReplace)
        sRetVal = sRetVal.Replace("fs" & arrReplace(i), "fs22")
    Next

    Return sRetVal
End Function

Open in new window


It works well.  The only thing that concerns me is that, to get the correct Font Size, instead of using fs11, I had to double it so that it's fs22.

Does that sound right?  Is the fs number half of the actual font size?
Avatar of Clif

ASKER

Well, no further comments, so I guess I'd better close this out before the weekend.

Thanks for the help.
Let me say I do believe Idle Mind deserved points, as he was the  first to mention Rtf, and of course, CodeCruiser showed a good more common alternative.
About Sans Serif, that should be the default of richText, it was here from the start.
About size, seems as if  you're right: look here  (and search for \fs) http://www.pindari.com/rtf1.html