ResourceDictionary WPF 风格的交互触发器

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22321966/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 11:11:15  来源:igfitidea点击:

Interaction Triggers in Style in ResourceDictionary WPF

c#wpfxamlcomboboxresourcedictionary

提问by MegaMind

I have a ComboBoxwhich I need to use in several places in my application, so I set most of the properties of that ComboBoxin ResourceDictionaryand use that as a Style where ever I need it.

我有一个ComboBox,我需要在几个地方在我的应用程序中使用,所以我设置大部分的属性ComboBoxResourceDictionary和使用,作为一个风格在以往任何时候我需要它。

Style for the ComboBoxis:

风格为ComboBox

<Style TargetType="{x:Type ComboBox}" x:Key="ComboBoxBranch"> 
    <Setter Property="ItemsSource" Value="{Binding Branches}"></Setter>
    <Setter Property="DisplayMemberPath" Value="BranchName"></Setter>              
    <Setter Property="SelectedItem" Value="{Binding SelectedBranch}"></Setter>        
</Style>

and I am using it like this in my XAML:

我在我的 XAML 中像这样使用它:

<ComboBox Style="{StaticResource ComboBoxBranch}">
     <i:Interaction.Triggers>
          <i:EventTrigger EventName="SelectionChanged">
             <i:InvokeCommandAction Command="{Binding SelectCustomerCommand}" CommandParameter="{Binding SelectedBranch}" ></i:InvokeCommandAction>
          </i:EventTrigger>
     </i:Interaction.Triggers>
</ComboBox>

I want to move the interaction trigger code as well to ResourceDictionary, so I don't need to write it in all my xamls. Is it possible somehow?

我也想将交互触发代码移动到ResourceDictionary,所以我不需要在我所有的 xamls 中编写它。有可能吗?

采纳答案by Anatoliy Nikolaev

As far as I know, Interaction.Triggerscan not be applied in Style, respectively and in a ResourceDictionary. But you can do so, to determine the ComboBoxas a resource with x:Shared="False"and reference it for ContentControllike this:

据我所知,Interaction.Triggers不能分别应用在 Style 和 ResourceDictionary 中。但是您可以这样做,将其确定ComboBox为资源x:Shared="False"并引用它,ContentControl如下所示:

<Window.Resources>
    <ComboBox x:Key="MyComboBox"
              x:Shared="False"
              ItemsSource="{Binding Branches}"
              DisplayMemberPath="BranchName"
              SelectedItem="{Binding SelectedBranch}">

        <i:Interaction.Triggers>
            <i:EventTrigger EventName="SelectionChanged">
                <i:InvokeCommandAction Command="{Binding SelectCustomerCommand}" />
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </ComboBox>
</Window.Resources>

<Grid>
    <ContentControl Name="MyComboBox1"
                    Width="100"
                    Height="30"
                    HorizontalAlignment="Left"
                    Content="{StaticResource MyComboBox}" />

    <ContentControl Name="MyComboBox2"
                    Width="100"
                    Height="30"
                    HorizontalAlignment="Right"
                    Content="{StaticResource MyComboBox}" />
</Grid>

When x:Shared="True"by default then one Style is common to all - in this case, the system swears on the duplicate Content. When x:Shared="False"when is created Style for each element whenever it its request. Quote from MSDN:

x:Shared="True"默认情况下,当一个 Style 对所有人通用时 - 在这种情况下,系统发誓复制Content. 何时x:Shared="False"为每个元素创建样式,只要它的请求。引自MSDN

When set to false, modifies WPF resource-retrieval behavior so that requests for the attributed resource create a new instancefor each request instead of sharing the same instance for all requests.

设置为false 时,修改 WPF 资源检索行为,以便对属性资源的new instance请求为每个请求创建一个,而不是为所有请求共享相同的实例。

For more information, please see:

有关更多信息,请参阅:

MSDN: x:Shared Attribute

MSDN: x:Shared Attribute

