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

发布于 2024-08-20 12:01:03 字数 1008 浏览 7 评论 0原文

有没有一种方法可以使用 WPF 触发器将焦点从一个控件设置到另一个控件?

就像下面的例子:

<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 之外执行此操作的文档。

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

Like the following example:

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

Is there a way for this EventTrigger to put to focus on the textBox "txtName"?

I am trying to find the way to do something like this using strict MVVM. If this is something that should not be done via the XAML (in MVVM) then that is fine. But I would like to see some kind of documentation as to how it fit in the MVVM pattern doing it outside the XAML.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

许你一世情深 2024-08-27 12:01:03

您是否考虑过使用附加行为。它们很容易实现和使用 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" />

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" />
oО清风挽发oО 2024-08-27 12:01:03

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

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

编辑:

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

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.

心的位置 2024-08-27 12:01:03

您还可以使用 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 继承的任何控件上重用。

希望这对某人有帮助。

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.

愁以何悠 2024-08-27 12:01:03

您需要一个 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>

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>
把梦留给海 2024-08-27 12:01:03

这是你想要的吗?

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

Is this what you want?

    <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
    }
魂归处 2024-08-27 12:01:03

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

  1. 按钮类型可以更通用,即ButtonBase,以处理更多情况,例如ToggleButton
  2. 目标类型也可以更通用,即UIElement。从技术上讲,我想这可能是 IInputElement
  3. 将事件处理程序设为静态,这样每次使用它时就不会生成运行时闭包。
  4. [编辑:2019] 更新为使用 null 条件和 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}}" />

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

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.

听风吹 2024-08-27 12:01:03

看看您是否使用任何调度程序,那么它会很有帮助,但这是我在代码中使用的一个简短技巧。只需在 XAML 中使用 Loaded 事件并创建一个新的处理程序。在该处理程序中粘贴此代码并宾果游戏!您已准备好开始

您的已加载事件,其中包含一些参数...

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

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

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文