Blog Archives

Implementing Edit and Format Features in a WPF RichTextBox

In previous blogs, I looked at how to access a plain text file through a RichTextBox and then how to open and save content in RTF format.   In the second blog, I also looked at some core editing features which are available as keyboard shortcuts.

This time I want to look at how to create a simple text editor based around the RichTextBox.  I also want to cover the scenario where you load a file of one type, such as a plain  text file, make changes and then save it as an RTF file.

Because there is quite a lot of content here, I’ve numbered the sections and listed them:

  1. Introduction to creating a WPF User Control in a library
  2. The skeleton of the RTBEditor user control
  3. Referencing the user control from another project
  4. Creating the File Menu Item of the user control
  5. Creating and coding the File >New menu item
  6. Creating and coding the File > Open menu item.
  7. Creating and coding the File > Save As Text menu item
  8. Creating and coding the File > Save As Rtf menu item
  9. Creating and coding the File > Exit menu item
  10. Creating the Edit menu item
  11. Creating the Format and Align menu items

 

1.  Creating a WPF User Control

Instead of adding a RichTextBox directly into a Window, I’m going to create a WPF UserControl.   In the interests of reusability and portability, I’ll store this in a WPF User Control Library.  To do this from your currently open WPF Solution:

  1. Select File > Add > New Project in the Visual Studio IDE main menu.
  2. Select ‘Windows’ from the list of Project types.
  3. Select WPF User Control Library from the list of templates.
  4. Rename it.  I named the library ‘WPFUserControls’.
  5. Click OK.

To add a new User Control to the library:

  1. Select Project > Add New Item from the IDE menu.
  2. Select WPF from the Categories list.
  3. Select User Control (WPF) from the Templates list.
  4. Rename it.  I named it RTBEditor.xaml.
  5. Click ‘Add’.

2.  Skeleton of the User Control

To create the skeleton of the User Control, do the following:

  1. Delete the default Grid in the UserControl markup.
  2. Replace it with a DockPanel.
  3. Inside the DockPanel, add a Menu element.
  4. Set its DockPanel.Dock property to Top.
  5. Set its MinHeight property to 25.
  6. Inside the DockPanel, but outside the markup for the Menu, add a RichTextBox.
  7. Name it ‘RTB’.
  8. Set its DockPanel.Dock property to Bottom.
  9. Set its MinHeight and MinWidth properties to 200.

The markup below includes the steps described above, plus a few other less important property settings I’m using for demo purposes:

  <DockPanel>

    <Menu Margin="0,0,0,5" Name="Menu1" DockPanel.Dock="Top" MinHeight="25" >

 

    </Menu>

    <RichTextBox Name="RTB" BorderThickness="2" DockPanel.Dock="Bottom"

                MinHeight="200" MinWidth="200"

                 Background="#FFEDEAEA">

    </RichTextBox>

  </DockPanel>

3.  Referencing the User Control

When I’m building a User Control, I like to insert an instance of it in a Window, so I can see how things are progressing.

Because the User Control is stored in a library, it’s necessary to reference this library from any project that wants to use it.    In the Solution I’m using, I have two projects; the Library, named WPFUserControls and a standard WPF project named WpfRichTextBox.   You can see this from the screenshot below:

RTB003

I need to add a reference to the library in the WPF Window Application project so that I can access and use the RTBEditor user control in one or more Windows.  To do this:

  1. Ensure you have the WPF Window Application project selected in the Solution Explorer.
  2. Select Project > Add Reference form the IDE main menu.
  3. In the Add Reference dialog box, click on the Projects tab.
  4. There will only be one Project Name in the list, so ensure this is selected and click OK.

In WPF, another step is needed.  You have to add details of the namespace to the markup of the Window where you want to use the user control.   I’m using a file named Window2 to host the text editor control, so I need to add the following markup to the xml namespaces at the top of the file:

xmlns:uc="clr-namespace:WPFUserControls;assembly=WPFUserControls"

You can type that in by hand if you want to, but it’s not the easiest syntax to remember.  Far easier is to type as far as the “uc=” point and then use the Intellisense list to select the library project. 

RTB004

In this case, you want to select the ‘WPFUserControls in assembly WPFUserControls’ item, because this is the name of the project you are referencing.   When you click on it, the correct syntax, as shown in the earlier XAML snippet, will be created for you automatically.

