WPF中ResourceDictionary中的Style交互触发器

15

我有一个ComboBox,需要在我的应用程序中的多个位置使用,所以我在ResourceDictionary中设置了该ComboBox的大部分属性,并在需要时将其用作样式。

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>

我正在使用它作为我的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>

我希望将交互触发代码也移动到ResourceDictionary中,这样我就不需要在所有的XAML文件中编写它了。有什么方法可以实现吗?


http://stackoverflow.com/questions/14986045/wpf-how-to-add-blend-interaction-trigger-to-style-resource - Heena
2个回答

16
据我所知,Interaction.Triggers 无法分别在样式和资源字典中应用。但是你可以这样做,通过将 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>

x:Shared="True"时,默认情况下,一个样式适用于所有元素 - 在这种情况下,系统会在重复的Content上出问题。当x:Shared="False"时,每个元素都会创建一个样式,只有在其请求时才会创建。引用自MSDN

当设置为false时,修改了WPF资源检索行为,以使对属性资源的请求为每个请求创建一个新实例,而不是共享同一实例。

欲了解更多信息,请参见:

MSDN:x:Shared属性

编辑:备选解决方案

在这里,Vspivak先生发表了一种解决方案,允许您轻松设置Style中的Interaction.Triggers

例如:

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

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

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

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

更多信息,请参见此链接:

在WPF/Silverlight样式中使用交互行为和操作


如果我能将其用作应用程序资源,那么我才能从中受益。在这里,window.resources对我来说没有太多意义,因为一个窗口只有一次,但是几个窗口都将拥有此组合框。但是我无法将其用作App.Resources。请给予建议。 - Manvinder
@MegaMind:是的,在这种情况下,您可以在App.xaml中定义它。 Window.Resources是相同的资源字典,仅为演示目的选择了它,只是测试一下。 - Anatoliy Nikolaev
1
它有效。使用内容控件而不是组合框是非常聪明的举动。如果可能的话,更详细地阐述一下使用ContentControl而不是ComboBox的原因,这样可以帮助理解这个东西的实际工作方式。谢谢。 - Manvinder
@AnatoliyNikolaev 我的WPF窗口中有多个控件,我想让它们共用一个交互触发器,这是否可行? - Meer

2

我通常使用Silverlight,因此不确定以下方法在WPF中是否可行: 您可以将您的xaml放入一个UserControl中,例如BranchSelection.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>

并像这样使用它:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接