2

I have two textBoxes and in my ViewModel I would like to be able to keep track of which box is currently in focus.

<TextBox x:Name="textBox1" Text="Text Box 1"/>
<TextBox x:Name="textBox2" Text="Text Box 2"/>

How can I read/identify which textBox is currently in focus from my ViewModel?

fs_tigre
  • 10,650
  • 13
  • 73
  • 146

3 Answers3

5

There are several ways how you can achieve this, some of them:

1) Use behavior:

  • You need System.Windows.Interactivity.dll
  • Behavior (setting IsFocused property will not make element focused, you need slightly extend behavior in order to achieve this)

    public class FocusChangedBehavior : Behavior<UIElement>
    {
        public static readonly DependencyProperty IsFocusedProperty = 
           DependencyProperty.Register(
           nameof(IsFocused),
           typeof(bool),
           typeof(FocusChangedBehavior),
           new FrameworkPropertyMetadata(default(bool), 
                FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
    
        public bool IsFocused
        {
            get { return (bool)this.GetValue(IsFocusedProperty); }
            set { this.SetValue(IsFocusedProperty, value); }
        }
    
        /// <inheritdoc />
        protected override void OnAttached()
        {
            this.AssociatedObject.GotFocus += this.AssociatedObjectFocused;
            this.AssociatedObject.LostFocus += this.AssociatedObjectUnfocused;
        }
    
        /// <inheritdoc />
        protected override void OnDetaching()
        {
            this.AssociatedObject.GotFocus -= this.AssociatedObjectFocused;
            this.AssociatedObject.LostFocus -= this.AssociatedObjectUnfocused;
        }
    
        private void AssociatedObjectFocused(object sender, RoutedEventArgs e)
        {
            this.IsFocused = true;
        }
    
        private void AssociatedObjectUnfocused(object sender, RoutedEventArgs e)
        {
            this.IsFocused = false;
        }
    }
    
  • In XAML you bind IsFocused to property in ViewModel.

    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"

    <TextBox x:Name="textBox1" Text="Text Box 1">
        <i:Interaction.Behaviors>
            <local:FocusChangedBehavior IsFocused="{Binding IsFocusedTxt1}" />
        </i:Interaction.Behaviors>
    </TextBox>
    
    <TextBox x:Name="textBox2" Text="Text Box 2">
        <i:Interaction.Behaviors>
            <local:FocusChangedBehavior IsFocused="{Binding IsFocusedTxt2}" />
        </i:Interaction.Behaviors>
    </TextBox>
    
  • Finally in View-Model create properties

    public bool IsFocusedTxt1 { get; set; }
    
    public bool IsFocusedTxt2 { get; set; }
    



2) Alternatively you could you use EventTrigger in the XAML

  • You need System.Windows.Interactivity.dll and MicrosoftExpressionInteractions (For the ActionCommand)
  • Event Triggers:

    <TextBox x:Name="textBox1" Text="Text Box 1">
        <i:Interaction.Triggers>
            <i:EventTrigger  EventName="GotFocus">
                <i:InvokeCommandAction Command="{Binding NotifyFocusedReceivedTxt1Command}" />
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </TextBox>
    
  • In ViewModel create command NotifyFocusedReceivedTxt1Command

    public ICommand NotifyFocusedReceivedTxt1Command { get; }
    
    // in constructor
    this.NotifyFocusedReceivedTxt1Command = new ActionCommand(this.FocusedReceivedTxt1);
    
    // and method
    private void FocusedReceivedTxt1()
    {
        // Your logic
    }
    
  • Also, if you don't want introduce many command/properties you could use same command and pass different textboxes by setting CommandParameter (slightly breaks MVVM, but not critically)

    <TextBox x:Name="textBox1" Text="Text Box 1">
        <i:Interaction.Triggers>
            <i:EventTrigger  EventName="GotFocus">
                <i:InvokeCommandAction Command="{Binding NotifyFocusedReceivedCommand}" 
                                       CommandParameter="{Binding ., ElementName=textBox1}" />
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </TextBox>
    
    <TextBox x:Name="textBox2" Text="Text Box 2">
        <i:Interaction.Triggers>
            <i:EventTrigger  EventName="GotFocus">
                <i:InvokeCommandAction Command="{Binding NotifyFocusedReceivedCommand}" 
                                       CommandParameter="{Binding ., ElementName=textBox2}" />
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </TextBox>
    

    and

    public ICommand NotifyFocusedReceivedCommand { get; }
    
    // in constructor
    this.NotifyFocusedReceivedCommand = new ActionCommand(this.FocusedReceived);
    
    // and method
    private void FocusedReceived(object control)
    {
        var txt = (TextBox)control;
        bool isFocused = txt.IsFocused;
        string name = txt.Name;
    }
    
lezhkin11
  • 415
  • 3
  • 6
  • I'm trying to make your first example work for I'm getting an error. This is what I did, 1) Copied your .cs code and created a class `FocusChangedBehavior.cs`. 2) Imported `using System.Windows.Interactivity;` inside the class. 3) Added `xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"` to my XAML file. 4) Added the interaction behavior to mytextBox`` and here is where I get ERROR: `The type local:FocusChangedBehavior was not found. Verify that you are not...` – fs_tigre Aug 20 '18 at 01:41
  • @ lezhkin11- Any idea what I'm I missing? – fs_tigre Aug 20 '18 at 01:41
  • 2
    you need to import namespace for the behavior. `xmlns:local="clr-namespace:NAMESPACE;assembly=ASSEMBLY_WITH_BEHAVIOR"`. If it's same assembly, then you omit assembly attribute. `xmlns:local="clr-namespace:NAMESPACE"`. It can be any alias name (not just local), in my case it's auto-generated – lezhkin11 Aug 20 '18 at 08:02
  • Your first example is now working, thanks a lot for your help! – fs_tigre Aug 20 '18 at 12:34
