Silverlight 4 textbox right click context menu with cut, copy and paste behavior

May 27, 2010 at 9:46 PMdsoltesz

I wanted to be able to allow my users the ability to right click in a textbox and have a contextmenu shown to be able to cut, copy and paste. I decided to create a behavior so that the functionality can easily be added to any textbox control.


using System.Windows;

using System.Windows.Controls;

using System.Windows.Input;

using System.Windows.Interactivity;

 

namespace MyApp.Behaviors.TextBox

{

    public class TextBoxCutCopyPasteBehavior : Behavior<System.Windows.Controls.TextBox>

    {

        private ContextMenu _contextMenu;

        private MenuItem _copy;

        private MenuItem _cut;

        private MenuItem _paste;

        public TextBoxCutCopyPasteBehavior()

        {

 

            _contextMenu = new ContextMenu();

            _cut = new MenuItem();

            _cut.Header = "Cut";

            _cut.Click += new RoutedEventHandler(cut_Click);

            _contextMenu.Items.Add(_cut);

 

            _copy = new MenuItem();

            _copy.Header = "Copy";

            _copy.Click += new RoutedEventHandler(copy_Click);

            _contextMenu.Items.Add(_copy);

 

            _paste = new MenuItem();

            _paste.Header = "Paste";

            _paste.Click += new RoutedEventHandler(paste_Click);

            _contextMenu.Items.Add(_paste);

 

 

        }

 

        void paste_Click(object sender, RoutedEventArgs e)

        {

            AssociatedObject.SelectedText = Clipboard.GetText();

            _contextMenu.IsOpen = false;

        }

 

        void cut_Click(object sender, RoutedEventArgs e)

        {

            Clipboard.SetText(AssociatedObject.SelectedText);

            AssociatedObject.SelectedText = string.Empty;

            AssociatedObject.Focus();

            _contextMenu.IsOpen = false;

        }

 

        void copy_Click(object sender, RoutedEventArgs e)

        {

            Clipboard.SetText(AssociatedObject.SelectedText);

            AssociatedObject.Focus();

            _contextMenu.IsOpen = false;

        }

 

        protected override void OnAttached()

        {

 

            AssociatedObject.MouseRightButtonDown += new MouseButtonEventHandler(AssociatedObject_MouseRightButtonDown);

            AssociatedObject.MouseRightButtonUp += new MouseButtonEventHandler(AssociatedObject_MouseRightButtonUp);

            AssociatedObject.SetValue(ContextMenuService.ContextMenuProperty, _contextMenu);

            base.OnAttached();

        }

 

        void AssociatedObject_MouseRightButtonUp(object sender, MouseButtonEventArgs e)

        {

            if (Clipboard.ContainsText())

                _paste.IsEnabled = true;

            else

                _paste.IsEnabled = false;

 

            if (string.IsNullOrEmpty(AssociatedObject.SelectedText))

            {

                _cut.IsEnabled = false;

                _copy.IsEnabled = false;

            }

            else

            {

                _cut.IsEnabled = true;

                _copy.IsEnabled = true;

            }

            _contextMenu.IsOpen = true;

 

        }

 

        void AssociatedObject_MouseRightButtonDown(object sender, MouseButtonEventArgs e)

        {

            e.Handled = true;

        }

 

        protected override void OnDetaching()

        {

            AssociatedObject.MouseRightButtonDown -= new MouseButtonEventHandler(AssociatedObject_MouseRightButtonDown);

            AssociatedObject.MouseRightButtonUp -= new MouseButtonEventHandler(AssociatedObject_MouseRightButtonUp);

            _contextMenu = null;

            base.OnDetaching();

 

        }

 

    }

}

 

 

