Hello,
Let's start by explaining what I'm trying to achieve. I basically want to bind a single RadRichTextBox to multiple different DataProviders.
In my specific situation one RtfDataProvider and one TxtDataProvider. So that the same text can be bound to two different properties.
Where one property is just plain text and the other has the applied format (rtf).
I just went ahead and just tried it, and to my surprise this actually worked perfectly fine. However when I tried to apply the same technique to a custom RadGridView.RowDetailsTemplate this quit working and only the format of the last defined DataProvider gets applied.
One could think that this isn't really that big of an issue if you just define the RtfDataProver last, because then the RadRichTextBox would always have the correct styles applied.
This train of though would be correct when you can assume that both properties will always contain the same values, which unfortunately is not the case in my specific scenario.
Here is what happened, my model (and corresponding database table) used to only have the property for plain text.
We've recently decided to add an extra property (or column in the database) to save text formatting (rtf).
The great thing about binding both of the properties to the different DataProviders was that even if the rtf-property was empty/null then the RadRichTextBox would still show the plain text and apply default formatting to it.
When using this technique in the RowDetailsTemplate then the following situations occurred:
- RtfDataProvider is defined last: when the rtf-property is empty the RadRichTextBox will always be empty even when the plain text-property has a value.
- TxtDataProvider is defined last: the RadRichTextBox will always show some text but won't apply any formatting to it even when there is some defined in the rtf-property.
To explain my scenario better I'll add some code to helpfully make things easier to understand. (This isn't my actual code, just a simpler version to get help sketch an idea)
1. My basic model that represents the table in our database
public
class
MyTaskObject : INotifyPropertyChanged
{
private
long
_taskId;
public
long
TaskId
{
get
{
return
_taskId; }
set
{ SetProperty(
ref
_taskId, value); }
}
private
string
_title;
public
string
Title
{
get
{
return
_title; }
set
{ SetProperty(
ref
_title, value); }
}
private
string
_text;
public
string
Text
{
get
{
return
_text; }
set
{ SetProperty(
ref
_text, value); }
}
private
string
_rtfText;
public
string
RtfText
{
get
{
return
_rtfText; }
set
{ SetProperty(
ref
_rtfText, value); }
}
#region Observable
public
event
PropertyChangedEventHandler PropertyChanged;
protected
void
OnPropertyChanged([CallerMemberName]
string
propName =
null
)
{
var handler = PropertyChanged;
if
(handler !=
null
)
{
handler(
this
,
new
PropertyChangedEventArgs(propName));
}
}
protected
bool
SetProperty<T>(
ref
T storage, T value, [CallerMemberName]
string
propertyName =
null
)
{
if
(Equals(storage, value))
return
false
;
storage = value;
OnPropertyChanged(propertyName);
return
true
;
}
#endregion
}
2. The first scenario where binding the 2 properties to different DataProviders and a single RadRichTextBox works perfectly as desired.
2.1 This is the Xaml of the view: it's a simple user control that allows the user to search a the database for a specific "task", load it and edit before saving it.
So imagine there to be a buttons to look for "tasks" and commit or discard the changes.
There are also some textboxes to edit the edit the "Title" property for example, followed by the following:
<
telerik:RtfDataProvider
x:Name
=
"TaskRtfProvider"
Grid.Row
=
"1"
Grid.Column
=
"0"
RichTextBox
=
"{Binding ElementName=TaskEditor}"
SetupDocument
=
"DataProviderBase_OnSetupDocument"
Rtf
=
"{Binding SelectedTask.RtfText, Mode=TwoWay, UpdateSourceTrigger=Explicit}"
/>
<
telerik:TxtDataProvider
x:Name
=
"TaskTxtProvider"
Grid.Row
=
"1"
Grid.Column
=
"0"
RichTextBox
=
"{Binding ElementName=TaskEditor}"
SetupDocument
=
"DataProviderBase_OnSetupDocument"
Text
=
"{Binding SelectedTask.Text, Mode=TwoWay, UpdateSourceTrigger=Explicit}"
/>
<
telerik:RadRichTextBox
Grid.Row
=
"1"
Grid.Column
=
"0"
x:Name
=
"TaskEditor"
Margin
=
"5,0"
IsImageMiniToolBarEnabled
=
"True"
IsSpellCheckingEnabled
=
"False"
Padding
=
"4,2"
FontSize
=
"11pt"
FontFamily
=
"Calibri"
LayoutMode
=
"Flow"
DocumentInheritsDefaultStyleSettings
=
"True"
/>
The UpdateSourceTrigger is Explicit because I use changed tracking and only wish to update the database when the user explicitly says to be clicking a button.
Because if I don't do it this way, and the user loads a task where the "RtfText" property is empty, then immediately after loading this will have a value defined by the default formatting of the RadRichTextBox.
In this first scenario these bindings actually work as they're supposed to.
2.2 The implementation of the SetupDocument-event, this is used to apply some basic formatting (mainly to remove excessive spacing between lines)
private
void
DataProviderBase_OnSetupDocument(
object
sender, SetupDocumentEventArgs e)
{
//var test = sender;
if
(e.Document ==
null
)
return
;
e.Document.ParagraphDefaultSpacingAfter = 0;
}
While debugging I've noticed that the second defined DataProvider (in this example the "TaskTxtProvider") does always trigger this event, only when the first DataProvider is null does this event get triggered twice.
Which is exactly what makes this scenario work because this way the RadRichTextBox shows the formatted text if there is any, and else applies the default formatting to the plain text.
This is of course the reason for defining the RtfDataProvider ("TaskRtfProvider") first.
3. The second scenario is where this stops working, and where I tried to place a RadRichTextBox into the rows of a RadGridView.
In this scenario the RadRichTextBox is purely used for displaying the data, not editing.