How to Focus an TextBox on Button click

2020-03-29 08:23发布

问题:

So how to Focus an TextBox on Button click using the MVVM Pattern?

i created an simple Testproject Based on this Answer which works on the first click, but after that it doesn't set the Focus anymore. What do i miss?

XAML (View)

<Grid>
    <TextBox Height='23' HorizontalAlignment='Left' Margin='12,12,0,0' VerticalAlignment='Top' Width='120'
             Text='{Binding TheText}'
             local:FocusExtension.IsFocused="{Binding IsFocused}"/>
    <Button Content='Click' Height='23' HorizontalAlignment='Left' Margin='138,11,0,0' VerticalAlignment='Top' Width='75' 
            Command='{Binding ClickCommand}'/>
    <Button Content='Just to deFocus' Height='28' HorizontalAlignment='Left' Margin='14,44,0,0' Name='button1' VerticalAlignment='Top' Width='199' />
</Grid>

ViewModel

public class ViewModel : INotifyPropertyChanged
{
    public string TheText { get; set; }
    public bool IsFocused { get; set; }

    private RelayCommand _clickCommand;
    public ICommand ClickCommand
    {
        get { return _clickCommand ?? (_clickCommand = new RelayCommand(param => this.OnClick())); }
    }
    private void OnClick()
    {
        IsFocused = true;
        RaisePropertyChanged("IsFocused");
    }

    #region INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged;

    public void RaisePropertyChanged(string propName)
    {
        if (this.PropertyChanged != null)
            this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
    }

    #endregion
}

and here is an Download link with an ready to go Project(VS2010) for the lazy ones ;)

回答1:

Your attached property value is never going back to false after the initial default is overwritten. hence your FocusExtension class is not calling Focus() on the TextBox since the PropertyChanged does not need to fire when setting IsFocused in your VM to true.

switch the OnIsFocusedPropertyChanged(...)

from:

private static void OnIsFocusedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    var uie = (UIElement)d;
    if ((bool)e.NewValue)
        uie.Focus(); // Don't care about false values.
}

to

private static void OnIsFocusedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
  var uie = (UIElement)d;
  if (!((bool)e.NewValue))
    return;
  uie.Focus();
  uie.LostFocus += UieOnLostFocus;
}

private static void UieOnLostFocus(object sender, RoutedEventArgs routedEventArgs) {
  var uie = sender as UIElement;
  if (uie == null)
    return;
  uie.LostFocus -= UieOnLostFocus;
  uie.SetValue(IsFocusedProperty, false);
}

Update:

Along with the above change also make sure

local:FocusExtension.IsFocused="{Binding IsFocused}"

is switched to

local:FocusExtension.IsFocused="{Binding IsFocused, Mode=TwoWay}"

Working Download Link

Another Update

To set the Mode=TwoWay as default for this attached property in FocusExtension class switch

public static readonly DependencyProperty IsFocusedProperty = DependencyProperty.RegisterAttached(
  "IsFocused",
  typeof(bool),
  typeof(FocusExtension),
  new UIPropertyMetadata(
    false,
    OnIsFocusedPropertyChanged));

to

public static readonly DependencyProperty IsFocusedProperty = DependencyProperty.RegisterAttached(
  "IsFocused",
  typeof(bool),
  typeof(FocusExtension),
  new FrameworkPropertyMetadata(
    false,
    FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
    OnIsFocusedPropertyChanged));

You can skip explicitly specifying Mode=TwoWay in xaml with the above declaration.



回答2:

You should make a command for lostFocus event, and when focus is lost, set isFocused property to false, then it will be working. Add Interactivity and Interactions librarys into your project, than you will be able to write something like :

 <i:Interaction.Triggers>
    <i:EventTrigger EventName="LostFocus">
        <ei:CallMethodAction TargetObject="{Binding}" MethodName="OnLostFocus"/>
    </i:EventTrigger>
 </i:Interaction.Triggers>

and in your viewModel write:

public void OnLostFocus()
{IsFocused = false;}

and move RaisePropertyChanged to the setter of your property



回答3:

I got the same issue before. I did a trick. In your OnClick method, do something like this:

if(IsFocus == true)
   IsFocus = false;
IsFocus = true;


标签: c# wpf xaml mvvm