6

I have Grid with multiple Textboxes. Depending on actions the user might take focus should be changed to one of the textboxes. My current solution uses a string property in the ViewModel and a data trigger in xaml to change focus. It works nicely but it seems a rather roundabout way to achieve this so I was wondering if it could be done in a clearner way?

    <Grid.Style>
        <Style TargetType="Grid">
            <Style.Triggers>
                <DataTrigger Binding="{Binding FocusedItem}" Value="number">
                    <Setter Property="FocusManager.FocusedElement" Value="{Binding ElementName=number}"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding FocusedItem}" Value="name">
                    <Setter Property="FocusManager.FocusedElement" Value="{Binding ElementName=name}"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding FocusedItem}" Value="id">
                    <Setter Property="FocusManager.FocusedElement" Value="{Binding ElementName=id}"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Grid.Style>

As you can see the value of the property and the name of the element is the same so I would like to do this i a single trigger instead of having one trigger per element.

Maybe someone can come up with a cleaner way?

Thanks in advance

Farawin
  • 1,375
  • 2
  • 14
  • 19
  • could i ask you why you do set the focus this way? because the user could also Tab by `TabIndex` so you only need to set the focus one time – WiiMaxx Mar 12 '13 at 14:31
  • 1
    as long as this solution works for you, you should take it – blindmeis Mar 12 '13 at 14:37
  • 2
    Personally I think Focus is a UI-specific concept, so I would put all my focus handling in the code-behind the View and not in my ViewModel (unless focus had some specific meaning in the business logic). – Rachel Mar 12 '13 at 14:45
  • @WiiMaxx This is a requested feature by my user group. Personally I agree with you :) – Farawin Mar 12 '13 at 14:49
  • 1
    @blindmeis If I never try to improve I will not learn anything and thus never get any better, – Farawin Mar 12 '13 at 14:50
  • @Rachel: just think of a save button - error msg from viewmodel - Name property is mandatory - now the viewmodel should set the focus to the control which is bind to Name property. – blindmeis Mar 12 '13 at 14:51
  • @Rachel But if the action is triggered by a change in the VM it makes sense to try to achieve it with data binding. I could have the VM call the UI (through an event or delegate) and use the code-behind I guess. – Farawin Mar 12 '13 at 14:52
  • @farawin dont make things harder as long as you dont need to. you will re-think your solution as far as you use usercontrol with control within where you wanna set focus to from the parent datacontext. btw i use attached behavior which set foucs to the control where the property is bind to. but this has also its drawbacks :) – blindmeis Mar 12 '13 at 14:54
  • @blindmeis True. But I'm still curious if it can be done better – Farawin Mar 12 '13 at 14:55
  • @Farawin As an example, if I wanted to set Focus on whatever control failed validation, I'd probably just attach a click event to the Button which finds the first control with a validation error and set focus on it. Although ideally I wouldn't even enable the Save button if there are validation errors :) – Rachel Mar 12 '13 at 14:55

1 Answers1

4

The way I handled setting focus in one of my projects was by using a focus extension (I apologize I do not remember where I saw the original post this is from).

    public static class FocusExtension
    {
        public static bool GetIsFocused(DependencyObject obj)
        {
           return (bool)obj.GetValue(IsFocusedProperty);
        }


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


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


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

And then in xaml file I use it as a Dependency Property:

<TextBox Uid="TB1" FontSize="13" localExtensions:FocusExtension.IsFocused="{Binding Path=TB1Focus}" Height="24" HorizontalAlignment="Left" Margin="113,56,0,0" Name="TB_UserName" VerticalAlignment="Top" Width="165" Text="{Binding Path=TB1Value, UpdateSourceTrigger=PropertyChanged}" />

You can then use a binding to set the focus.

Heinrich
  • 2,144
  • 3
  • 23
  • 39
  • 1
    Thanks. I've thought about using a solution like this one but decided against it as it couples my VM too strongly to the actual UI. I'm starting to think that code-behind is probably the way to go after all. – Farawin Mar 14 '13 at 07:39
  • Your textbox also needs to have Focusable="True" set on it, otherwise this will not work – krilovich Dec 03 '13 at 20:23
  • 1
    The original post I believe is http://stackoverflow.com/questions/1356045/set-focus-on-textbox-in-wpf-from-view-model-c-wpf – Richard Mitchell Oct 09 '14 at 07:37