0
public static DependencyProperty IsFocusedProperty = DependencyProperty.RegisterAttached(
    "IsFocused",
    typeof(bool),
    typeof(TextBoxProperties),
    new UIPropertyMetadata(false,OnIsFocusedChanged)
);

public static bool GetIsFocused(DependencyObject dependencyObject) {
    return (bool)dependencyObject.GetValue(IsFocusedProperty);
}

public static void SetIsFocused(DependencyObject dependencyObject, bool value) {
    dependencyObject.SetValue(IsFocusedProperty, value);
}

you can use this property

William R.
  • 145
  • 1
  • 11
-1

This can not be done via the ViewModel on Server-side, a workaround would look like this:

View Code: (js & html)

function updateFocus(textboxNr) {
  
  $.ajax({
    type: "POST",
    url: '@Url.Action("Index", "Controller")',
    data: {
      Focus: textboxNr
    },
    contentType: "application/json; charset=utf-8",
    dataType: "json",
  });
  
}
<textarea id="1" name="1" onfocus="updateFocus(1)">Text Box 1</textarea>
<textarea id="2" name="2" onfocus="updateFocus(2)">Text Box 2</textarea>
William R.
  • 145
  • 1
  • 11
  • There is also a way to just attach Event Listeners to a DOM Element and so on, but that is easiest with jquery. – William R. Aug 19 '18 at 14:31
  • Another thing, this must be a new controller action that handles the input from the ajax post. And the controller must take a ViewModel as an argument. like: public ActionResult Index(MyModel postData) { ... } – William R. Aug 19 '18 at 14:33
  • 2
    This is a very simple WPF application and I rather not use other languages/technologies. Thanks. – fs_tigre Aug 19 '18 at 16:00
  • Ooooooh.... I see... haha I assumed the markup language was html instead of xml – William R. Aug 20 '18 at 07:43
  • Well, at lead you know how to achieve this with asp.net mvc – William R. Aug 20 '18 at 07:48