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

Best practice to implement Shift+F3 behavior (change word case like MSWord does)

7 Answers 136 Views
RichTextBox
This is a migrated thread and some comments may be shown as answers.
Grigory
Top achievements
Rank 1
Grigory asked on 25 Jul 2014, 10:23 AM
I need to implement "ChangeCase" keyboard shortcut (like Shift+F3 in MS Word) for RadRichTextBox, which changes the text either selected by mouse, or the last word before caret's position. So i can't find best way how to do this. May be i should use the way with selections like:

DocumentPosition pos = new DocumentPosition(this.radRichTextBox.Document.CaretPosition);
  
pos.MoveToCurrentWordStart();
this.radRichTextBox.Document.Selection.SetSelectionStart(pos);
  
pos.MoveToCurrentWordEnd();
this.radRichTextBox.Document.Selection.AddSelectionEnd(pos);

And then work with selected word. Or just replace word in position. 

The result should be (3 consecutive Shift+F3):
word --(Shift+F3)-> Word --(Shift+F3)-> WORD --(Shift+F3)-> word

7 Answers, 1 is accepted

Sort by
0
Grigory
Top achievements
Rank 1
answered on 25 Jul 2014, 11:56 AM
And  i have some questions about entry point for this behavior. Should i use RadRichTextBox.RegisterCommand or just handle OnPreviewKeyDown
0
Boby
Telerik team
answered on 30 Jul 2014, 11:58 AM
Hello Grigoy,
You could use both of the approaches - registering custom command or handling PreviewEditorKeyDown event, for example:
public partial class MainDemoControl : UserControl
{
    public static RoutedCommand ToggleCaseRoutedCommand = new RoutedCommand();
 
    public MainDemoControl()
    {
        RadRichTextBox.RegisterCommand(MainDemoControl.ToggleCaseRoutedCommand, Key.F3, ModifierKeys.Shift);
 
        InitializeComponent();
 
        this.radRichTextBox.CommandBindings.Add(new CommandBinding(MainDemoControl.ToggleCaseRoutedCommand, Executed, CanExecute));
    }
 
    private void Executed(object sender, ExecutedRoutedEventArgs e)
    {
        this.radRichTextBox.BeginUndoGroup();
 
        if (this.radRichTextBox.Document.Selection.IsEmpty)
        {
            DocumentPosition pos = new DocumentPosition(this.radRichTextBox.Document.CaretPosition);
 
            pos.MoveToCurrentWordStart();
            this.radRichTextBox.Document.Selection.SetSelectionStart(pos);
 
            pos.MoveToCurrentWordEnd();
            this.radRichTextBox.Document.Selection.AddSelectionEnd(pos);
        }
        else if (this.radRichTextBox.Document.Selection.Ranges.Count > 1)
        {
            // If multirange selection is present, use only the first range.
            DocumentPosition start = new DocumentPosition(this.radRichTextBox.Document.Selection.Ranges.First.StartPosition);
            DocumentPosition end = new DocumentPosition(this.radRichTextBox.Document.Selection.Ranges.First.EndPosition);
 
            this.radRichTextBox.Document.Selection.SetSelectionStart(start);
            this.radRichTextBox.Document.Selection.AddSelectionEnd(end);
        }
 
        string selectedText = this.radRichTextBox.Document.Selection.GetSelectedText();
 
        this.radRichTextBox.Insert(selectedText.ToUpper());
 
        this.radRichTextBox.EndUndoGroup();
    }
 
    private void CanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
        e.CanExecute = true;
    }

Don't hesitate to contact us if you have other questions.


Regards,
Boby
Telerik
 
Check out Telerik Analytics, the service which allows developers to discover app usage patterns, analyze user data, log exceptions, solve problems and profile application performance at run time. Watch the videos and start improving your app based on facts, not hunches.
 
