I have a ComboBox
which I need to use in several places in my application, so I set most of the properties of that ComboBox
in ResourceDictionary
and use that as a Style where ever I need it.
Style for the ComboBox
is:
<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:
<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?
The WPF styling and templating model enables you to specify triggers within your Style. Essentially, triggers are objects that enable you to apply changes when certain conditions (such as when a certain property value becomes true , or when an event occurs) are satisfied.
Triggers are used to create visual effects on controls and framework elements. Triggers are parts of styles and are always defined inside a style. Basically, there are 3 types of triggers, they are: Property Trigger.
A DataTrigger allows you to set property values when the property value of the data object matches a specified Value.
As far as I know, Interaction.Triggers
can not be applied in Style, respectively and in a ResourceDictionary. But you can do so, to determine the ComboBox
as a resource with x:Shared="False"
and reference it for ContentControl
like this:
<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
:
When set to false, modifies WPF resource-retrieval behavior so that requests for the attributed resource create a
new instance
for each request instead of sharing the same instance for all requests.
For more information, please see:
MSDN: x:Shared Attribute
Edit: alternative solution
Here
, Mr.Vspivak published a solution that allows you easily set the Interaction.Triggers
in Style.
Example:
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; }
}
}
For more info, see this link:
Using Interactivity Behaviors and Actions in WPF/Silverlight Styles
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.xaml
for example:
<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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With