Intersoft ClientUI Documentation
How-to: Implement Rich Editor Command Interface with ToolBar, MenuBar and ContextMenu

This example shows a complete implementation of routed commands in a text editor application.

Example

Description

One of the benefits of using commanding pattern in UI development is that it allows you to easily reuse a command in multiple command sources and eliminate the needs to write redundant code for the same command logic. This is made possible because the commanding pattern introduces a concept that separates the semantics and the object that invokes a command from the logic that executes the command. Consequently, you can rapidly create better user experience that works consistently and reliably.

The following example shows how the routed command model can be used in a real-world scenario such as in a text editor application. In the example, the routed commands such as Cut, Copy, and Paste are bound to multiple command sources such as the items of UXMenuBar, UXToolBar and UXContextMenu.

Code

XAML
Copy Code
<Intersoft:UXPage 
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        xmlns:Intersoft="http://intersoft.clientui.com/schemas"
        xmlns:Commands="clr-namespace:ClientUIApplication_Docs.Commands"
        x:Class="ClientUIApplication_Docs.RoutedCommands.EditorCommandInterface" 
        Title="EditorCommandInterface Page"
        d:DesignWidth="640" d:DesignHeight="480">

    <Grid x:Name="LayoutRoot">
        <Intersoft:DockPanel Name="dockPanel1" FillChildMode="Custom">
            
            <Intersoft:UXMenuBar Intersoft:DockPanel.Dock="Top" AccessModifiers="Control,Alt">
                <Intersoft:UXMenuItem Header="_Edit">
                    <Intersoft:UXMenuItem Header="Cu_t" Command="Commands:EditingCommands.Cut" InputGestureText="Ctrl+Shift+X" Icon="../Images/CutHS.png"/>
                    <Intersoft:UXMenuItem Header="_Copy" Command="Commands:EditingCommands.Copy" InputGestureText="Ctrl+Shift+C" Icon="../Images/CopyHS.png"/>
                    <Intersoft:UXMenuItem Header="_Paste" Command="Commands:EditingCommands.Paste" InputGestureText="Ctrl+Shift+V" Icon="../Images/PasteHS.png"/>
                </Intersoft:UXMenuItem>
            </Intersoft:UXMenuBar>
            
            <Intersoft:UXToolBar Name="toolBar1" Intersoft:DockPanel.Dock="Top">
                <Intersoft:UXToolGroup>
                    <Intersoft:UXToolBarButton Command="Commands:EditingCommands.Cut" DisplayMode="Image" Icon="../Images/CutHS.png"/>
                    <Intersoft:UXToolBarButton Command="Commands:EditingCommands.Copy" DisplayMode="Image" Icon="../Images/CopyHS.png"/>
                    <Intersoft:UXToolBarButton Command="Commands:EditingCommands.Paste" DisplayMode="Image" Icon="../Images/PasteHS.png"/>
                </Intersoft:UXToolGroup>
            </Intersoft:UXToolBar>
            
            <RichTextBox Name="textBox1"
                         Intersoft:DockPanel.IsFillElement="True"
                         Intersoft:ContextMenuService.ContextMenuName="contextMenu1">
                <Paragraph>
                    <Run Text="Type some text here..."/>
                </Paragraph>
            </RichTextBox>
            
            <Intersoft:UXContextMenu x:Name="contextMenu1">
                <Intersoft:UXMenuItem Header="Cut" Command="Commands:EditingCommands.Cut" InputGestureText="Ctrl+Shift+X" Icon="../Images/CutHS.png"/>
                <Intersoft:UXMenuItem Header="Copy" Command="Commands:EditingCommands.Copy" InputGestureText="Ctrl+Shift+C" Icon="../Images/CopyHS.png"/>
                <Intersoft:UXMenuItem Header="Paste" Command="Commands:EditingCommands.Paste" InputGestureText="Ctrl+Shift+V" Icon="../Images/PasteHS.png"/>
            </Intersoft:UXContextMenu>
            
        </Intersoft:DockPanel>

    </Grid>

</Intersoft:UXPage>

The following C# code represents the code behind for the XAML page listed above. This example uses the RegisterClassCommandBinding and RegisterClassInputBinding respectively to register the command and input binding on a particular type. The OnCanExecute event handler implements the logic to determine the condition when the Cut, Copy and Paste command can be executed, while the OnCommandExecuted event handler implements the logic that executes the commands.

