Copying a WPF ListBox Selected Item via Ctrl-C and a Context Menu

This week I was working with a WPF application. The application had a ListBox control that gets populated with item IDs from a SQL database. I wanted to allow the user to copy onto the clipboard, a selected item ID. I wanted to allow both Ctrl-C copying and right-click context menu copying. How hard could it be? I searched the Internet for useful information on this task and basically found chaos; many incomplete suggestions, flat-out incorrect code, and zero complete code examples.

So, fast forward hours later. Many, many hours later. I finally got Ctrl-C and context menu copying to work to my satisfaction. I made a little demo for this post because I’m sure that I’ll have to do this again in the future and I’m equally sure I’ll forget how to do this. See the image below for the demo where I’m copying a ListBox item and then pasting into notepad. The XAML for the demo app’s UI is:

<Window x:Class="WpfCopyListBoxSelectedItemDemo.MainWindow"
        Title="MainWindow" Height="350" Width="525">
    <ListBox Height="150" HorizontalAlignment="Left" Margin="12,20,0,0" Name="listBox1"
      VerticalAlignment="Top" Width="200">

        <CommandBinding Command="ApplicationCommands.Copy"
          CanExecute="CtrlCCopyCmdCanExecute" Executed="CtrlCCopyCmdExecuted" />

          <TextBlock Text="{Binding Path=.}"> 
              <MenuItem Command="Copy"> 
                  <CommandBinding Command="ApplicationCommands.Copy" 
                    Executed="RightClickCopyCmdExecuted" /> 

There’s a lot going on here. The ListBox.CommandBindings tag sets up the ability to do a Crtl-C copy. The idea is that a Ctrl-C applies to the entire ListBox. The CanExecute and Executed methods are:

private void CtrlCCopyCmdExecuted(object sender, ExecutedRoutedEventArgs e)
  ListBox lb = (ListBox)(sender);
  var selected = lb.SelectedItem;
  if (selected != null) Clipboard.SetText(selected.ToString());

private void CtrlCCopyCmdCanExecute(object sender, CanExecuteRoutedEventArgs e)
  e.CanExecute = true;

Notice the sender is the entire ListBox, so I grab the selected item and copy it to the system Clipboard. Not too bad. But then for the context menu copy . . .

The ListBox.ItemTemplate tag sets up the ability to do a context menu copy. Sometimes I get the feeling that the designers of WPF went a little too far in making WPF flexible. But anyway, the main point is that the context menu is associated with the selected item, which isn’t entirely obvious unless you scan the XAML code carefully. I spent a ton of time wading through the ItemTemplate to DataTemplate to TextBlock to TextBlock.ContextMenu (not the real context menu yet!) to ContextMenu to MenuItem to MenuItem.CommandBindings to CommandBinding hierarchy.

The CanExecute and Executed code is for the context menu copy is:

private void RightClickCopyCmdExecuted(object sender, ExecutedRoutedEventArgs e)
  MenuItem mi = (MenuItem)sender;
  var selected = mi.DataContext;
  if (selected != null) Clipboard.SetText(selected.ToString());

private void RightClickCopyCmdCanExecute(object sender, CanExecuteRoutedEventArgs e)
  e.CanExecute = true;

Notice that the sender is the context menu item (copy) and you can get the selected item through the DataContext property, in part because the context menu is associated with the selected item in the ListBox.

The complexity of WPF is always a little bit unnerving. I’m rarely completely sure I have done things the most efficient way and sometimes I’m not even sure my code is completely correct (meaning obscure scenarios where the WPF code will fail), something I pride myself on. Anyway, all in all a very interesting problem.

This entry was posted in Software Test Automation. Bookmark the permalink.