With this in place, you can add an instance of the user control to the Window.  Here’s the markup for the Window so far:

 

    1 <Window x:Class="Window2"

    2    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    3    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    4    xmlns:uc="clr-namespace:WPFUserControls;assembly=WPFUserControls"

    5    Title="Using the Text Editor " Height="500" Width="400">

    6 

    7   <Grid>

    8     <uc:RTBEditor />

    9 

   10   </Grid>

   11 </Window>

There’s not much to see yet, but we can build up the user control and watch its progress.

4.  The File Menu of the User Control

WPF Menus are mainly composed of MenuItems.   The two key properties of a MenuItem are its Header (the text the user sees)  and its Click property (the link to the event handler for the Click event in the code-behind).

MenuItems can be nested, so in this case several sub items are nested inside the main File menu.  The following XAML placed inside the Menu1 Menu will create the standard list of File items:

    6       <MenuItem Header="_File" Name="FileMenuItem" >

    7         <MenuItem Header="_New" Name="mnuItemNew" Click="mnuItemNew_Click" />

    8         <MenuItem Header="_Open" Name="mnuItemOpen" Click="mnuItemOpen_Click" />

    9         <MenuItem Header="Save as _Text" Name="mnuItemTxtSave" Click="mnuItemTxtSave_Click" />

   10         <MenuItem Header="Save as _RTF" Name="mnuItemRTFSave" Click="mnuItemRTFSave_Click" />

   11         <Separator />

   12         <MenuItem Header="E_xit" Name="mnuExit" Click="mnuExit_Click" />

   13       </MenuItem>

The only points to note here are that you can use accelerator keys, such as Alt & F for the File Menu, or – more usefully – Alt & F & N for File > New.   Placing the underscore immediately in front of the letter you want as the hotkey is all that’s needed.
You’ll see that I also added a Separator element between the Exit menu item and the others.

5.   The New File Item

For our purposes, we don’t really need to create a new file at this point.  What the user wants is a blank RichTextBox in which to enter some content.   So we can just the use the Clear method to do this:

    Private Sub mnuItemNew_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)

        RTB.Document.Blocks.Clear()

    End Sub

In a production application, you would of course want to build in a feature that asks the user to confirm what is effectively a total deletion of the current content. 

6.  The Open File Item

WPF currently doesn’t have its own OpenFileDialog, but you can use the old Win32 version.  Add the following two Imports statements to the code-behind:

Imports Microsoft.Win32

Imports System.IO

The first is for the OpenFileDialog; the second for the FileStream I’m just about to use to open and read the file.

Here is the code for this menu item:

   13     Private Sub mnuItemOpen_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)

   14         'Let the user select a file

   15         Dim ofd As New OpenFileDialog

   16         With ofd

   17             '  Set preferred file types

   18             .Filter = "Text Files(*.txt;*.rtf)|*.txt;*.rtf" & _

   19             "| All files (*.*)|*.*"

   20 

   21             If .ShowDialog() = True Then

   22                 '  Identify the chosen file extension

   23                 Dim dataformat As String

   24                 Select .FileName.Substring(.FileName.Length – 3)

   25                     Case "rtf"

   26                         dataformat = DataFormats.Rtf

   27                     Case Else

   28                         dataformat = DataFormats.Text

   29                 End Select

   30 

   31                 '  Then load the file using the appropriate data format

   32                 Dim fs As New FileStream(.FileName, FileMode.Open, FileAccess.Read)

   33                 Using fs

   34                     'Create a TextRange that comprises the start and end points of the RichTextBox text

   35                     Dim RTBText As New TextRange(RTB.Document.ContentStart, RTB.Document.ContentEnd)

   36                     RTBText.Load(fs, dataformat)

   37                 End Using

   38             End If

   39         End With

   40     End Sub

The OpenFileDialog code is straightforward and is no different from what you do in Windows Forms.   The Filter aims to encourage users to select text files that are editable in the RichTextBox, but allows them the freedom to choose others.   If they choose anything other than an Rtf file, the data format is set to Text.   This will allow actual text format files to be displayed properly; any others will simply display their binary form content.

The file load code is the same as I used in the earlier blogs, except that I use a variable named ‘dataformat’ instead of passing in the actual DataFormats object as the second parameter of the Load method of the TextRange.

7.  The Save As Text MenuItem

