Прикрепить поведение XAML ко всем элементам управления одного типа

У меня есть InvokeCommandAction что у меня есть то, что прикреплено к GotFocus событие TextBox вот так:

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

Присоединенное свойство "Триггеры" можно применять только к типам, производным от "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>

Где мне тогда просто нужно установить Tag собственность каждого TextBox указать сообщение для отображения. Я на правильном пути? Нужно ли изменить его, чтобы использовать ControlTemplate или что-то типа того?

РЕДАКТИРОВАТЬ

Я видел подобный вопрос здесь: триггеры взаимодействия в стиле в 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" />

Это также не работает, все еще жалуясь на:

Присоединенное свойство "Триггеры" можно применять только к типам, производным от "DependencyObject".

РЕДАКТИРОВАТЬ 2

Здесь GotFocusCommand Информация. Он устанавливает значение string это имеет TextBlock связаны с этим.

Это по моему 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}" />

1 ответ

Решение

Есть несколько способов сделать то, что вы хотите. Один пример:

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 (из сборки Interacctivity). Когда это свойство установлено, мы просто присоединяем все эти триггеры, как это делает 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" />

Вызовет GotFocusCommand для контекста данных TextBox с параметром "тестовое сообщение".

Другие вопросы по тегам