WPF - 单击按钮时设置焦点 - 无代码

时间:2010-02-04 23:40:20

标签: c# wpf mvvm focus eventtrigger

有没有办法使用WPF FocusTrigger从一个控件设置为另一个控件?

如以下示例所示:

<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是否有办法专注于textBox“txtName”?

我正在尝试使用严格的MVVM找到这样做的方法。如果这是不应该通过XAML(在MVVM中)完成的,那么这很好。但我希望看到一些文档,说明它如何适合在XAML之外的MVVM模式。

7 个答案:

答案 0 :(得分:30)

您是否考虑过使用附加行为?它们易于实现并使用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" />

答案 1 :(得分:14)

我不在视觉工作室附近所以我现在不能尝试这个,但是在我的头脑中,你应该能够做到这样的事情:

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

编辑:

此处有一个关于此问题的后续问题(最近询问):How to set autofocus only in xaml?包含此方法,以及有关如何使用它的一些不同想法。

答案 2 :(得分:10)

你也可以使用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继承的任何控件。

希望这有助于某人。

答案 3 :(得分:4)

您需要TriggerAction来调用所需控件上的Focus()方法。

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

    Target.Focus();
 }
}

要将焦点设置为Control,请在LayoutRoot(或任何控件)之后放置一个Triggers集合,选择该事件作为触发器,并选择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>

答案 4 :(得分:1)

这是你想要的吗?

    <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
    }

答案 5 :(得分:0)

这与Ian Oakes的解决方案相同,但我做了一些小改动。

  1. 按钮类型可以更通用,即ButtonBase,以处理更多情况,例如ToggleButton
  2. 目标类型也可以更通用,即UIElement。从技术上讲,我想这可能是IInputElement
  3. 我使事件处理程序变为静态,因此每次都不会生成运行时闭包。为了确保它保持静止,我将它移出lambda。
  4. 非常感谢伊恩。

    public sealed class EventFocusAttachment
    {
        [DebuggerStepThrough]
        public static UIElement GetTarget(ButtonBase b) { return (UIElement)b.GetValue(TargetProperty); }
    
        [DebuggerStepThrough]
        public static void SetTarget(ButtonBase b, UIElement target) { b.SetValue(TargetProperty, target); }
    
        public static readonly DependencyProperty TargetProperty =
            DependencyProperty.RegisterAttached("Target",
                                                typeof(UIElement),
                                                typeof(EventFocusAttachment),
                                                new UIPropertyMetadata(null, TargetPropertyChanged));
    
        public static void TargetPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs _)
        {
            ButtonBase bb;
            if ((bb = o as ButtonBase) != null)
                bb.Click += handler;
        }
    
        static void handler(Object o, RoutedEventArgs _)
        {
            UIElement target;
            if ((target = GetTarget((ButtonBase)o)) != null)
                target.Focus();
        }
    };
    

    用法与上述基本相同:

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

    请注意,事件可以定位/聚焦原始按钮本身。

答案 6 :(得分:0)

看看是否正在使用任何Dispatcher,这将很有帮助,但这是我在代码中使用的一个简单技巧。只需在XAML中使用Loaded事件并创建一个新的处理程序即可。在该处理程序中粘贴此代码和宾果游戏!你准备好了

您加载的事件带有一些参数...

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

PS:它需要Windows。如果您不知道,请执行线程;)