Once the behavior is created we can now add to our TextBox

  1. add a reference to System.Windows.Interactivity
  2. In your xaml code add
    1. xmlns

      :i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
    2. xmlns

      :behaviors="clr-namespace:MyApp.Behaviors.TextBox;assembly=MyApp"
    3.  <TextBox x:Name="myTextBox" >
             <i:Interaction.Behaviors>
                 <behaviros:TextBoxCutCopyPasteBehavior/>
             </i:Interaction.Behaviors>
         </TextBox>


That's it, now you have right click support for your textbox with cut, copy and paste.

Posted in: behaviors | silverlight

Tags: ,

Comments (8) -

Good post Dan!
Can you say why a text selection disappears when context menu opens?

Reply

Ivan, I did notice that behavior as well, right now I'm not really sure.  Seems to be how functionality is tied into SL4 and the context menu control

Reply

Joe Gershgorin
United States Joe Gershgorin says:

Thanks for the snippet, I used as a base for some improvements I made:

Added :

* Delete, Select All, and poor man's Undo menu items

* Workaround for loss of textbox selection highlight when context menu is shown

* Workaround for IsEnabled visual of a menu item not updating when IsEnabled property value is changed via code (known SL4 bug):

blogs.msdn.com/.../...abled-property-directly.aspx

* Catch security exception if user disallows clipboard access, and addition of Subscribable event when exception occurs so developer can be notified / act in response to the exception.

* Ability to specify header of menu items via behavior properties (Good for localization support)

Available here:
http://www.codepaste.net/ckwbjm

Reply

I tried to use Joe's solution with select-on-entry textbox which selects whole content if it receives focus:

partial class TextBoxBase1 : TextBox
    {
        protected override void OnGotFocus(RoutedEventArgs e)
        {
            if (!AcceptsReturn)
                SelectAll();
            base.OnGotFocus(e);
        }

If part of Text is selected, Joe behaviour causes whole Text to be selected. How to fix this so that part of text can also copied using Joe's behavior ?

Reply

How to add icon and right-aligned shortcut mnemonics like in code below ?

Andrus.

        <toolkit:ContextMenuService.ContextMenu>
            <toolkit:ContextMenu KeyDown="TextBox_KeyDown" >
                <toolkit:MenuItem Command="{Binding CopyCommand}">
                    <toolkit:MenuItem.Header>
                        <toolkit:HeaderedContentControl Header="Copy" Content="Ctrl+C" Style="{StaticResource HeaderedContentControlStyle1}"/>
                    </toolkit:MenuItem.Header>
                    <toolkit:MenuItem.Icon>
                        <Image Source="Images/save-16x16.png" />
                    </toolkit:MenuItem.Icon>
                </toolkit:MenuItem>

        <!--forces text and mnemonics in same line in context menu: forums.silverlight.net/.../469771.aspx-->;
        <Style x:Key="HeaderedContentControlStyle1" TargetType="toolkit:HeaderedContentControl">
            <Setter Property="Foreground" Value="#FF000000"/>
            <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
            <Setter Property="VerticalContentAlignment" Value="Stretch"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="toolkit:HeaderedContentControl">
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="1*" />
                            </Grid.RowDefinitions>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="1*" MinWidth="150" />
                                <ColumnDefinition Width="Auto" />
                            </Grid.ColumnDefinitions>

                            <ContentPresenter Grid.Row="0" Grid.Column="0" Cursor="{TemplateBinding Cursor}" ContentTemplate="{TemplateBinding HeaderTemplate}" Content="{TemplateBinding Header}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                            <ContentPresenter Grid.Row="0" Grid.Column="1" Cursor="{TemplateBinding Cursor}" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>

                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

Reply

I don't see why it wouldn't work with SL5

Reply

Nice pice off code thanks!

Reply

Why doesn't it work on a ChildWindow?

If you right click in a textbox with this behavior on it and then right click again, the contextmenu moves to the upper left corner.

It is as if that first time of focusing the textbox does something and then the next to, since it isn't focusing the same way, it works differently.

Reply

Add comment

  Country flag

biuquote
  • Comment
  • Preview
Loading