WPF - Настройте фокус при нажатии кнопки - Без кода
Есть ли способ установить Focus
из одного элемента управления в другой с помощью WPF Trigger
s?
Как в следующем примере:
<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.
Ответы
Ответ 1
Рассматривали ли вы использование приложенного поведения. Они просты в реализации и используют 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" />
Ответ 2
Я не нахожусь рядом с визуальной студией, поэтому я не могу попробовать это прямо сейчас, но, черт побери, вы должны быть в состоянии сделать что-то вроде этого:
FocusManager.FocusedElement="{Binding ElementName=txtName}">
Edit:
Здесь есть дополнительный вопрос (задаваемый совсем недавно): Как настроить автофокус только в xaml?, который содержит этот метод, и несколько разных идей о том, как его использовать.
Ответ 3
Вы также можете использовать поведение 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.
Надеюсь, это поможет кому-то.
Ответ 4
вам понадобится TriggerAction
, чтобы вызвать метод Focus() на нужном элементе управления.
public class SetFocusTrigger : TargetedTriggerAction<Control>
{
protected override void Invoke(object parameter)
{
if (Target == null) return;
Target.Focus();
}
}
Чтобы настроить фокус на элемент управления, вы размещаете коллекцию триггеров после 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>
Ответ 5
Это то, что вы хотите?
<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>
С#:
public void MoveFocusOnClick(object sender, RoutedEventArgs e)
{
Keyboard.Focus(txtName); // Or your own logic
}
Ответ 6
Это то же самое, что и решение Яна Оукса, но я внес пару небольших изменений.
- Тип кнопки может быть более общим, а именно
ButtonBase
, для обработки большего количества случаев, например ToggleButton
.
- Тип цели также может быть более общим, а именно
UIElement
. Технически, это может быть IInputElement
, я полагаю.
- Сделайте обработчик событий статическим, чтобы он не генерировал закрытие среды выполнения при каждом его использовании.
- [edit: 2019] Обновлен для использования нулевого условного синтаксиса и тела выражения С# 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}}" />
Обратите внимание, что событие может нацеливать/фокусировать саму кнопку-источник.
Ответ 7
Посмотрите, используете ли вы какой-либо Диспетчер, тогда это будет полезно, но это короткий трюк, который я использовал в своем коде. Просто используйте событие Loaded в вашем XAML и создайте новый обработчик. В этот обработчик вставьте этот код и бинго! ты готов идти
Ваше загруженное событие с некоторыми аргументами здесь...
{
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Normal, new System.Action(() => {
The element to be focused goes here......
}));
}
PS: требуется Windows. Threading, если вы не знали;)