Search code examples
c#wpfcomboboxcopy-paste

Overriding ApplicationCommands.Copy for editable WPF ComboBox


I am overriding ApplicationCommands.Copy on a number of controls and have had success, but cannot figure out how to get it to work with an editable ComboBox in WPF. For example, a TextBox works fine like this:

 var copyCommandBinding = new CommandBinding(ApplicationCommands.Copy, CopyToClipboardExecuted);
 MyTextBox.CommandBindings.Add(copyCommandBinding); // Works as expected

When I try to do the same for an editable ComboBox, it doesn't work, even if I return true for the CanExcute:

 var copyCommandBinding = new CommandBinding(ApplicationCommands.Copy, CopyToClipboardExecuted);
 MyComboBox.CommandBindings.Add(copyCommandBinding);  // Nothing happens

I have worked around this by instead adding a KeyUp event handler and testing for CTRL+C, which doesn't feel like the correct solution:

 public MyWindow()
 {
      MyComboBox.KeyUp += MyComboBox_KeyUp;
 }

 private void MyComboBox_KeyUp(object sender, KeyEventArgs e)
 {
      if (!Keyboard.IsKeyDown(Key.LeftCtrl) && !Keyboard.IsKeyDown(Key.RightCtrl)) return;
      if (e.Key == Key.C) CopyToClipboardExecuted();
 }

I do know that adding a TextChanged event handler on an editable ComboBox requires some extra work to tap into the TestBoxBase's event, so I'm wondering if there is some similar trick to getting the ApplicationCommands.Copy support for the same TextBoxBase. I just can't seem to find any documentation covering this detail.

Any gueses, help or hints is appreciated, thanks!


Solution

  • My assumption was correct and a little more digging yielded the answer. To access the TextBoxBase of the ComboBox for this purpose, you have to use the Template.FindName() method. The documentation shows that this is findable as "PART_EditableTextBox." You can also use this found reference for setting up event handlers on the TextBoxBase. The only catch is that you can only do so after the control has been rendered:

    private void MyWindow_ContentRendered(object sender, EventArgs e)
    {
        // Intercept Copy at Window
        CommandBinding copyCommandBinding = new CommandBinding(ApplicationCommands.Copy, CopyToClipboardExecuted);
        CommandBindings.Add(copyCommandBinding);
    
        // Intercept Copy within TextBox
        MyTextBox.CommandBindings.Add(copyCommandBinding);
    
        // Intercept Copy within ComboBox
        TextBox comboTextBox = MyTextBox.Template.FindName("PART_EditableTextBox", MyTextBox) as TextBox;
        comboTextBox.CommandBindings.Add(copyCommandBinding);
        comboTextBox.TextChanged += MyComboBox_TextChanged;
    }