ResourceDictionary WPF中样式中的交互触发器

时间:2014-03-11 09:48:17

标签: c# wpf xaml combobox resourcedictionary

我需要在我的应用程序的多个位置使用ComboBox,因此我在ComboBox中设置了ResourceDictionary的大多数属性并将其用作样式我需要它。

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,所以我不需要在所有的xamls中编写它。有可能吗?

2 个答案:

答案 0 :(得分:11)

据我所知,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>

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

  

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

有关详细信息,请参阅:

MSDN: x:Shared Attribute

<强> Edit: alternative solution

Here,Mr.Vspivak发布了一个解决方案,可让您轻松设置样式中的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; }
    }
}

有关详细信息,请参阅此链接:

Using Interactivity Behaviors and Actions in WPF/Silverlight Styles

答案 1 :(得分:1)

我通常使用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>