0
Grigory
Top achievements
Rank 1
answered on 31 Jul 2014, 06:28 AM
Thank you. I've already implemented this behavior. Glad to see that my code is quite similar to your version. Telerik controls have friendly API :)
0
Daniel
Top achievements
Rank 1
answered on 23 Nov 2015, 05:41 PM

Hi,

This solution works only partially. It works only if you use a shortcut like Shift F3 and it clears the formatting.

The toggle functionality should change not only to upper but also from all upper to all lower and from lower to first upper. Fortunately I don't need toggle.

I need just to upper and to lower but not from shortcut but from context menu. Unfortunately if you use context menu the selection is lost. (it is visible, but the statement Document.Selection.IsEmpty returns true). I call it a bug.

Then there is the lost of formatting if you use

string selectedText = this.radRichTextBox.Document.Selection.GetSelectedText();
this.radRichTextBox.Insert(selectedText.ToUpper());

 I replaced it with

foreach (var box in Document.Selection.GetSelectedBoxes().ToList())
{
     if (!box.IsFormattingSymbol)
         box.Text = box.Text.ToUpper();
}

And it almost works (from shortcut not from context menu). You need to scroll up to hide affected text and then scroll down to redraw it. I tried with Invalidate but no luck. Also undo doesn't work.

 

So my questions are:

Is there a way to force document to be redrawn?

Is there a way to use context menu and have access to selection?

Regards,

Daniel

0
Boby
Telerik team
answered on 24 Nov 2015, 11:58 AM
Hi Daniel,

You are right, the solution I proposes was only a workaround, and is not considering formatting of the text.

The best approach would be to get the selected spans from the selected span layout boxes, modify their text with ToUpper/ToLower where needed and then re-insert the spans. This could be tricky tough, as you would need mapping between the spans and the plain text representation, moreover some of the spans may be only partially selected.

Using the RadRichTextBox.InsertInine method to insert spans and BeginUndoGroup/EndUndoGroup methods will ensure that the history will be preserved, and Undo/Redo would work as expected. 

There is a public item in the feedback portal regarding the change case command here:
Change Case Command
where you can vote for this feature and track eventual implementation.

For the context menu, you can customize it (add custom elements) using one of the approaches described in the Context Menu help article. I have performed some tests and the selection seems correctly reported as not empty (for example in a ContextMenu.Showing event handler).

Regards,
Boby
Telerik
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
0
Daniel
Top achievements
Rank 1
answered on 24 Nov 2015, 06:35 PM

Thanks,

I've fixed the issue with selection.

I tried to use InsertInline but it works only if you select one whole span.

As I asked before. IS there a way to force document to be redrawn?

Regards,

Daniel

0
Boby
Telerik team
answered on 26 Nov 2015, 06:51 AM
Hi Daniel,

You could, but I would not recommend using the approach with setting the text directly on the layout boxes, because of the following reasons:
- This relies on an implementation detail - namely that setting text on a layout box would affect the underlying span. For historical reasons the API of the layout model is too "open", exposing properties (like set_Text) which are not very user-friendly and should not be used in normal circumstances.
- The layout would be only partially invalidated - for example the last words could be trimmed as the parent paragraph layout box is not notified for the change.
- Setting properties directly on the document model elements (and not through RadRichTextBox or RadDocumentEditor) is not recommended as it could be easy to broke the underlying model (some validation checks are skipped) and moreover it resets the document history.
- Without more complex logic, you would get a behavior similar to the approach suggested by me (with using InsertInline) - if only parts of the span box are selected, you would change the case of the whole span box.

Otherwise, the most easiest way to simulate what happens on scroll is to invoke the RecreateUI method of the active document presenter:
this.radRichTextBox.ActiveEditorPresenter.RecreateUI();

Regards,
Boby
Telerik
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
Tags
RichTextBox
Asked by
Grigory
Top achievements
Rank 1
Answers by
Grigory
Top achievements
Rank 1
Boby
Telerik team
Daniel
Top achievements
Rank 1
Share this question
or