
The article assumes that you're familiar with Visual Basic .NET and the Visual
Studio .NET Windows Forms designer.
Introduction
A couple of weeks ago, I've had a busy weekend working on a soon-to-be-released
.NET component. The feature I was working on was a nag dialog that the
evaluation version of the component should display occasionally. Obviously, I
wanted the dialog to show up quickly and also to look attractively. I also
wanted to be able to display an active hyperlink that would take the user to the
product's site when clicked.
Another possible example needing a similar functionality might be an 'About'
dialog box. (I've seen a couple of 'About' dialogs containing just plain text,
even with non-clickable homepage and 'purchase' hyperlinks).
Splash screens could also have been more attractive displaying program
information (name, version number...) in distinct fonts and colors, for example.
Details
Or, consider the various 'TIP:' labels scattered throughout dialog boxes in some
applications. More often than not, decent formatting could have improved
readability and thus the usability of the dialog.
Let's go back to my nag dialog. As I said, I wanted to use formatted text and
graphics to make it look nice and "cool". I've considered the following possible
implementation options:
1. Several differently formatted Label
and LinkLabel
controls.
I've abandoned this option quickly, because the formatted text should appear as
"flowing" and I wasn't able to layout the differently formatted labels
correctly.
2. Third-party HTML rendering controls.
I've found a couple of ActiveX controls, but I didn't want to introduce any
additional dependencies (and the interop overhead), so I didn't research the
controls further.
3. Hosting the WebBrowser control.
This is an ActiveX control, so it suffers from the same drawbacks as the
previous approach. It's also too slow for the purposes of my nag dialog.
4. The RichTextBox
control.
The control supports text formatting and embedded graphics. It has also the
DetectUrls
property meaning it can underline properly formatted URLs, display
the 'hand' cursor when an user hovers the mouse over the link and it can also
raise the LinkClicked
event when the link is clicked. The event can be handled
with a small amount of code to show the URL in the user's browser.
In addition, by setting a couple of the control's appearance properties, it
could be made to look and act like a non-editable, formatted label. And the
control's display performance is really good.
For me, RichTextBox
was a clear winner.
After deciding to use the RichTextBox
, I've created an RTF file with the text
for my nag dialog (I've used the good ol' WordPad to do this). I've added the
RTF file to the component's project and marked it as an embedded resource:
I've added code to load the RTF text into the RichTextBox.Rtf
property from
inside the nag dialog Load
event handler, which was rather easy:
Dim Assm As [Assembly] = Me.GetType().Assembly
Dim RtfStream As System.IO.Stream = Assm.GetManifestResourceStream(
"NagDialog.NagDialogText.rtf")
Me.RichTextBox1.LoadFile(RtfStream, RichTextBoxStreamType.RichText)
RtfStream.Close()
The stream returned from the
Assembly.GetManifestResourceStream
is simply passed
to the
RichTextBox.LoadFile
method.
The Assembly.GetManifestResourceStream
method requires a resource name in the
form <Namespace>.<FileName>, where <Namespace> is the project's root namespace
and <FileName> is the actual file name (as it appears in the Solution Explorer).
Please bear in mind that the resource name is case sensitive.
I wanted the nag dialog to display the same product information that was already
embedded into the assembly's manifest as attributes (i.e.
AssemblyProductAttribute
, AssemblyCompanyAttribute
, etc.). In order to do that,
I've used substitution strings inside the RTF text. After loading the text, I've
replaced the substitution strings with the actual attribute values, for example:
Dim Attrs() As Object = Assm.GetCustomAttributes(
GetType(AssemblyTitleAttribute), False)
If (Not Attrs Is Nothing) AndAlso (Attrs.Length > 0) Then
Me.RichTextBox1.Rtf = Me.RichTextBox1.Rtf.Replace("[0]",
DirectCast(Attrs(0), AssemblyTitleAttribute).Title)
End If
My first attempt was to use substitution strings with curly braces (e.g. "{0}"),
suitable for passing to the
String.Format
method. However, I've quickly realized
that this approach won't work with RTF-formatted text, because curly braces have
special meaning in RTF (they're are used to enclose RTF groups). The only other
"forbidden" character is the backslash "\" character, so anything else can be
used.
The other thing to watch out when using string substitution in RTF is to make
sure that the whole substitution string doesn't contain different formatting.
Otherwise the string will get escaped in the RTF text and you won't be able to
replace it. For example, if you enter "(0)" as a substitution string with the
closing brace formatted as bold, you'll get "(0\b )" as a result.
Let's recap quickly what you have to do in order to use RTF in a Form
using the
described technique:
- Write the RTF text in your RTF editor of choice (WordPad works quite well;
Word adds too much overhead). Don't forget to add substitution strings for the
product information that will be loaded from the assembly.
- Add the RTF file to your project and mark it as embedded resource (set the
Build Action property of the file to 'Embedded Resource').
- Place a
RichTextBox
control on your dialog and set its properties that will
make it look like a label (colors, TabStop
, etc.).
- Write code to load the RTF into the
RichTextBox
control and to do the
substitutions along the way.
It's quite boring, error prone work, so I've encapsulated the technique into an
easy-to-use control - the RichTextLabel
control:
The control is intended for reuse in source form. The
accompanying solution
contains both, the VB.NET version as well as the C# version of the control.
The RichTextLabel
control exposes just one, all important RtfResourceName
property. You'll need to set the property to the name of the RTF text resource
you wish to display in the control. Everything else is done automatically.
In order to use the RichTextLabel
control within your project, you'll have to
follow these steps:
- Add the
RichTextLabel.vb
or RichTextLabel.cs
source file to you project and
recompile. The RichTextLabel
control is derived from UserControl
, so it will be added to the
Visual Studio .NET toolbox automatically.
- Add an RTF file to your project and set its build action to 'Embedded
Resource'. You can have the following substitution strings in the file:
Substitution String |
Replaced by this property's value |
[Title] | AssemblyTitleAttribute.Title |
[Product] | AssemblyProductAttribute.Product |
[Copyright] | AssemblyCopyrightAttribute.Copyright |
[Company] | AssemblyCompanyAttribute.Company |
- Place the
RichTextLabel
control onto a Form
and set its
RtfResourceName
property to the name of the RTF file you've added in step 2.
In order to make things easier for you, the RtfResourceName
property has an
associated type editor (RtfResourceNameEditor
), which displays a popup list of
all the RTF resource files available in the current assembly (see the image at the top of the article).
In order to keep the RichTextLabel
control as small as possible, I've put the
type editor in a separate LaMarvin.Windows.Forms.RichTextLabel.Design.dll
assembly that needn't be directly referenced by the project containing the
RichTextLabel
control. If you want to take advantage of the richer design-time
support, just copy the LaMarvin.Windows.Forms.RichTextLabel.Design.dll
assembly
to your project's output directory.
If you'd like to edit the RTF files added to your project comfortably from
within the Visual Studio .NET IDE, you might want to associate the .RTF file
extension with WordPad. Right-click on any RTF file in the Solution Explorer and
select the "Open With..." command. In the Open With dialog box, add WordPad.exe
to the list of available programs and set it as the default editor for .RTF
files. After that, double-clicking an RTF file in the Solution Explorer will
open the file in WordPad automatically.
History
-
Saturday, March 20, 2004 -
Published.