The code to save the content to a .txt file is very similar to that used in the earlier blog.  The only difference is that again I have added a file dialog to give the user the choice of file name and save location.    This time, of course, it’s a SaveFileDialog. 

   42     Private Sub mnuItemTxtSave_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)

   43         Dim sfd As New SaveFileDialog

   44         sfd.Filter = "Text Files(*.txt)|*.txt"

   45 

   46         If sfd.ShowDialog = True Then

   47             Dim fs As FileStream = File.OpenWrite(sfd.FileName)

   48             Using fs

   49                 Dim RTBText As New TextRange(RTB.Document.ContentStart, RTB.Document.ContentEnd)

   50                 RTBText.Save(fs, DataFormats.Text)

   51             End Using

   52         End If

   53     End Sub

 

8.  The Save As RTF Menu Item

It isn’t really necessary to have two separate procedures for the two different formats.    The above procedure can easily be edited to identify which of the menu items called it and then switch the Filter of the SaveFileDialog and the DataFormats value accordingly.  But for now we’ll just live with a bit of unnecessary duplication:

   55     Private Sub mnuItemRTFSave_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)

   56         Dim sfd As New SaveFileDialog

   57         sfd.Filter = "Rtf Files(*.Rtf)|*.Rtf"

   58 

   59         If sfd.ShowDialog = True Then

   60             Dim fs As FileStream = File.OpenWrite(sfd.FileName)

   61             Using fs

   62                 Dim RTBText As New TextRange(RTB.Document.ContentStart, RTB.Document.ContentEnd)

   63                 RTBText.Save(fs, DataFormats.Rtf)

   64             End Using

   65         End If

   66     End Sub

9.  The Exit MenuItem

I’ll be honest: I included this out of habit and then realised that this is a user control, not a Window or a Form.   So you wouldn’t usually want the Exit menu item to unilaterally close down the parent container.  So in this case I’ll use ‘Exit’ as meaning ‘Clear the current content’.   In which case, this now familiar Clear method will do nicely:

   68     Private Sub mnuExit_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)

   69         RTB.Document.Blocks.Clear()

   70     End Sub

10.   The Edit Menu Item

This is another container for nested menu items.   For this example, I’ve chosen to use the Cut, Copy and Paste commands.  Here’s the markup:

   14      <MenuItem Header="_Edit">

   15         <MenuItem Header="Cut" Command="Cut"/>

   16         <MenuItem Header="Copy" Command="Copy" />

   17         <MenuItem Header="Paste" Command="Paste" />

   18         <Separator />

   19         <MenuItem Header="Undo" Command="Undo" />

   20         <MenuItem Header="Redo" Command="Redo" />

   21       </MenuItem>

You’ll notice that I haven’t used the underscores with these menu items.  I could have, but as you will see if you run this project you already have the standard Ctrl & X, Ctrl & C, etc and I thought that a different set with Alt & whatever would just be confusing.

There are several key things to notice in this markup.   The first is that there is no Click property pointing to an event handler in the code-behind.   In fact, there is no code-behind needed for this functionality.  Because all five of these are built-in Application Commands, selecting any of them will cause that action to take place (maybe – more on this in a minute).   So, simply by adding the ‘Command=Undo’  to the markup, everything is in place to ensure that selecting that menu item will automatically cause the user’s last action to be undone.

Note that this is an Application wide command, so it will undo whatever user action the application was last aware of.  In other words, if this user control is surrounded by other controls in the Window and you apply an action to one of those other controls and then use the RTBEditor’s Undo command, it will undo the action you took on the other control.   It’s an application command, not a RichTextBox one.

Next, these commands are smart.  If it’s not appropriate for them to be available, then they won’t be.   What I mean by that is if, for example, you haven’t selected any content and so there is nothing on the clipboard, then the Paste command won’t be available.   The same logic applies to all these commands.   Here’s a screenshot:

RTB005

As you can see, there is no text in the RichTextBox and nothing has been selected.  Therefore the Cut and Copy commands (and their wired up menu items) are disabled.  In short, these commands know when they can or cannot execute.   (If you’re wondering, Paste is available because I had some other content still on the clipboard – it’s an Application wide command, remember).

Notice also that because these are Application commands with built-in shortcut key combinations, those combos are automatically displayed next to each menu item for you. 

11.   The Format and Align Menu Items

