WPF - Set Focus when a button is clicked - No Code Behind

前端 未结 7 1263
小蘑菇
小蘑菇 2020-11-29 03:53

Is there a way to set Focus from one control to another using WPF Triggers?

Like the following example:



        
相关标签:
7条回答
  • 2020-11-29 03:55

    I'm not near visual studio so I can't actually try this right now, but off the top of my head, you should be able to do something like this:

    FocusManager.FocusedElement="{Binding ElementName=txtName}">
    

    Edit:

    There is a followup question (asked more recently) about this here: How to set autofocus only in xaml? which contains this method, and a few different ideas on how to use it.

    0 讨论(0)
  • 2020-11-29 04:05

    This is the same as Ian Oakes' solution, but I made a couple minor changes.

    1. The button type can be more general, namely ButtonBase, to handle more cases, such as ToggleButton.
    2. The target type can also be more general, namely UIElement. Technically, this could be IInputElement, I suppose.
    3. Made the event handler static so that it won't generate a runtime closure every time this is used.
    4. [edit: 2019] Updated to use null-conditional and C#7 expression body syntax.

    Many thanks to Ian.


    public sealed class EventFocusAttachment
    {
        public static UIElement GetTarget(ButtonBase b) => (UIElement)b.GetValue(TargetProperty);
    
        public static void SetTarget(ButtonBase b, UIElement tgt) => b.SetValue(TargetProperty, tgt);
    
        public static readonly DependencyProperty TargetProperty = DependencyProperty.RegisterAttached(
                "Target",
                typeof(UIElement),
                typeof(EventFocusAttachment),
                new UIPropertyMetadata(null, (b, _) => (b as ButtonBase)?.AddHandler(
                    ButtonBase.ClickEvent,
                    new RoutedEventHandler((bb, __) => GetTarget((ButtonBase)bb)?.Focus()))));
    };
    

    Usage is basically the same as above:

    <ToggleButton z:EventFocusAttachment.Target="{Binding RelativeSource={RelativeSource Self}}" />
    

    Note that the event can target/focus the originating button itself.

    0 讨论(0)
  • 2020-11-29 04:09

    you need a TriggerAction to invoke the Focus() method on the desired control.

    public class SetFocusTrigger : TargetedTriggerAction<Control>
    {
     protected override void Invoke(object parameter)
     {
        if (Target == null) return;
    
        Target.Focus();
     }
    }
    

    To have the focus set to a Control , you place a Triggers collection after your LayoutRoot (or any control really), select the event as the trigger, and select the SetFocusTrigger as the class to run. In the SetFocusTrigger declaration, you put the name of the control that you want to receive the focus by using the TargetName property.

    <Button x:Name="LayoutRoot" >
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Clicked">
            <local:SetFocusTrigger TargetName="StartHere"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
    
    <TextBox x:Name="StartHere"/>
    </Button>
    
    0 讨论(0)
  • 2020-11-29 04:10

    You could also use a WPF Behavior...

        public class FocusElementAfterClickBehavior : Behavior<ButtonBase>
    {
        private ButtonBase _AssociatedButton;
    
        protected override void OnAttached()
        {
            _AssociatedButton = AssociatedObject;
    
            _AssociatedButton.Click += AssociatedButtonClick;
        }
    
        protected override void OnDetaching()
        {
            _AssociatedButton.Click -= AssociatedButtonClick;
        }
    
        void AssociatedButtonClick(object sender, RoutedEventArgs e)
        {
            Keyboard.Focus(FocusElement);
        }
    
        public Control FocusElement
        {
            get { return (Control)GetValue(FocusElementProperty); }
            set { SetValue(FocusElementProperty, value); }
        }
    
        // Using a DependencyProperty as the backing store for FocusElement.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty FocusElementProperty =
            DependencyProperty.Register("FocusElement", typeof(Control), typeof(FocusElementAfterClickBehavior), new UIPropertyMetadata());
    }
    

    Here is the XAML to use the behavior.

    Include namespaces:

    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 
    xmlns:local="clr-namespace:WpfApplication1"
    

    Attach WPF Behavior to button and bind element you want to set focus to:

    <Button Content="Focus" Width="75">
        <i:Interaction.Behaviors>
            <local:FocusElementAfterClickBehavior FocusElement="{Binding ElementName=CheckBoxComboBox, Mode=OneWay}"/>
        </i:Interaction.Behaviors>
    </Button>
    <ComboBox x:Name="CheckBoxComboBox" HorizontalAlignment="Center" VerticalAlignment="Center" Width="120" Grid.Row="1"/>
    

    So this way you have no code behind and it is reusable on any control that inherits from ButtonBase.

    Hope this helps someone.

    0 讨论(0)
  • 2020-11-29 04:11

    Have you considered using an attached behaviour. They are simple to implement and use AttachedProperty's. Although it still requires code, this code is abstracted away in a class and be reused. They can eliminate the need 'code behind' and are often used with the MVVM pattern.

    Try this one and see if it works for you.

    public class EventFocusAttachment
    {
        public static Control GetElementToFocus(Button button)
        {
            return (Control)button.GetValue(ElementToFocusProperty);
        }
    
        public static void SetElementToFocus(Button button, Control value)
        {
            button.SetValue(ElementToFocusProperty, value);
        }
    
        public static readonly DependencyProperty ElementToFocusProperty =
            DependencyProperty.RegisterAttached("ElementToFocus", typeof(Control), 
            typeof(EventFocusAttachment), new UIPropertyMetadata(null, ElementToFocusPropertyChanged));
    
        public static void ElementToFocusPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            var button = sender as Button;
            if (button != null)
            {
                button.Click += (s, args) =>
                    {
                        Control control = GetElementToFocus(button);
                        if (control != null)
                        {
                            control.Focus();
                        }
                    };
            }
        }
    }
    

    And then in your XAML do something like...

    <Button 
        Content="Click Me!" 
        local:EventFocusAttachment.ElementToFocus="{Binding ElementName=textBox}" 
        />
    <TextBox x:Name="textBox" />
    
    0 讨论(0)
  • 2020-11-29 04:16

    Look if you're using any Dispatcher then it would be helpful but this is a short trick I used in my code. Just use the Loaded event in your XAML and make a new handler. In that handler paste this code and bingo! you're ready to go

    Your loaded event with some arguments here...

    {
        Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Normal, new System.Action(() => {
            The element to be focused goes here......
        }));
    }
    

    PS: It requires Windows. Threading if you didn't know ;)

    0 讨论(0)
提交回复
热议问题