7 Answers, 1 is accepted
In Silverlight, normally it is only possible to copy and paste rich text content between RadRichTextBoxes in the same application. In WPF, you can also paste rich text copied from a browser, Microsoft Word, etc.
In order to disable pasting of rich text content in both cases, you can handle the CommandExecuting event of RadRichTextBox. If the command being executed is PasteCommand, you can cancel it and insert a stripped version of the content of the clipboard like this:
private
void
rrtb_CommandExecuting(
object
sender, Telerik.Windows.Documents.RichTextBoxCommands.CommandExecutingEventArgs e)
{
if
(e.Command
is
PasteCommand)
{
DocumentFragment fragment = ClipboardEx.GetDocument();
if
(fragment !=
null
)
{
RadDocument document = fragment.ToDocument();
string
toBePasted =
new
TxtFormatProvider().Export(document);
(sender
as
RadRichTextBox).Insert(toBePasted);
e.Cancel =
true
;
}
}
}
The DocumentFragment that ClipboardEx.GetDocument() returns may represent a rich text document. In order to strip the rich content, such as tables, images, hyperlinks, etc., we export it it to a string using the plain-text format provider. At the end, we insert the resulting plain text string and cancel the paste.
I hope this helps.
Iva Toteva
the Telerik team
Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>
Any ideas?
Michael
The following code will do the custom paste you want. It iterates through the document elements and removes the mentioned types you don't want to include. You only need to attach it to the rich text box.
private
void
editor_CommandExecuting(
object
sender, Telerik.Windows.Documents.RichTextBoxCommands.CommandExecutingEventArgs e)
{
if
(e.Command
is
PasteCommand)
{
DocumentFragment fragment = ClipboardEx.GetDocument();
if
(fragment !=
null
)
{
RadDocument document = fragment.ToDocument();
foreach
(Block block
in
document.EnumerateChildrenOfType<Block>().ToList<Block>())
{
if
(block
is
Paragraph)
{
Paragraph para = block
as
Paragraph;
foreach
(var item
in
para.Inlines)
{
if
(item
is
ImageInline || item
is
FloatingImageBlock)
{
para.Inlines.Remove(item);
}
}
}
else
if
(block
is
Table)
{
block.Parent.Children.Remove(block);
}
}
DocumentFragment newFragment =
new
DocumentFragment(document);
(sender
as
RadRichTextBox).InsertFragment(newFragment);
e.Cancel =
true
;
}
}
}
However, the can execute behavior of paste command cannot easily be simulated because .net doesn't have access to events of the clipboard which can copy items out of the application. Here's an article about doing this with win32 functions in C#: http://www.radsoftware.com.au/articles/clipboardmonitor.aspx
Hope this solves your issue.
Martin
the Telerik team
Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>
IE, if I copy "word" and paste it into "a sent^ance" where the carrot is, I get
"a sentword
ance"
When creating a DocumentFragment out of a document, due to the fact that the document ends with a paragraph, the fragment will also end in a new paragraph.
What you can do is to add the following line just before cancelling the execution of the command:
(sender
as
RadRichTextBox).Delete(
true
);
It will delete the additional paragraph. However, on undo, the two actions will be undone separately - first, the deleted additional paragraph will be added, then the pasted text will be removed.
What I would suggest you do is to check if there is unallowed content and only then cancel the PasteCommand and insert the fragment through the API, like this:
void
editor_CommandExecuting(
object
sender, Telerik.Windows.Documents.RichTextBoxCommands.CommandExecutingEventArgs e)
{
bool
hasChangeOccured =
false
;
if
(e.Command
is
PasteCommand)
{
DocumentFragment fragment = ClipboardEx.GetDocument();
if
(fragment !=
null
)
{
RadDocument document = fragment.ToDocument();
foreach
(Block block
in
document.EnumerateChildrenOfType<Block>().ToList<Block>())
{
if
(block
is
Paragraph)
{
Paragraph para = block
as
Paragraph;
foreach
(var item
in
para.Inlines)
{
if
(item
is
ImageInline || item
is
FloatingImageBlock)
{
para.Inlines.Remove(item);
hasChangeOccured =
true
;
}
}
}
else
if
(block
is
Table)
{
block.Parent.Children.Remove(block);
hasChangeOccured =
true
;
}
}
if
(hasChangeOccured)
{
DocumentFragment newFragment =
new
DocumentFragment(document);
this
.editor.InsertFragment(newFragment);
this
.editor.Delete(
true
);
e.Cancel =
true
;
}
}
}
}
In this way, in the most common paste within the document, the undo will work correctly. When the user pastes a document that contains images and tables, the action will be undone in two steps.
I hope you find this solution acceptable. Regards,
Iva Toteva
the Telerik team
Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>
I did the following:
private void OnCommandExecuting(object sender, CommandExecutingEventArgs e)
{
if (e.Command is PasteCommand)
{
var fragment = ClipboardEx.GetDocument();
if (fragment != null)
{
var document = fragment.ToDocument();
foreach (Block block in document.EnumerateChildrenOfType<
Block
>().ToList<
Block
>())
{
if (block is Paragraph)
{
var para = block as Paragraph;
foreach (var item in para.Inlines)
{
if (item is ImageInline || item is FloatingImageBlock || item is AnnotationMarkerBase)
{
para.Inlines.Remove(item);
}
else if (item is Span)
{
var span = item as Span;
span.ForeColor = Colors.Black;
span.FontFamily = FontFamily;
span.FontSize = FontSize;
}
}
}
else if (block is Table)
{
block.Parent.Children.Remove(block);
}
}
var selection = new DocumentSelection(document);
selection.SelectAll();
this.editor.InsertFragment(new DocumentFragment(selection));
e.Cancel = true;
}
}
}
I don't seem to have the same problem when I add it as a selection, and undo is working fine. However, not knowing the code base, I would like to know if there are any other potential fall backs that you are aware of.
Thanks,
Mike
Your code should behave correctly. Our only concern is that you have performance issues on large documents using the EnumerateChildrenOfType() method which has the following implementation
public
IEnumerable<T> EnumerateChildrenOfType<T>() where T : DocumentElement
{
if
(
this
is
T)
{
yield
return
(T)
this
;
}
if
(CanHaveChildOfType(
this
.GetType(),
typeof
(T)))
{
foreach
(DocumentElement child
in
this
.Children)
{
foreach
(T subChild
in
child.EnumerateChildrenOfType<T>())
{
yield
return
subChild;
}
}
}
}
Currently we cannot think of better solution for your case.
You should test this for your scenario and see if it is acceptable.
If you have any further questions, do not hesitate to contact us.
All the best,
Martin
the Telerik team
Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>