If you’ve groked the idea of how commands are used here, you’ll have no problem with the next two sets of menu items – Format and Align.   Here’s the markup:

 

   22       <MenuItem Header="Format">

   23         <MenuItem Header="_Bold" Command="{x:Static EditingCommands.ToggleBold}" />

   24         <MenuItem Header="_Italic" Command="{x:Static EditingCommands.ToggleItalic}" />

   25         <MenuItem Header="_Underline" Command="{x:Static EditingCommands.ToggleUnderline}" />

   26       </MenuItem>

   27       <MenuItem Header="Align">

   28         <MenuItem Header="Left" Command="{x:Static EditingCommands.AlignLeft}" />

   29         <MenuItem Header="Center" Command="{x:Static EditingCommands.AlignCenter}" />

   30         <MenuItem Header="Right" Command="{x:Static EditingCommands.AlignRight}" />

   31       </MenuItem>

The key difference here (and I’ll admit that it caught me out at first) is that you need to use a markup extension to get at these commands.   You can see that the first three commands have the prefix of ‘Toggle’, as in ‘Toggle Bold’, and so on.   And the commands do work in exactly that way.  Click them once to toggle the effect on, click again to toggle it off.   You can select text and apply one of the formats or you can toggle a setting and then begin to type.  The setting you have just made will then be applied.   That’s why you will find that they are not disabled even when nothing is selected.

You’ll see that the markup extension requires the fully qualified name of the command.   There are other groups of commands, other than ApplicationCommands and EditingCommands and I will be looking at some of those in the next blog.   I also plan to include other commands and features, such as the ability to colour text and tweak the fonts.   At some stage I also want to bring the whole thing completely into the world of WPF by using a FlowDocument as the content container inside the RichTextBox.

Summary

In the meantime, you have the makings of a useful text editor here.   There are many other Application and Editing commands and you may well want to add some of those to the menu.    WPF’s Commands can make many of these common tasks very easy to implement.

How To Create and Use a Custom Command in WPF

  I've got to be honest and admit up front that initially I wasn't completely sold on the idea that WPF Commands are the great leap forward that they are sometimes billed as. OK, so I get that they can reduce the repetition of event handling code, but even their most ardent supporters aren't going to be able to claim that the required code is particularly intuitive. I will agree though that the availability of CanExecute is useful sometimes when multiple controls are bound to a Command. As a WinForms developer moving to WPF, I still find that in many cases I'm happy to use event handlers and, where necessary, a utility method or two.

So, having got that caveat out of the way, let's look at a basic situation where we want a range of different user actions to trigger the same task.

I've decided to use the scenario that I covered in this blog. It's maybe not the most realistic scenario, but it does give an opportunity to use a wide range of user inputs and gestures to move the image across the screen. It also allows the use of the CanExecute event.

In the earlier blog, the user had to click on an image to make it move from left to right across a Canvas. This can soon become tiresome, so by means of a Command we'll give her several other options. In order to make the use of CanExecute realistic, we will say that once the image reaches the far right edge of the Canvas, it must stay there. So the image move will be allowed to happen (i.e. CanExecute) as long as it hasn't reached the right hand edge of the canvas.

Creating the User Interface
The following XAML will create an updated version of the Canvas-and-Image UI used previously. This version includes various controls that will be bound to the Command. 

<Window x:Class="Window3"

   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

 

   Title="Using A Command" Height="300" Width="300">

 

 

    <Grid x:Name="MainGrid">

    <Grid.RowDefinitions>

      <RowDefinition Height="Auto" />

      <RowDefinition Height="218*" />

      <RowDefinition Height="Auto" />

    </Grid.RowDefinitions>

 

 

      <!–  Canvas in the main middle section –>

    <Canvas x:Name="MainCanvas" Grid.Row="1">

      <Image x:Name="MoveableImage" Width="55" Source="questionmark2.jpg"

 

        Canvas.Left="0" Canvas.Top="0" />

    </Canvas>

 

    <!– Menu at the top –>

    <Menu Grid.Row="0" Margin="3">

      <MenuItem Header="Move It" Margin="5"

 

               ></MenuItem>

    </Menu>

 

    <ToolBar Grid.Row="2" >

      <Button Content="Move It"

              />

    </ToolBar>

 

      <Button Grid.Row="1" VerticalAlignment="Bottom"

             HorizontalAlignment="Right" Width="100"

             Height="33" Content="Move It" Margin="0,0,4,2"

              />

  </Grid>

</Window>

 (If you've looked at the markup closely and think that the order of elements is haphazard, you would be wrong.   Try moving the MenuItem to what seems a more logical position above the Canvas and you will get an Error when the code is completed.  As you will see, the code behind for the MenuItem will eventually have a link to the Image.   So therefore the Image must be created first in the top down XAML file, before the MenuItem can know about it .  I often think that it's these little potential Gotchas that make the WinForms-to-WPF learning curve so difficult.)

