将 XAML 行为附加到所有相同类型的控件
Attach XAML Behavior to all controls of same type
我有一个 InvokeCommandAction
附加到 TextBox
的 GotFocus
事件,如下所示:
<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>
这种方式工作得很好,但我有几十个 TextBox
具有相同设置。而不是重复代码(正如我目前对每个人所做的那样),我希望只将该触发器附加到 {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>
然后我只需要设置每个 TextBox
的 Tag
属性 来指定要显示的消息。我在正确的轨道上吗?我需要更改它以使用 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
信息。它设置绑定了 TextBlock
的 string
的值。
这是我的 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>
请注意我是如何绑定到 TextBox.Tag 属性 的。您不能像在问题中那样绑定到它,因为您的数据上下文将是视图模型(使用 GotFocusCommand)。
另请注意触发器集合如何作为资源字典中的单独元素移动,并为其设置 x:Shared="false" 。这将导致每次访问此 属性 时都会创建一组新的触发器,因此每个 TextBox 都会有自己的一组触发器。
然后任意
<TextBox Text="Test" Tag="test message" />
将在 TextBox 的数据上下文上调用 GotFocusCommand,参数为 "test message"。
我有一个 InvokeCommandAction
附加到 TextBox
的 GotFocus
事件,如下所示:
<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>
这种方式工作得很好,但我有几十个 TextBox
具有相同设置。而不是重复代码(正如我目前对每个人所做的那样),我希望只将该触发器附加到 {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>
然后我只需要设置每个 TextBox
的 Tag
属性 来指定要显示的消息。我在正确的轨道上吗?我需要更改它以使用 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
信息。它设置绑定了 TextBlock
的 string
的值。
这是我的 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>
请注意我是如何绑定到 TextBox.Tag 属性 的。您不能像在问题中那样绑定到它,因为您的数据上下文将是视图模型(使用 GotFocusCommand)。 另请注意触发器集合如何作为资源字典中的单独元素移动,并为其设置 x:Shared="false" 。这将导致每次访问此 属性 时都会创建一组新的触发器,因此每个 TextBox 都会有自己的一组触发器。
然后任意
<TextBox Text="Test" Tag="test message" />
将在 TextBox 的数据上下文上调用 GotFocusCommand,参数为 "test message"。