wpf - 将 XAML 行为附加到相同类型的所有控件

标签 wpf xaml attachedbehaviors

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

<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} 类型的控件。

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

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

很遗憾,这不适用于 Triggers:

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

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

<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 或类似的东西?

编辑

我在这里看到了一个类似的问题:Interaction Triggers in Style in ResourceDictionary WPF

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

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

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

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

这个也不行,还在提示:

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

编辑 2

这里是 GotFocusCommand 信息。它设置绑定(bind)了 TextBlockstring 的值。

这是在我的 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}" />

最佳答案

有几种方法可以做你想做的事。一个例子:

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 一样。然后像这样使用它:

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

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

然后是任何

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

会在TextBox的数据上下文中调用GotFocusCommand,参数为“测试消息”。

关于wpf - 将 XAML 行为附加到相同类型的所有控件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36555034/

相关文章:

windows-phone-7 - 图像文件不显示

wpf - 我什么时候应该使用 FrameworkPropertyMetadata 或 UIPropertyMetadata 而不是普通的 PropertyMetadata?

c# - 具有自定义依赖属性的 UserControl 的问题

c# - 如何将 ObservableCollection 绑定(bind)到 ListBox?

c# - 将 SemanticZoom 内的 GridView 从 SemanticZoom 外滚动到某个组

wpf - 如何修复 AttachedBehavior 上的 DependencyPropertyDescriptor AddValueChanged 内存泄漏?

c# - 如何为 FlowDocumentScrollViewer 的自动滚动创建附加行为

.net - 使用命令行参数自定义容器配置

c# - 当属性值在 setter 内更改时更新 View

WPF MenuItem 标题和 HeaderTemplate