This is what it should look like:

  

Creating the Command
Let's start by creating a new Command Class called MoveItCommand. It will have a single property named MoveIt, a backing field named _move it and a parameterless constructor. The property and field are of type RoutedUICommand, but apart from that are standard and really don't need any additional explanation: 

    Private Shared _moveit As RoutedUICommand

    Public Shared ReadOnly Property MoveIt() As RoutedUICommand

        Get

            Return _moveit

        End Get

    End Property

 The constructor is used to create the various keyboard and mouse gestures that can be used to move the image. For the purposes of demonstration, I've gone totally overboard on these and have included four variations in the final version. But for clarity, I am only showing one in the code snippet below:

     Shared Sub New()

        '  Add keyboard and mouse gestures

        Dim UserInputs As New InputGestureCollection()

        UserInputs.Add(New KeyGesture(Key.M, ModifierKeys.Alt))

 

         '  Assign these gestures to the _moveit field (and thereby to the MoveIt property)

        _moveit = New RoutedUICommand("Move Element", "Move", GetType(MoveItCommand), UserInputs)

 

    End Sub 

  • The constructor begins by creating a new empty collection of mouse gestures.
  • The second line creates and stores a gesture which takes the Alt and M keys as the combination to be used to fire this command. Notice the order of the keys used as arguments – effectively it is "M & Alt" which isn't the way we usually think of the combination. If you switch the order and place the Modifier key first, you will get a runtime error. Trust me, I've been there, done that!
  • Finally, a new RoutedCommand is created, containing descriptive text, its name, owner type (which is this custom command class – MoveItCommand), and the collection of Gestures that will work with this command.

 Note that all the members of this class are Shared, ensuring that only instance of the Command will be in use in the application when it runs.

Additional Gestures
If you are happy with how the Class and its Constructor works, I'll now add in those other gestures I mentioned. These include F12 on its own, a mouse click on its own and finally a combination of mouse click and key press. Here's the revised code:  

    Shared Sub New()

        '  Add keyboard and mouse gestures

        Dim UserInputs As New InputGestureCollection()

        UserInputs.Add(New KeyGesture(Key.M, ModifierKeys.Alt))

        UserInputs.Add(New KeyGesture(Key.F12, ModifierKeys.None))

        UserInputs.Add(New MouseGesture(MouseAction.RightClick, ModifierKeys.None))

        UserInputs.Add(New MouseGesture(MouseAction.LeftClick, ModifierKeys.Shift))

         '  Assign these gestures to the _moveit field (and thereby to the MoveIt property)

        _moveit = New RoutedUICommand("Move Element", "Move", GetType(MoveItCommand), UserInputs)

    End Sub

 

  • The first gesture is Alt and M, which I've already covered.
  • The second gesture is the F12 key on its own. Note that you must include Modifiers.None. if you don't, you will get a runtime error.
  • The third gesture will fire the command when the mouse is right clicked anywhere in the Window. Because of the way that WPF and RoutedCommands work, this effectively means that you can still click on the Image to make it move. If you wanted to limit the mouse click to the Image only, you could harness the power of Bubbling and Tunneling events to trap the mouse button press at the Image level. I haven't done that in this example.
  • The final gesture takes a combination of both the mouse left button click together with holding down the Shift key. Quite awkward to use and only included to demonstrate the range of available gestures.

 

Command Bindings
Having created the custom Command Class, if we want to use it in the XAML – and we do – then it is necessary to create an XML namespace mapping. Without this link, the XAML markup would have no idea about the existence of the class and its property. The syntax is fairly simple: 

   xmlns:local="clr-namespace:GetValue" 

The alias of 'local' is used as the key which is mapped to the namespace in which the MoveItCommand class resides. The namespace is the same as the name of the project and in this case the project is named 'GetValue'.

Given this namespace mapping, we can now set up the CommandBinding in the XAML pane:

 

  <Window.CommandBindings>

    <CommandBinding Command="local:MoveItCommand.MoveIt"

                   Executed="CommandBinding_Executed"

                   CanExecute="CommandBinding_CanExecute"/>

  </Window.CommandBindings>

 

As you see from the snippet above, you create a new CommandBinding inside a CommandBindings collection. The Command property of the CommandBinding points to that MoveIt property created in the MoveItCommand class.

The Executed and CanExecute properties point to two methods which we will create next in the code-behind.