C#
Copy Code
using System.Windows;
using ClientUIApplication_Docs.Commands;
using Intersoft.Client.Framework.Input;
using Intersoft.Client.UI.Navigation;
using Intersoft.Client.Framework;
using System.Windows.Controls;
using System.Windows.Documents;

namespace ClientUIApplication_Docs.RoutedCommands
{
    public partial class EditorCommandInterface : UXPage
    {
        public EditorCommandInterface()
        {
            EditingCommands.Initialize();
            InitializeComponent();
            textBox1.SelectionChanged += new RoutedEventHandler(textBox1_SelectionChanged);
        }

        static EditorCommandInterface()
        {
            RoutedUICommand[] commands = new RoutedUICommand[] 
            {
                EditingCommands.Cut, EditingCommands.Copy, EditingCommands.Paste
            };

            // register class-level command binding for each RoutedUICommand
            foreach (RoutedUICommand command in commands)
            {
                CommandManager.RegisterClassCommandBinding(
                    typeof(UXPage),
                    new CommandBinding(command, OnCommandExecuted, OnCanExecute));
            }

            // register class-level input binding

            System.Windows.Input.ModifierKeys modifiers = System.Windows.Input.ModifierKeys.Control | System.Windows.Input.ModifierKeys.Shift;

            CommandManager.RegisterClassInputBinding(
                typeof(UXPage),
                new InputBinding(EditingCommands.Cut, new KeyGesture(System.Windows.Input.Key.X, modifiers)));

            CommandManager.RegisterClassInputBinding(
                typeof(UXPage),
                new InputBinding(EditingCommands.Copy, new KeyGesture(System.Windows.Input.Key.C, modifiers)));

            CommandManager.RegisterClassInputBinding(
                typeof(UXPage),
                new InputBinding(EditingCommands.Paste, new KeyGesture(System.Windows.Input.Key.V, modifiers)));
        }

        private static void OnCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            EditorCommandInterface page = sender as EditorCommandInterface;

            // The e.CanExecute provides feedback to the associated caller whether the 
            // e.Command can be executed. If the value is false, the UI will be automatically disabled
            // thus enables consistent and reliable command interface.

            if (page == null)
            {
                e.CanExecute = false;
                return;
            }

            if (e.Command == EditingCommands.Cut || e.Command == EditingCommands.Copy)
            {
                // Enable Cut or Copy command if the textbox has selection
                e.CanExecute = page.textBox1.Selection != null && !string.IsNullOrEmpty(page.textBox1.Selection.Text);
            }
            else if (e.Command == EditingCommands.Paste)
            {
                // Enable Paste command if the Clipboard has a valid text
                e.CanExecute = page.textBox1.Selection != null && Clipboard.ContainsText();
            }
            else
            {
                e.CanExecute = true;
            }
        }

        private static void OnCommandExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            EditorCommandInterface page = sender as EditorCommandInterface;
            RichTextBox rtb = page.textBox1;

            // When the e.Command is determined to be executable, the Executed handler will be invoked.
            // This function encapsulates the logic for each command in the sample.

            if (e.Command == EditingCommands.Cut)
            {
                // Removes the selection and set it to the Clipboard
                Clipboard.SetText(rtb.Selection.Text);
                rtb.Selection.Insert(new Run());
            }
            else if (e.Command == EditingCommands.Copy)
            {
                // Copy the selected text to the Clipboard
                Clipboard.SetText(rtb.Selection.Text);
            }
            else if (e.Command == EditingCommands.Paste)
            {
                // Paste the Clipboard data to the current selection
                if (!string.IsNullOrEmpty(Clipboard.GetText()))
                    rtb.Selection.Insert(new Run() { Text = Clipboard.GetText() });
            }

            // Ensure the command source has the latest UI state after operation
            CommandManager.InvalidateRequerySuggested();
        }

        private void textBox1_SelectionChanged(object sender, RoutedEventArgs e)
        {
            // Invalidate the commands in this page,
            // this will call the CanExecute event handler of each commands
            // to update their CanExecute state.

            CommandManager.InvalidateRequerySuggested();
        }
    }
}

There are a number of interesting behaviors that you can explore in the above example, such as:

To learn how to create the routed commands for EditingCommands used in the above example, see How-to: Create Routed Commands In a Static Class.

For more information on commanding, see Commanding Overview.

See Also

Concepts

Other Resources