将 XAML 行为附加到所有相同类型的控件 [英] Attach XAML Behavior to all controls of same type

查看:33
本文介绍了将 XAML 行为附加到所有相同类型的控件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 InvokeCommandAction,它附加到 TextBoxGotFocus 事件,如下所示:

I have an InvokeCommandAction that I have that is attached to the GotFocus event of a TextBox like so:

<TextBox Grid.Row="0"
         Grid.Column="1"
         Width="40"
         HorizontalAlignment="Right">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="GotFocus">
            <i:InvokeCommandAction Command="{Binding GotFocusCommand}" CommandParameter="Enter data [message to be displayed]" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
</TextBox>

它以这种方式工作得很好,但我有几十个具有相同设置的 TextBoxes.与其重复代码(正如我目前为每个代码所做的那样),我希望只将该触发器附加到所有 {x:Type TextBox} 类型的控件上.

It works just fine this way, but I have dozens of TextBoxes with this same setup. Rather than repeating the code (as I am currently doing for every one), I am hoping to just attach that trigger to all controls of type {x:Type TextBox}.

通常,我会在 Resources 部分设置属性,如下所示:

Normally, I would set properties in the Resources section, like this:

<UserControl.Resources>
    <Style TargetType="TextBlock">
        <Setter Property="Padding" Value="5,0,0,0" />
        <Setter Property="VerticalAlignment" Value="Center" />
    </Style>
</UserControl.Resources>

不幸的是,这不适用于触发器:

Unfortunately, this will not work for Triggers:

附加属性触发器"只能应用于派生自DependencyObject"的类型.

The attached property "Triggers" can only be applied to types that are derived from "DependencyObject".

理想情况下,我想做这样的事情:

Ideally, I would like to do something like this:

<UserControl.Resources>
    <Style TargetType="{x:Type TextBox}">
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="GotFocus">
                <i:InvokeCommandAction Command="{Binding GotFocusCommand}" CommandParameter="{Binding Tag}" />
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </Style>
</UserControl.Resources>

然后我只需要设置每个 TextBoxTag 属性来指定要显示的消息.我在正确的轨道上吗?我是否需要更改它以使用 ControlTemplate 或类似的东西?

Where I then just need to set the Tag property of each TextBox to specify the message to be displayed. Am I on the right track? Do I need to change it to use a ControlTemplate or something like that?

编辑

我在这里看到了一个类似的问题:ResourceDictionary WPF 中的交互触发器样式

I have seen a similar question here: Interaction Triggers in Style in ResourceDictionary WPF

阅读该问题的答案后,我尝试了以下操作:

After reading the answers for that question, I tried the following:

<UserControl.Resources>
    <TextBox x:Key="TextBoxWithTag">
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="GotFocus">
                <i:InvokeCommandAction Command="{Binding GotFocusCommand}" CommandParameter="{Binding Path=Tag, RelativeSource={RelativeSource Self}}" />
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </TextBox>
</UserControl.Resources>

然后像这样分配给一个控件:

Then assigning to a control like so:

<ContentControl Grid.Row="0"
                Grid.Column="1"
                Width="40"
                HorizontalAlignment="Right"
                Content="{StaticResource TextBoxWithTag}"
                Tag="test tag" />

这也不管用,还在抱怨:

This also does not work, still complaining about:

附加属性触发器"只能应用于派生自DependencyObject"的类型.

The attached property "Triggers" can only be applied to types that are derived from "DependencyObject".

编辑 2

这是 GotFocusCommand 信息.它设置一个 string 的值,它绑定了一个 TextBlock.

Here is the GotFocusCommand information. It sets the value of a string that has a TextBlock bound to it.

这是在我的 ViewModel 中:

private ICommand _gotFocusCommand;
public ICommand GotFocusCommand
{
    get
    {
        if (_gotFocusCommand == null)
        {
            _gotFocusCommand = new DelegateCommand<string>(TextBoxGotFocus);
        }
        return _gotFocusCommand;
    }
}

private void TextBoxGotFocus(string infoText)
{
    CardInfoText = infoText;
}

然后是 XAML:

<TextBlock Grid.Row="2"
           Grid.Column="0"
           Grid.ColumnSpan="2"
           HorizontalAlignment="Center"
           Text="{Binding CardInfoText}" />

推荐答案

有几种方法可以满足您的需求.一个例子:

There are several ways to do what you want. One example:

public static class UIBehaviors {
    public static readonly DependencyProperty AttachedTriggersProperty = DependencyProperty.RegisterAttached(
        "AttachedTriggers", typeof (EventTriggerCollection), typeof (UIBehaviors), new PropertyMetadata(null, OnAttachedTriggersChanged));

    private static void OnAttachedTriggersChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
        var triggers = Interaction.GetTriggers(d);
        if (e.OldValue != null) {
            foreach (var trigger in (EventTriggerCollection) e.OldValue) {
                triggers.Remove(trigger);
            }
        }
        if (e.NewValue != null) {
            foreach (var trigger in (EventTriggerCollection) e.NewValue) {
                triggers.Add(trigger);
            }
        }
    }

    public static void SetAttachedTriggers(DependencyObject element, EventTriggerCollection value) {
        element.SetValue(AttachedTriggersProperty, value);
    }

    public static EventTriggerCollection GetAttachedTriggers(DependencyObject element) {
        return (EventTriggerCollection) element.GetValue(AttachedTriggersProperty);            
    }
}

public class EventTriggerCollection : Collection<EventTrigger> {

}

这里我们声明了附加属性,它接受一组 EventTrigger(来自交互程序集).设置此属性后,我们只需附加所有这些触发器,就像 i:Interaction.Triggers 一样.然后像这样使用它:

Here we declare attached property which accepts set of EventTrigger (from Interactivity assembly). When this property is set, we just attach all those triggers, like i:Interaction.Triggers will do. Then use it like this:

<Window.Resources>
    <local:EventTriggerCollection x:Shared="False" x:Key="textBoxTriggers">
        <i:EventTrigger EventName="GotFocus">
            <i:InvokeCommandAction Command="{Binding GotFocusCommand}" CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=TextBox}, Path=Tag}" />
        </i:EventTrigger>
    </local:EventTriggerCollection>
    <Style TargetType="{x:Type TextBox}">
        <Setter Property="local:UIBehaviors.AttachedTriggers" Value="{StaticResource textBoxTriggers}"/>
    </Style>
</Window.Resources>

注意我如何绑定到 TextBox.Tag 属性.您不能像在您的问题中那样绑定到它,因为您的数据上下文将是视图模型(使用 GotFocusCommand).另请注意触发器集合如何作为资源字典中的单独元素移动,并为其设置 x:Shared="false".这将导致每次访问此属性时都会创建一组新的触发器,因此每个 TextBox 都将拥有自己的一组触发器.

Note how I bind to TextBox.Tag property. You cannot just bind to it as in your question, because your data context will be view model (with GotFocusCommand). Also note how triggers collection is moved as a separate element in resource dictionary, and x:Shared="false" set for it. This will cause creating new set of triggers each time this property is accessed, so each TextBox will have it's own set of triggers.

然后任何

<TextBox Text="Test" Tag="test message" />

将对 TextBox 的数据上下文调用 GotFocusCommand,以测试消息"为参数.

Will call GotFocusCommand on data context of TextBox, with "test message" as parameter.

这篇关于将 XAML 行为附加到所有相同类型的控件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