3

I have a WPF ComboBox which has its IsEditable property bound to a view model which can switch it on and off. When it is switched on, I want to give focus to the ComboBox and select all the text in the edit TextBox.

I can't see the best to accomplish this. Should I replace the ControlTemplate, subclass the ComboBox base class, and provide the needed properties, use Attached Properties, or some other approach?

codekaizen
  • 26,990
  • 7
  • 84
  • 140

1 Answers1

6

I have a solution for you.

I'm using a combination of Model-View-ViewModel and Attached Property approach.

Firstly, you need to know the Messaging system in MVVM for this to work, as well as know your Commands. So starting with Attached Properties, we begin to set an IsFocused event to the combo box we would like to have focused and all text selected.

  #region Select On Focus

  public static bool GetSelectWhenFocused(DependencyObject obj)
  {
    return (bool)obj.GetValue(SelectWhenFocusedProperty);
  }

  public static void SetSelectWhenFocused(DependencyObject obj, bool value)
  {
    obj.SetValue(SelectWhenFocusedProperty, value);
  }

  // Using a DependencyProperty as the backing store for SelectWhenFocused.  This enables animation, styling, binding, etc...
  public static read-only DependencyProperty SelectWhenFocusedProperty =
      DependencyProperty.RegisterAttached("SelectWhenFocused", typeof(bool), typeof(EditableComboBox), new UIPropertyMetadata(OnSelectOnFocusedChanged));

  public static void OnSelectOnFocusedChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
  {
    bool SetProperty = (bool)args.NewValue;     

    var comboBox = obj as ComboBox;
    if (comboBox == null) return;

    if (SetProperty)
    {
      comboBox.GotFocus += GotFocused;
      Messenger.Default.Register<ComboBox>(comboBox, Focus);
    }
    else
    {
      comboBox.GotFocus -= GotFocused;
      Messenger.Default.Unregister<ComboBox>(comboBox, Focus);
    }
  }

  public static void GotFocused(object sender, RoutedEventArgs e)
  {
    var comboBox = sender as ComboBox;
    if(comboBox == null) return;

    var textBox = comboBox.FindChild(typeof(TextBox), "PART_EditableTextBox") as TextBox;
    if (textBox == null) return;

    textBox.SelectAll();
  }

  public static void Focus(ComboBox comboBox)
  {
    if(comboBox == null) return;
    comboBox.Focus();
  }
  #endregion

What this code shows is when we set the Attached Property SelectWhenFocused to true, it will register to listen for the GotFocused Event and select all the text inside.

To use is simple:

<ComboBox
  IsEditable="True"
  ComboBoxHelper:EditableComboBox.SelectWhenFocused="True"
  x:Name="EditBox" />

Now we need a button that will set the focus on the ComboBox when clicked on.

<Button
  Command="{Binding Focus}"
  CommandParameter="{Binding ElementName=EditBox}"
  Grid.Column="1" >Focus</Button>

Notice how the CommandParameter is binding to the ComboBox by its name EditBox. This is so when the command executes, only this ComboBox gets focused and all text selected.

In my ViewModel, I have the Focus command declared as following:

public SimpleCommand Focus { get; set; }
public WindowVM()
{
  Focus = new SimpleCommand {ExecuteDelegate = x => Broadcast(x as ComboBox)};
}

This is a tested and proven technique that works for me. I hope it's not an overkill solution to your problem. Good luck.

Tri Q Tran
  • 5,500
  • 2
  • 37
  • 58
  • Thanks, Tri. I've implemented this pattern, but I note there is no `FindChild` method on the ComboBox. – codekaizen Jan 28 '10 at 02:22
  • My apologies, FindChild is here: http://stackoverflow.com/questions/636383/wpf-ways-to-find-controls/1501391#1501391 – Tri Q Tran Jan 28 '10 at 02:24
  • Thanks, Tri. I guessed at the implementation after I wondered a bit. Looks like I got pretty close. However, the TextBox child isn't found, and the reason is likely that I have the complicating factor of changing the IsEditable property of the ComboBox, and when the GotFocused event is handled, the ComboBox control template hasn't changed yet, and the TextBox isn't created. Any insight on how to defer the search until after the control template is applied? – codekaizen Jan 28 '10 at 02:34
  • Accepted, since using an Attached Behavior is the approach I eventually took, and appears to ultimately be the most fitting approach for MVVM. – codekaizen Dec 01 '12 at 02:40
  • @TriQTran, what is `PART_EditableTextBox`? – Bigeyes Jan 13 '17 at 18:28
  • @Bigeyes `PART_EditableTextBox` is the name of the editable text box when using the default template of a combo-box. – Tri Q Tran Mar 25 '17 at 11:32