Edit: alternative solution

Edit: alternative solution

Here, Mr.Vspivak published a solution that allows you easily set the Interaction.Triggersin Style.

Here, Mr.Vspivak 发布了一个解决方案,可以让您轻松设置Interaction.Triggersin Style。

Example:

例子:

MainWindow.xaml

MainWindow.xaml

<Window x:Class="StylesInteractivity.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"        
        xmlns:ie="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" 
        xmlns:Core="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions" 
        xmlns:int="clr-namespace:System.Windows.Interactivity" 
        xmlns:si="clr-namespace:StylesInteractivity"
        Title="MainWindow" Height="350" Width="525">

    <Window.Resources>
        <si:ViewModel x:Key="Model" />
    </Window.Resources>

    <Grid>      
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>

        <TextBlock Grid.Column="1" x:Name="_tblock" 
                   Text="Default" 
                   HorizontalAlignment="Center" 
                   VerticalAlignment="Center" 
                   FontSize="24" 
                   FontWeight="Bold" />

        <ListBox ItemsSource="{Binding Source={StaticResource Model}, Path=DataSource}" 
                 Grid.Column="0"
                 HorizontalAlignment="Center" 
                 VerticalAlignment="Center">

            <ListBox.ItemContainerStyle>
                <Style TargetType="ListBoxItem">
                    <Setter Property="FontSize" Value="24"/>
                    <Setter Property="FontWeight" Value="Bold"/>

                    <Setter Property="int:InteractivityItems.Template">
                        <Setter.Value>
                            <int:InteractivityTemplate>
                                <int:InteractivityItems>
                                    <int:InteractivityItems.Behaviors>
                                        <int:FlipOnHover />
                                    </int:InteractivityItems.Behaviors>

                                    <int:InteractivityItems.Triggers>
                                        <ie:EventTrigger EventName="MouseMove">
                                            <Core:ChangePropertyAction PropertyName="Text"
                                                                       TargetObject="{Binding ElementName=_tblock}"
                                                                       Value="{Binding}" />
                                        </ie:EventTrigger>
                                    </int:InteractivityItems.Triggers>
                                </int:InteractivityItems>
                            </int:InteractivityTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </ListBox.ItemContainerStyle>
        </ListBox>
    </Grid>
</Window>

InteractivityHelper.cs

InteractivityHelper.cs

/// <summary>
/// <see cref="FrameworkTemplate"/> for InteractivityElements instance
/// <remarks>Subclassed for forward compatibility, perhaps one day <see cref="FrameworkTemplate"/> </remarks>
/// <remarks>will not be partially internal</remarks>
/// </summary>
public class InteractivityTemplate : DataTemplate
{

}

/// <summary>
/// Holder for interactivity entries
/// </summary>
public class InteractivityItems : FrameworkElement
{
    private List<Behavior> _behaviors;
    private List<TriggerBase> _triggers;

    /// <summary>
    /// Storage for triggers
    /// </summary>
    public List<TriggerBase> Triggers
    {
        get
        {
            if (_triggers == null)
                _triggers = new List<TriggerBase>();
            return _triggers;
        }
    }

    /// <summary>
    /// Storage for Behaviors
    /// </summary>
    public List<Behavior> Behaviors
    {
        get
        {
            if (_behaviors == null)
                _behaviors = new List<Behavior>();
            return _behaviors;
        }
    }

    #region Template attached property

    public static InteractivityTemplate GetTemplate(DependencyObject obj)
    {
        return (InteractivityTemplate)obj.GetValue(TemplateProperty);
    }

    public static void SetTemplate(DependencyObject obj, InteractivityTemplate value)
    {
        obj.SetValue(TemplateProperty, value);
    }

    public static readonly DependencyProperty TemplateProperty =
        DependencyProperty.RegisterAttached("Template", 
        typeof(InteractivityTemplate), 
        typeof(InteractivityItems),
        new PropertyMetadata(default(InteractivityTemplate), OnTemplateChanged));

