|
This article was prompted by comments about about
Rich Parson's RichTextBoxExtended Control. This is a very useful
control that includes a tool bar to augment the existing RichText control
into a word processor.
The comments from users were very positive, but there was a desire to be able to
set the content of a rich text control via the Rtf property so it
can be persisted in the form design. Secondly for cases where the content is
being edited at runtime, it makes sense to be able to data bind to the Rtf
property. This article explains how to do both of these so you can build fully
functional Rich Text forms with a minimum of effort.
I made a few minor changes unconnected with persistance that I hope won't be controversial.
DefaultValue
attributes to all the properties so they don't show up bold unless changes.
ToolBarVisible, ReadOnly and BorderStyle
When working with the existing RichTextBox control, there are two
"features" that can catch you out. Firstly, as mentioned by Rich, if you
set Rtf before form initialization, the RichTextBox
control will sometimes loose its formatting and just display plain text.
Secondly, there is a bug in the Rtf property where it sometimes
returns a null character as the very last character in the string. This causes
no problems with designer form persistance when the RichTextBox control
has a short content. However once the content gets longer, the designer
serialization code has a nervous breakdown and messes up the job of breaking
the string into multiple lines. (Possibly this is why MS made the Rtf
property unavailable as standard in the Property editor in Visual
Studio.) This caused me more than a little bit of grief but it needn't
cause you any if you use the RichTextBoxExtended included with
this article.
In order for consumers of the Rtf
property to be aware of changes, there needs to be a property change event
called RtfChanged. This event will automatically be hooked to
handle data binding or form designer serialization.
We must ensure that this event is triggered whenever the content has been
updated. The obvious case is when the user has updated the content directly.
This we check in a Leave event handler. The Rtf property
may be set directly so we need to trigger our event here also. Finally Rtf
could change indirectly as a result of a change to the Text property.
[Category("Property Changed")]
public event EventHandler RtfChanged;
[
System.ComponentModel.Description("Contents in Rtf format"),
RecommendedAsConfigurable(true),
Category("Data"),
Bindable(true),
Editor(typeof(Design.RichTextBoxExtendedEditor),typeof(System.Drawing.Design.UITypeEditor))
]
public string Rtf
{
get
{ return rtb1.Rtf;
}
set
{ rtb1.Rtf= value;
if (RtfChanged!=null)
RtfChanged(this,EventArgs.Empty);
}
}
#endregion
private void rtb1_TextChanged(object sender, System.EventArgs e)
{
if (RtfChanged!=null)
RtfChanged(this,EventArgs.Empty);
}
private void rtb1_Leave(object sender, System.EventArgs e)
{
if (this.rtb1.Modified && RtfChanged!=null)
RtfChanged(this,EventArgs.Empty);
}
There's a pleasing recursion here, as the ExtendedRichText control
is used to edit its own Rtf property.
Implementing a property editor is accomplished by adding the Editor
attribute to the Rtf property. This attribute specifies which UITypeEditor
to invoke. The implementation of this UITypeEditor is fairly
simple as shown below:
class RichTextBoxExtendedRtfEditor:System.Drawing.Design.UITypeEditor
{
public override System.Drawing.Design.UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
if (context==null)
return base.GetEditStyle(null);
return System.Drawing.Design.UITypeEditorEditStyle.Modal;
}
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
if (context!=null && provider!=null)
{
IWindowsFormsEditorService edSrv= (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
if (edSrv!=null)
{
RichTextBoxExtendedRtfEditorForm dialog= new RichTextBoxExtendedRtfEditorForm();
if (value is String)
dialog.Value= (string)value;
if (edSrv.ShowDialog(dialog)==System.Windows.Forms.DialogResult.OK)
value= dialog.Value;
dialog.Dispose();
dialog= null;
}
}
return value;
}
}
Once persistance of Rtf has been implemented for designer
serialization, it will automatically work for data binding. The image of the
included demo application shows two RichTextExtended controls- one
is read only and is persisted in the form design. The other is editable and is
data bound and persisted in a DataSet at runtime. To keep the
sample simple to install this dataset is persisted in a text file as Xml. In a
real world application, persistence would probably be to a database and it
works fine this way. Just make sure your database columns are large enough as
the formatting information makes rich text bigger than you might expect.
A standalone solution (RichTextExtended.sln ) has been provided in
the demo project. If you use AgileStudio, a
solution has also been included (RichTextBoxExtended2.sln) that
implements automatic
binding. In fact I used this mechanism to knock together the demo app
really quickly.
This modified RichTextBoxExtended now has a property editor to
allow you to persist Rich Text as part of the form design. It also supports
data binding to allow the editing of Rich Text from a data source at runtime.
Now you can knock together rich text apps without having to hand code your Rtfassignments.
However, if you want to go one step further, the automatic binding in AgileStudio makes it really fly as this automatically builds the SqlServer database and builds the StoreProcs, typed datasets and bindings when you just drop the ExtendedRichText control on the form.