WPF Tricks: Context Menu via Behavior

In WPF, behaviors allow for an element of functionality to be packaged into a reusable component. Behaviors enable application designers to trigger changes in the program behavior with little effort and without interfering with the program code. For example, the developers provide behaviors for drag & drop or touch interactions that are then simply used on the corresponding element in the “blend”. This tutorial shows you how to create a context menu by means of a behavior.

The goal

Enable the user to copy the content of a list. As we do not want to convert the individual list elements into editable text boxes, a context menu is required to enable copying:

Figure 1: Context menu for copying the content

Here is how:

We create a new behavior derived from behavior<T>. The blend behavior is located in System.Windows.Interactivity. The type is specified as FrameworkElement—this allows our behavior to work on virtually all controls:

    public class CopyContentBahavior : Behavior<FrameworkElement>

First, we override OnAttached() and OnDetaching(). This is where we can react if a behavior is added or removed.

    protected override void OnAttached()
    {
     base.OnAttached();
    }
    protected override void OnDetaching()
    {
     base.OnDetaching();
    }

The behavior gives us access to the element to which it has been attached. This is what the AssociatedObject property is for. The AssociatedObject is always of the type specified in the class definition (here: FrameworkElement). Now, we attach our context menu with the desired options to the AssociatedObject. In the current example, the command “Copy content” is enough. This command is supposed to copy the clicked-on text to the clipboard. In the example, an icon is provided for the menu entry as well:

    protected override void OnAttached()
    {
     base.OnAttached();
                
     var uri = new Uri("copy-icon-256.png");
     var logo = new BitmapImage();
     logo.BeginInit();
     logo.DecodePixelWidth = 16;
     logo.UriSource = uri;
     logo.EndInit();
     var menuEntry = new MenuItem
     {
      Header = "Copy content",
      Icon = new Image { Source = logo }
     };
     menuEntry.Click += MenuEntryOnClick;
     AssociatedObject.ContextMenu = new ContextMenu();
     AssociatedObject.ContextMenu.Items.Add(menuEntry);
     AssociatedObject.PreviewMouseRightButtonDown += MouseDown;
    }

The element to which we add our context menu is not necessarily the element the content of which we want to copy. We want to copy the content of a ListViewItems, not the entire ListView. Applying the behavior to every single list item, however, would be unreasonably arduous. Therefore, we add an event handler to the AssociatedObject. A right-hand click is not only supposed to open the menu, but identify the “clicked-on” element at the same time. We store the “hit” element in the ‘_element’ field.

    private void MouseDown(object sender, MouseButtonEventArgs args)
    {
     _element = args.MouseDevice.DirectlyOver;
     args.Handled = true;
    }

Now, we implement the actual copying function: For this purpose, the menu entry is given a corresponding event handler. We assume that the hit element is a text block. If it is not, we cannot really copy anything to the clipboard anyway.

    private void MenuEntryOnClick(object sender, RoutedEventArgs args)
    {
     var textBlock = _hitElement as TextBlock;
     if (textBlock != null)
     {
      Clipboard.SetDataObject(textBlock.Text);
     }
    }

Lastly, we use the behavior in our application. It is important that the namespace “blend” is integrated as well: xmlns:i=”http://schemas.microsoft.com/expression/2010/interactivity”

    <ListView>
     <i:Interaction.Behaviors>
      <behaviors:CopyContentBahavior/>
     </i:Interaction.Behaviors>
    </ListView>

This post was written by: