WPF — установить фокус при нажатии кнопки — без кода

Есть ли способ установить Focus с одного элемента управления на другой с помощью WPF Triggers?

Как в следующем примере:

<Page
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Grid>  
    <Grid.RowDefinitions>
      <RowDefinition/>
      <RowDefinition/>
      <RowDefinition/>
    </Grid.RowDefinitions>

    <TextBox Name="txtName"></TextBox>    
    <TextBox Grid.Row="1" Name="txtAddress"></TextBox>
    <Button Grid.Row="2" Content="Finish">
        <Button.Triggers>
            <EventTrigger RoutedEvent="Button.Click">

                <!-- Insert cool code here-->  

            </EventTrigger>
        </Button.Triggers>
    </Button>
  </Grid>
</Page>

Есть ли способ для этого EventTrigger сфокусироваться на текстовом поле «txtName»?

Я пытаюсь найти способ сделать что-то подобное, используя строгий MVVM. Если это то, что не следует делать через XAML (в MVVM), то это нормально. Но я хотел бы увидеть какую-то документацию о том, как это вписывается в шаблон MVVM, делая это вне XAML.


person Vaccano    schedule 04.02.2010    source источник


Ответы (7)


Рассматривали ли вы использование привязанного поведения. Они просты в реализации и использовании AttachedProperty. Хотя для этого по-прежнему требуется код, этот код абстрагируется в классе и используется повторно. Они могут устранить потребность в «коде позади» и часто используются с шаблоном MVVM.

Попробуйте этот и посмотрите, работает ли он для вас.

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

А затем в своем XAML сделайте что-то вроде...

<Button 
    Content="Click Me!" 
    local:EventFocusAttachment.ElementToFocus="{Binding ElementName=textBox}" 
    />
<TextBox x:Name="textBox" />
person Ian Oakes    schedule 06.02.2010
comment
Мне это нравится. Я собираюсь попробовать это. - person Vaccano; 06.02.2010
comment
Это отлично сработало. Хотя он использует код, это не код за окном, так что я в порядке. Спасибо! - person Vaccano; 07.02.2010
comment
Это единственное рабочее решение, которое мне удалось использовать с DataTemplate и сфокусировать текстовое поле из ViewModel. Спасибо. - person Alexandru Dicu; 14.05.2013
comment
Похоже, это не работает, если мой TextBox изначально был скрыт, а мой DataTemplate делает его видимым через триггер, а затем пытается установить на него фокус. - person dotNET; 13.11.2016

Я не рядом с визуальной студией, поэтому я не могу попробовать это прямо сейчас, но, по моему мнению, вы сможете сделать что-то вроде этого:

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

Редактировать:

Здесь есть дополнительный вопрос (заданный совсем недавно): Как установить автофокус только в xaml?, который содержит этот метод и несколько разных идей о том, как его использовать.

person caesay    schedule 05.02.2010
comment
Мне это нравится, но мне нужно немного больше кода, чтобы увидеть, как это может работать. Я не смог найти способ установить это с помощью EventTrigger. (Из того, что я могу сказать, раскадровка не может этого сделать.) - person Vaccano; 05.02.2010
comment
@Vaccano - я сильно опоздал к разговору, но если вы очень хотите изменить фокус после события по клику, как вы изначально опубликовали, вам вообще не нужен EventTrigger, вы можете просто установить фокус свойство элемента, на которое указал caesay... после нажатия кнопки фокус изменит фокус на связанный элемент. - person Aaj; 30.04.2012
comment
Здесь он работает точно так, как просил ОП. Хорошая работа, кессей! - person Click Ok; 19.04.2016
comment
Очевидно, это работает внутри DataTemplate, где я сначала устанавливаю для TextBox Visibility значение True, а затем пытаюсь установить на него фокус с помощью вышеуказанного метода. - person dotNET; 13.11.2016

Вы также можете использовать поведение WPF...

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

Вот XAML для использования поведения.

Включить пространства имен:

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

Прикрепите поведение WPF к кнопке и привяжите элемент, на который вы хотите установить фокус:

<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"/>

Таким образом, у вас нет кода, и его можно повторно использовать в любом элементе управления, наследующемся от ButtonBase.

Надеюсь, это поможет кому-то.

person droidalmatter    schedule 01.03.2011
comment
это лучше, чем присоединенное свойство, в том смысле, что оно имеет лучшую поддержку Blend. Однако, если вы не используете смесь, для этого потребуется чертовски много кода xaml. - person Elad Katz; 02.03.2011
comment
Не забудьте также добавить ссылку на System.Windows.Interactivity. - person Lee Oades; 21.04.2011

вам нужен TriggerAction для вызова метода Focus() для нужного элемента управления.

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

    Target.Focus();
 }
}

Чтобы установить фокус на Control , вы помещаете коллекцию Triggers после вашего LayoutRoot (или любого элемента управления), выбираете событие в качестве триггера и выбираете SetFocusTrigger в качестве класса для запуска. В объявлении SetFocusTrigger вы указываете имя элемента управления, для которого хотите получить фокус, используя свойство TargetName.

<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>
person ar.gorgin    schedule 21.06.2015
comment
Это отличное решение - спасибо! Всего одна незначительная ошибка в XAML... EventName должен быть Click вместо Clicked - person Mash; 30.10.2018
comment
В настоящее время можно использовать нулевой оператор распространения, чтобы превратить метод Invoke в однострочный. - person maets; 02.10.2019

Это то, что вы хотите?

    <TextBox Name="txtName"></TextBox>
    <TextBox Grid.Row="1" Name="txtAddress"></TextBox>
    <Button Grid.Row="2" Content="Finish">
        <Button.Style>
            <Style TargetType="{x:Type Button}">
                <EventSetter Event="Click" Handler="MoveFocusOnClick" />
            </Style>
        </Button.Style>
        <!--<Button.Triggers>
            <EventTrigger RoutedEvent="Button.Click">
            </EventTrigger>
        </Button.Triggers>-->
    </Button>

c#:

    public void MoveFocusOnClick(object sender, RoutedEventArgs e)
    {
        Keyboard.Focus(txtName); // Or your own logic
    }
person mg007    schedule 05.02.2010
comment
Да, если я хочу использовать код позади, то есть несколько способов сделать это. Мне было интересно, есть ли способ сделать это без кода. - person Vaccano; 05.02.2010

Это то же самое, что и решение Яна Оукса, но я внес несколько незначительных изменений.

  1. Тип кнопки может быть более общим, а именно ButtonBase, чтобы обрабатывать больше случаев, например ToggleButton.
  2. Целевой тип также может быть более общим, а именно UIElement. Технически, я полагаю, это может быть IInputElement.
  3. Сделал обработчик событий статическим, чтобы он не генерировал замыкание во время выполнения каждый раз, когда это используется.
  4. [править: 2019] Обновлено для использования синтаксиса тела выражения с нулевым условием и C#7.

Большое спасибо Яну.


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

Использование в основном такое же, как и выше:

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

Обратите внимание, что событие может нацеливаться/фокусироваться на самой исходной кнопке.

person Glenn Slayden    schedule 12.06.2015

Посмотрите, если вы используете какой-либо Dispatcher, это было бы полезно, но это короткий трюк, который я использовал в своем коде. Просто используйте событие Loaded в своем XAML и создайте новый обработчик. Вставьте этот код в этот обработчик и бинго! ты готов идти

Ваше загруженное событие с некоторыми аргументами здесь...

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

PS: Требуется Windows. Ветка, если вы не знали ;)

person Yash Joshi    schedule 26.10.2018