Executing The CommandBinding The task of the CommandBinding_Executed method is to carry out whatever tasks we want to have actioned when the command is invoked. In this demonstration project, this task is simply to shunt the image a few units to the right. So the code is as follows:  

    Private Sub CommandBinding_Executed(ByVal sender As System.Object, ByVal e As System.Windows.Input.ExecutedRoutedEventArgs)

        '  Move the image 5 units to the right

        Dim LeftPos As Double = MoveableImage.GetValue(Canvas.LeftProperty)

        MoveableImage.SetValue(Canvas.LeftProperty, LeftPos + 5)

 

    End Sub 

(If you are not sure about how the GetValue and SetValue functions work, you can read up on it in my blog here).

CanExecute
CanExecute is an optional feature which can be quite useful. In this demonstration, we will only allow the Command to work (and therefore only allow the Image to continue moving to the right) if it hasn't reached the right hand edge of the Canvas. More subtly, if CanExecute becomes set to False, any controls that are bound to the Command will automatically be disabled. I will add Command Bindings to the buttons, etc shortly.

First, here is the code for the CanExecute method: 

    Private Sub CommandBinding_CanExecute(ByVal sender As System.Object, ByVal e As System.Windows.Input.CanExecuteRoutedEventArgs)

        '  Only allow execution of the command if the image

        '  has not yet reached the right hand edge.

        e.CanExecute = MoveableImage.GetValue(Canvas.LeftProperty) < (MainCanvas.ActualWidth – MoveableImage.Width)

 

    End Sub 

It tests the current position of the Image to see if it has reached the right hand edge of the Canvas. CanExecute will be set to True if there is still space to the right side of the Image, otherwise it will be set to False.

The complete code behind for the Window and the Custom Command class together now looks like this: 

Partial Public Class Window3

 

    Private Sub CommandBinding_Executed(ByVal sender As System.Object, ByVal e As System.Windows.Input.ExecutedRoutedEventArgs)

        '  Move the image 5 units to the right

        Dim LeftPos As Double = MoveableImage.GetValue(Canvas.LeftProperty)

        MoveableImage.SetValue(Canvas.LeftProperty, LeftPos + 5)

 

    End Sub

 

    Private Sub CommandBinding_CanExecute(ByVal sender As System.Object, ByVal e As System.Windows.Input.CanExecuteRoutedEventArgs)

        '  Only allow execution of the command if the image

        '  has not yet reached the right hand edge.

        e.CanExecute = MoveableImage.GetValue(Canvas.LeftProperty) < (MainCanvas.ActualWidth – MoveableImage.Width)

 

    End Sub

End Class

 

Public Class MoveItCommand

 

    Private Shared _moveit As RoutedUICommand

    Public Shared ReadOnly Property MoveIt() As RoutedUICommand

        Get

            Return _moveit

        End Get

    End Property

 

    Shared Sub New()

        '  Add keyboard and mouse gestures

        Dim UserInputs As New InputGestureCollection()

        UserInputs.Add(New KeyGesture(Key.M, ModifierKeys.Alt))

        UserInputs.Add(New KeyGesture(Key.F12, ModifierKeys.None))

        UserInputs.Add(New MouseGesture(MouseAction.RightClick, ModifierKeys.None))

        UserInputs.Add(New MouseGesture(MouseAction.LeftClick, ModifierKeys.Shift))

         '  Assign these gestures to the _moveit field (and thereby to the MoveIt property)

        _moveit = New RoutedUICommand("Move Element", "Move", GetType(MoveItCommand), UserInputs)

    End Sub

 

End Class 

At this point, you can run the project and use any of those four gestures to move the Image. Once the Image hits the right hand edge, it will stop moving. The menu item, the two buttons don't play any part in the action yet – that is, clicking them will have no effect and they won't become disabled when the Image reaches the right hand limit.

Binding the Command to the Controls
All that remains to do is to bind this custom command to the menu item and the two buttons. Now things really do become simple and you can begin to see the potential benefit of using a Command. The following piece of markup is added to the MenuItem, the Button in the Toolbar and the Button in the Canvas:  

        Command="local:MoveItCommand.MoveIt" 

With those bindings in place, you can move the Image by any of the seven methods included – the four input gestures and by clicking on any of the three controls. Note now that once the Image hits the right hand edge, those three controls all become disabled.

So, there is a fairly simple example of using your own Command. You can see the final version of the XAML markup here, and also the code behind here.

WP Like Button Plugin by Free WordPress Templates