    private static void OnTemplateChanged(
        DependencyObject d, 
        DependencyPropertyChangedEventArgs e)
    {
        InteractivityTemplate dt = (InteractivityTemplate)e.NewValue;
#if(!SILVERLIGHT)
        dt.Seal();
#endif
        InteractivityItems ih = (InteractivityItems)dt.LoadContent();
        BehaviorCollection bc = Interaction.GetBehaviors(d);
        TriggerCollection tc = Interaction.GetTriggers(d);

        foreach (Behavior behavior in ih.Behaviors)
            bc.Add(behavior);

        foreach (TriggerBase trigger in ih.Triggers)
            tc.Add(trigger);
    }

    #endregion
}

FlipOnHover.cs

FlipOnHover.cs

public class FlipOnHover : Behavior<FrameworkElement>
{
    protected override void OnAttached()
    {
        AssociatedObject.MouseEnter += AssociatedObject_MouseEnter;
        AssociatedObject.MouseLeave += AssociatedObject_MouseLeave;
        Transform t = AssociatedObject.RenderTransform;

        AssociatedObject.RenderTransform = new TransformGroup();
        ((TransformGroup)AssociatedObject.RenderTransform).Children.Add(t);
        ((TransformGroup)AssociatedObject.RenderTransform).Children.Add(new ScaleTransform());
        base.OnAttached();
    }

    void AssociatedObject_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
    {
        ((ScaleTransform)((TransformGroup)AssociatedObject.RenderTransform).Children[1]).ScaleY = 1;
    }

    void AssociatedObject_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
    {
        ((ScaleTransform)((TransformGroup)AssociatedObject.RenderTransform).Children[1]).CenterX = AssociatedObject.ActualWidth / 2;
        ((ScaleTransform)((TransformGroup)AssociatedObject.RenderTransform).Children[1]).CenterY = AssociatedObject.ActualHeight / 2;
        ((ScaleTransform)((TransformGroup)AssociatedObject.RenderTransform).Children[1]).ScaleY=-1;
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        AssociatedObject.MouseEnter -= AssociatedObject_MouseEnter;
        AssociatedObject.MouseLeave -= AssociatedObject_MouseLeave;
    }
}

ViewModel.cs

ViewModel.cs

public class ViewModel
{
    private ObservableCollection<String> _dataSource = new ObservableCollection<string>();

    public ViewModel()
    {
        _dataSource.Add("Cat");
        _dataSource.Add("Dog");
        _dataSource.Add("Mouse");
        _dataSource.Add("Owl");
        _dataSource.Add("Rabbit");
    }

    public IEnumerable<string> DataSource
    {
        get { return _dataSource; }
    }
}

For more info, see this link:

有关更多信息,请参阅此链接:

Using Interactivity Behaviors and Actions in WPF/Silverlight Styles

Using Interactivity Behaviors and Actions in WPF/Silverlight Styles

回答by Martin

I usually work with Silverlight so I'm not sure if the following approach is sensible in WPF: You can pull your xaml into a UserControl, say BranchSelection.xamlfor example:

我通常使用 Silverlight,所以我不确定以下方法在 WPF 中是否合理:您可以将您的 xaml 拉入一个UserControlBranchSelection.xaml例如:

<UserControl x:Class="foobar.BranchSelection">
    <ComboBox 
        ItemsSource="{Binding Branches}"
        DisplayMemberPath="BranchName"
        SelectedItem="{Binding SelectedBranch}">
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="SelectionChanged">
                <i:InvokeCommandAction
                    Command="{Binding SelectCustomerCommand}"
                    CommandParameter="{Binding SelectedBranch}"/>
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </ComboBox>
</UserControl>

And use it like this:

并像这样使用它:

<StackPanel>
    <BranchSelection x:Name="CustomerSelector_1"/>
    <BranchSelection x:Name="CustomerSelector_2"/>
</StackPanel>