This is a migrated thread and some comments may be shown as answers.

Limiting the RadRichTextBox

7 Answers 228 Views
RichTextBox
This is a migrated thread and some comments may be shown as answers.
Mike
Top achievements
Rank 1
Mike asked on 06 Jan 2012, 10:15 PM
We're working on an RtfEditor that allows the user to cut, copy, paste, bold, italic, underline, find, and spellcheck that works in both Silverlight and WPF.  We've tried using the RadRichTextBox, which seems to work fine, however, we don't want to allow Images, Tables, or changed font or font color.  We simply do not add these buttons, and remove these options from the context menu, but when someone pastes into our RtfEditor, they can still paste Images, Tables, different fonts and colors, and hyperlinks.  Can we prevent this in the RadRichTextBox?

7 Answers, 1 is accepted

Sort by
0
Iva Toteva
Telerik team
answered on 11 Jan 2012, 06:00 PM
Hello Michael,

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.

Greetings,
Iva Toteva
the Telerik team

Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>

0
Mike
Top achievements
Rank 1
answered on 12 Jan 2012, 09:57 PM
This gives me access to the PasteCommand when it is excecuting, which is good.  However, I do want to allow bold, italics, and underline, which this method will remove.  Also, I want to be able to disable the PasteCommand if something invalid is on the clipboard, thus disabling the button in the Ribbon Bar.  Currently, with this method, If I copy an image, the paste button will be displayed as enabled, even though there is nothing for me to paste.
Any ideas?
Michael
0
Martin Ivanov
Telerik team
answered on 17 Jan 2012, 06:28 PM
Hi 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.

Greetings,
Martin
the Telerik team

Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>

0
Mike
Top achievements
Rank 1
answered on 30 Jan 2012, 05:11 PM
That works for restricting images and tables, so only what I want to allow is allowed, however, it pastes each item as a new paragraph, leaving a newline after each paste.

IE, if I copy "word" and paste it into "a sent^ance" where the carrot is, I get
"a sentword
ance"

0
Iva Toteva
Telerik team
answered on 02 Feb 2012, 06:53 PM
Hi Michael,

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 >>

0
Mike
Top achievements
Rank 1
answered on 02 Feb 2012, 07:26 PM
Thanks for the reply, however, that won't quite work for me.  You see, I also need to limit the color, fontfamily, and font size of anything pasted in, so I will effectively be changing it everytime.  However, I have found a solution that appears to be working.

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
0
Martin Ivanov
Telerik team
answered on 03 Feb 2012, 07:00 PM
Hi Michael,
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 >>

Tags
RichTextBox
Asked by
Mike
Top achievements
Rank 1
Answers by
Iva Toteva
Telerik team
Mike
Top achievements
Rank 1
Martin Ivanov
Telerik team
Share this question
or