3

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 ;)

Community
  • 1
  • 1
WiiMaxx
  • 5,322
  • 8
  • 51
  • 89
  • *[...] but after that it doesn't set the Focus anymore [...]* - Do you mean that you cannot toggle the focus state or do you mean, that after the control lost the focus, the control doesn't gain the focus again? – DHN Jun 06 '13 at 14:37
  • @DHN after the control lost the focus, the control doesn't gain the focus again – WiiMaxx Jun 06 '13 at 14:38
  • @DHN i raise the `PropertyChanged` in my `OnClick()` :o) – WiiMaxx Jun 06 '13 at 14:45
  • Yeah I saw this a bit too late. ;o) Odd implemention...but well at least it should work. But you should reconsider that approach. – DHN Jun 06 '13 at 14:46

3 Answers3

7

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.

Viv
  • 17,170
  • 4
  • 51
  • 71
  • that also doesn't work it looks like the Binding expired or something like that because it doesn't get called again – WiiMaxx Jun 07 '13 at 05:16
  • also the `UieOnLostFocus()`get called but doesn't change the value in my ViewModel – WiiMaxx Jun 07 '13 at 05:20
  • @WiiMaxx you need to make the Binding `Mode=TwoWay` or updates are not gonna get applied. I've also updated my answer with a download link – Viv Jun 07 '13 at 07:00
  • mhh crazy. As i test it before with `Mode=TwoWay` it doesn't o.O , but now it works fine – WiiMaxx Jun 07 '13 at 07:21
  • @WiiMaxx yeh no idea how your test was structured so can't really comment on it. Hence why I attached the download. You should maybe edit your question and remove your edit now since that might be misleading for future readers of this thread. – Viv Jun 07 '13 at 07:28
  • 1
    @Viv saved me after 6 years im really happy and grateful thanks – Avinash Reddy Apr 04 '19 at 07:54
1

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

Sasha
  • 833
  • 1
  • 20
  • 40
  • it's what Viv already suggested in his approach did but it doesn't work it looks like the Binding expired (but i didn't get an BindingError) – WiiMaxx Jun 07 '13 at 05:51
  • change IsFocused = true to IsFocused = ! IsFocused and you will see, that the problem is that you are not setting focus to false . – Sasha Jun 07 '13 at 06:40
1

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;
Thai Anh Duc
  • 524
  • 6
  • 12
  • +1 wow that is simple :D _(but as side note the Viv's answer is better because i get notified if the UIElement lost his Focus)_ – WiiMaxx Jun 07 '13 at 07:57