为什么当我的触发器触发时我的故事板操作会被跳过
Why is my story board actions skipped when my trigger triggers
我有一个 Tabcontrol,我正在尝试添加基于触发器的操作。
使用路由事件我可以让我的故事板触发并且我的动作按预期响应。现在,如果选择 header,我试图阻止我的故事板操作针对特定选项卡 header 触发。
这迫使我放弃路由事件,因为我无法使用 属性 条件和路由事件来确定我的故事板是否会执行。
快进,现在我终于能够让 IsMouseOver 响应我希望的鼠标。 (当鼠标进入和离开 tabitem header 时,我的背景属性会发生变化和变回。我想我快到了,但是只要我添加与之前完全相同的故事板,我的代码就决定偷懒并跳过它出于某种原因。背景 setter 仍在触发,但情节提要保持沉默。
我尝试删除 setter 但故事板仍然没有触发。
一天结束:我希望为“鼠标悬停”和“被选中”的所有可能组合设置在彼此之间很好地过渡的样式。
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Tenant_Tool_Analytics_Module.Resources.Components"
xmlns:controls="clr-namespace:Tenant_Tool_Analytics_Module.Resources.Components">
<Style TargetType="{x:Type controls:NavigationElement}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:NavigationElement}">
<Border BorderBrush="Black" BorderThickness=".7" x:Name="Bd">
<Grid Width="150" Height="50" Opacity="0.75" x:Name="NavigationElementGrid">
<Grid.Background >
<RadialGradientBrush GradientOrigin="0.5,0.5" Center="0.5,0.5" RadiusX="0.5" RadiusY="2.0">
<GradientStop Color="#006A4D" Offset="1.0"/>
<GradientStop Color="#56be88" Offset=".2"/>
</RadialGradientBrush >
</Grid.Background>
<ContentControl Grid.Column="0" Content="{TemplateBinding Icon}"/>
<TextBlock x:Name="NavigationElementText" Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Left" Text="{TemplateBinding LabelText}" FontFamily="Futura XBlkIt BT" FontSize="12"/>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
</Grid.RowDefinitions>
</Grid>
</Border>
<!--
Trigger related to when the mouse is over the header
I would like it to Execute the doubleanimations
-->
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
<!-- Missing condition: If selected = false -->
</MultiTrigger.Conditions>
<!-- Start BUG the bellow code does not execute -->
<MultiTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="NavigationElementGrid" Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:0.3"/>
<DoubleAnimation Storyboard.TargetName="NavigationElementText" Storyboard.TargetProperty="FontSize" To="14" Duration="0:0:0.3"/>
</Storyboard>
</BeginStoryboard>
</MultiTrigger.EnterActions>
<!-- End BUG -->
<!-- The triggers are fireing becuase this is being set. -->
<Setter Property="Background" TargetName="Bd" Value="Blue"/>
</MultiTrigger>
<!--
When the mouse leaves I want it to return to it's original
state.
-->
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="False"/>
<!-- Missing condition: If selected = false -->
</MultiTrigger.Conditions>
<!-- Start BUG the bellow code does not execute -->
<MultiTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="NavigationElementGrid" Storyboard.TargetProperty="Opacity" To=".75" Duration="0:0:0.3"/>
<DoubleAnimation Storyboard.TargetName="NavigationElementText" Storyboard.TargetProperty="FontSize" To="12" Duration="0:0:0.3"/>
</Storyboard>
</BeginStoryboard>
</MultiTrigger.EnterActions>
<!-- End BUG -->
<!-- The triggers are fireing becuase this is being set. -->
<Setter Property="Background" TargetName="Bd" Value="Red"/>
</MultiTrigger>
<!-- Missing two more MultiTriggers (very similar to above) for the cases of if the tab is selected.-->
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
背后的代码
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace Tenant_Tool_Analytics_Module.Resources.Components
{
public class NavigationElement : Control
{
public string LabelText
{
get
{
return (string)GetValue(LabelTextProperty);
}
set
{
SetValue(LabelTextProperty, value);
}
}
public static readonly DependencyProperty LabelTextProperty =
DependencyProperty.Register("LabelText", typeof(string), typeof(NavigationElement), new PropertyMetadata(string.Empty));
public object Icon
{
get
{
return (object)GetValue(IconProperty);
}
set
{
SetValue(IconProperty, value);
}
}
public static readonly DependencyProperty IconProperty =
DependencyProperty.Register("Icon", typeof(object), typeof(NavigationElement), new PropertyMetadata(string.Empty));
public System.Windows.Media.Brush BackgroundColour
{
get
{
return (System.Windows.Media.Brush)GetValue(BackgroundColourProperty);
}
set
{
SetValue(BackgroundColourProperty, value);
}
}
public static readonly DependencyProperty BackgroundColourProperty =
DependencyProperty.Register("BackgroundColour", typeof(System.Windows.Media.Brush), typeof(NavigationElement), new PropertyMetadata(Brushes.Black));
}
}
实现代码
<Window x:Class="Tenant_Tool_Analytics_Module.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Tenant_Tool_Analytics_Module"
xmlns:views="clr-namespace:Tenant_Tool_Analytics_Module.Views"
xmlns:controls="clr-namespace:Tenant_Tool_Analytics_Module.Resources.Components"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<DockPanel LastChildFill="True">
<views:HeaderView x:Name="HeaderView" DockPanel.Dock="Top"/>
<TabControl DockPanel.Dock="Left" TabStripPlacement="Left" Margin="0, 0, 0, 10">
<TabItem Padding="0">
<TabItem.Header>
<controls:NavigationElement LabelText="Stacking Plan" Icon="{StaticResource StackPlanIcon}"/>
</TabItem.Header>
<Button Content="HI"/>
</TabItem>
<TabItem Padding="0">
<TabItem.Header>
<controls:NavigationElement LabelText="Tenant Profile" Icon="{StaticResource TenantProfileIcon}"/>
</TabItem.Header>
<Button Content="HI"/>
</TabItem>
<TabItem Padding="0">
<TabItem.Header>
<controls:NavigationElement LabelText="Submarket" Icon="{StaticResource SubmarketIcon}"/>
</TabItem.Header>
<Button Content="HI"/>
</TabItem>
<TabItem Padding="0">
<TabItem.Header>
<controls:NavigationElement LabelText="Industry" Icon="{StaticResource IndustryIcon}"/>
</TabItem.Header>
<Button Content="HI"/>
</TabItem>
</TabControl>
</DockPanel>
</Window>
我认为您没有意识到 MultiDataTrigger
对象同时具有 EnterActions
和 ExitActions
这一事实。您可以只使用一个 MultiDataTrigger
在 EnterActions
上触发 true
而另一个 MultiDataTrigger
在 EnterActions
上触发 false
MultiDataTrigger
在 true
上触发,同时 EnterActions
(将对象更改为异常状态)和 ExitActions
将其转换回正常状态。
Triggers
合集现在可以按预期运行,并且更易于阅读:
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
<!-- Missing condition: If selected = false -->
</MultiTrigger.Conditions>
<MultiTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="NavigationElementGrid"
Storyboard.TargetProperty="Opacity"
To="1"
Duration="0:0:0.3"/>
<DoubleAnimation Storyboard.TargetName="NavigationElementText"
Storyboard.TargetProperty="FontSize"
To="14" Duration="0:0:0.3"/>
<ColorAnimationUsingKeyFrames Storyboard.TargetName="BorderBackgroundBrush"
Storyboard.TargetProperty="Color">
<DiscreteColorKeyFrame KeyTime="0" Value="Blue"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</MultiTrigger.EnterActions>
<MultiTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="NavigationElementGrid"
Storyboard.TargetProperty="Opacity"
To=".75"
Duration="0:0:0.3"/>
<DoubleAnimation Storyboard.TargetName="NavigationElementText"
Storyboard.TargetProperty="FontSize"
To="12"
Duration="0:0:0.3"/>
<ColorAnimationUsingKeyFrames Storyboard.TargetName="BorderBackgroundBrush"
Storyboard.TargetProperty="Color">
<DiscreteColorKeyFrame KeyTime="0" Value="Red"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</MultiTrigger.ExitActions>
</MultiTrigger>
<!-- Missing two more MultiTriggers (very similar to above) for the cases of if the tab is selected.-->
</ControlTemplate.Triggers>
另请注意我是如何使用 ColorAnimationUsingKeyFrames
来更改 Border.Background
属性 而无需在另一个 Trigger
中使用 Setter
。这样,所有更改都在同一个 Storyboard
中执行。为此,您只需将命名的 SolidColorBrush
分配给您的 "Bd"
Border
:
<Border.Background>
<SolidColorBrush x:Name="BorderBackgroundBrush" Color="Red"></SolidColorBrush>
</Border.Background>
为了防止 Storyboard
在选择了祖先 TabItem
的情况下播放,我建议您在 NavigationElement
中添加一个布尔值 IsSelected
DependencyProperty
,这样您就可以通过在 Style
中添加 Setter
来将此 属性 绑定到其 TabItem
祖先,如下所示:
<Setter Property="IsSelected" Value="{Binding IsSelected, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=TabItem}}"/>
您只需在 MultiDataTrigger
中添加条件(但您已经知道了):
<Condition Property="IsMouseOver" Value="True"/>
<Condition Property="IsSelected" Value="False"/>
旁注:我建议您换行并缩进您的 XAML 属性以避免长 XAML 行迫使您滚动。除了提高可读性之外,将每个 XAML 属性放在新行上对版本控制更友好,因为一个属性更改只会影响一行。
我有一个 Tabcontrol,我正在尝试添加基于触发器的操作。
使用路由事件我可以让我的故事板触发并且我的动作按预期响应。现在,如果选择 header,我试图阻止我的故事板操作针对特定选项卡 header 触发。
这迫使我放弃路由事件,因为我无法使用 属性 条件和路由事件来确定我的故事板是否会执行。
快进,现在我终于能够让 IsMouseOver 响应我希望的鼠标。 (当鼠标进入和离开 tabitem header 时,我的背景属性会发生变化和变回。我想我快到了,但是只要我添加与之前完全相同的故事板,我的代码就决定偷懒并跳过它出于某种原因。背景 setter 仍在触发,但情节提要保持沉默。
我尝试删除 setter 但故事板仍然没有触发。
一天结束:我希望为“鼠标悬停”和“被选中”的所有可能组合设置在彼此之间很好地过渡的样式。
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Tenant_Tool_Analytics_Module.Resources.Components"
xmlns:controls="clr-namespace:Tenant_Tool_Analytics_Module.Resources.Components">
<Style TargetType="{x:Type controls:NavigationElement}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:NavigationElement}">
<Border BorderBrush="Black" BorderThickness=".7" x:Name="Bd">
<Grid Width="150" Height="50" Opacity="0.75" x:Name="NavigationElementGrid">
<Grid.Background >
<RadialGradientBrush GradientOrigin="0.5,0.5" Center="0.5,0.5" RadiusX="0.5" RadiusY="2.0">
<GradientStop Color="#006A4D" Offset="1.0"/>
<GradientStop Color="#56be88" Offset=".2"/>
</RadialGradientBrush >
</Grid.Background>
<ContentControl Grid.Column="0" Content="{TemplateBinding Icon}"/>
<TextBlock x:Name="NavigationElementText" Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Left" Text="{TemplateBinding LabelText}" FontFamily="Futura XBlkIt BT" FontSize="12"/>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
</Grid.RowDefinitions>
</Grid>
</Border>
<!--
Trigger related to when the mouse is over the header
I would like it to Execute the doubleanimations
-->
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
<!-- Missing condition: If selected = false -->
</MultiTrigger.Conditions>
<!-- Start BUG the bellow code does not execute -->
<MultiTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="NavigationElementGrid" Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:0.3"/>
<DoubleAnimation Storyboard.TargetName="NavigationElementText" Storyboard.TargetProperty="FontSize" To="14" Duration="0:0:0.3"/>
</Storyboard>
</BeginStoryboard>
</MultiTrigger.EnterActions>
<!-- End BUG -->
<!-- The triggers are fireing becuase this is being set. -->
<Setter Property="Background" TargetName="Bd" Value="Blue"/>
</MultiTrigger>
<!--
When the mouse leaves I want it to return to it's original
state.
-->
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="False"/>
<!-- Missing condition: If selected = false -->
</MultiTrigger.Conditions>
<!-- Start BUG the bellow code does not execute -->
<MultiTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="NavigationElementGrid" Storyboard.TargetProperty="Opacity" To=".75" Duration="0:0:0.3"/>
<DoubleAnimation Storyboard.TargetName="NavigationElementText" Storyboard.TargetProperty="FontSize" To="12" Duration="0:0:0.3"/>
</Storyboard>
</BeginStoryboard>
</MultiTrigger.EnterActions>
<!-- End BUG -->
<!-- The triggers are fireing becuase this is being set. -->
<Setter Property="Background" TargetName="Bd" Value="Red"/>
</MultiTrigger>
<!-- Missing two more MultiTriggers (very similar to above) for the cases of if the tab is selected.-->
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
背后的代码
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace Tenant_Tool_Analytics_Module.Resources.Components
{
public class NavigationElement : Control
{
public string LabelText
{
get
{
return (string)GetValue(LabelTextProperty);
}
set
{
SetValue(LabelTextProperty, value);
}
}
public static readonly DependencyProperty LabelTextProperty =
DependencyProperty.Register("LabelText", typeof(string), typeof(NavigationElement), new PropertyMetadata(string.Empty));
public object Icon
{
get
{
return (object)GetValue(IconProperty);
}
set
{
SetValue(IconProperty, value);
}
}
public static readonly DependencyProperty IconProperty =
DependencyProperty.Register("Icon", typeof(object), typeof(NavigationElement), new PropertyMetadata(string.Empty));
public System.Windows.Media.Brush BackgroundColour
{
get
{
return (System.Windows.Media.Brush)GetValue(BackgroundColourProperty);
}
set
{
SetValue(BackgroundColourProperty, value);
}
}
public static readonly DependencyProperty BackgroundColourProperty =
DependencyProperty.Register("BackgroundColour", typeof(System.Windows.Media.Brush), typeof(NavigationElement), new PropertyMetadata(Brushes.Black));
}
}
实现代码
<Window x:Class="Tenant_Tool_Analytics_Module.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Tenant_Tool_Analytics_Module"
xmlns:views="clr-namespace:Tenant_Tool_Analytics_Module.Views"
xmlns:controls="clr-namespace:Tenant_Tool_Analytics_Module.Resources.Components"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<DockPanel LastChildFill="True">
<views:HeaderView x:Name="HeaderView" DockPanel.Dock="Top"/>
<TabControl DockPanel.Dock="Left" TabStripPlacement="Left" Margin="0, 0, 0, 10">
<TabItem Padding="0">
<TabItem.Header>
<controls:NavigationElement LabelText="Stacking Plan" Icon="{StaticResource StackPlanIcon}"/>
</TabItem.Header>
<Button Content="HI"/>
</TabItem>
<TabItem Padding="0">
<TabItem.Header>
<controls:NavigationElement LabelText="Tenant Profile" Icon="{StaticResource TenantProfileIcon}"/>
</TabItem.Header>
<Button Content="HI"/>
</TabItem>
<TabItem Padding="0">
<TabItem.Header>
<controls:NavigationElement LabelText="Submarket" Icon="{StaticResource SubmarketIcon}"/>
</TabItem.Header>
<Button Content="HI"/>
</TabItem>
<TabItem Padding="0">
<TabItem.Header>
<controls:NavigationElement LabelText="Industry" Icon="{StaticResource IndustryIcon}"/>
</TabItem.Header>
<Button Content="HI"/>
</TabItem>
</TabControl>
</DockPanel>
</Window>
我认为您没有意识到 MultiDataTrigger
对象同时具有 EnterActions
和 ExitActions
这一事实。您可以只使用一个 MultiDataTrigger
在 EnterActions
上触发 true
而另一个 MultiDataTrigger
在 EnterActions
上触发 false
MultiDataTrigger
在 true
上触发,同时 EnterActions
(将对象更改为异常状态)和 ExitActions
将其转换回正常状态。
Triggers
合集现在可以按预期运行,并且更易于阅读:
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
<!-- Missing condition: If selected = false -->
</MultiTrigger.Conditions>
<MultiTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="NavigationElementGrid"
Storyboard.TargetProperty="Opacity"
To="1"
Duration="0:0:0.3"/>
<DoubleAnimation Storyboard.TargetName="NavigationElementText"
Storyboard.TargetProperty="FontSize"
To="14" Duration="0:0:0.3"/>
<ColorAnimationUsingKeyFrames Storyboard.TargetName="BorderBackgroundBrush"
Storyboard.TargetProperty="Color">
<DiscreteColorKeyFrame KeyTime="0" Value="Blue"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</MultiTrigger.EnterActions>
<MultiTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="NavigationElementGrid"
Storyboard.TargetProperty="Opacity"
To=".75"
Duration="0:0:0.3"/>
<DoubleAnimation Storyboard.TargetName="NavigationElementText"
Storyboard.TargetProperty="FontSize"
To="12"
Duration="0:0:0.3"/>
<ColorAnimationUsingKeyFrames Storyboard.TargetName="BorderBackgroundBrush"
Storyboard.TargetProperty="Color">
<DiscreteColorKeyFrame KeyTime="0" Value="Red"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</MultiTrigger.ExitActions>
</MultiTrigger>
<!-- Missing two more MultiTriggers (very similar to above) for the cases of if the tab is selected.-->
</ControlTemplate.Triggers>
另请注意我是如何使用 ColorAnimationUsingKeyFrames
来更改 Border.Background
属性 而无需在另一个 Trigger
中使用 Setter
。这样,所有更改都在同一个 Storyboard
中执行。为此,您只需将命名的 SolidColorBrush
分配给您的 "Bd"
Border
:
<Border.Background>
<SolidColorBrush x:Name="BorderBackgroundBrush" Color="Red"></SolidColorBrush>
</Border.Background>
为了防止 Storyboard
在选择了祖先 TabItem
的情况下播放,我建议您在 NavigationElement
中添加一个布尔值 IsSelected
DependencyProperty
,这样您就可以通过在 Style
中添加 Setter
来将此 属性 绑定到其 TabItem
祖先,如下所示:
<Setter Property="IsSelected" Value="{Binding IsSelected, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=TabItem}}"/>
您只需在 MultiDataTrigger
中添加条件(但您已经知道了):
<Condition Property="IsMouseOver" Value="True"/>
<Condition Property="IsSelected" Value="False"/>
旁注:我建议您换行并缩进您的 XAML 属性以避免长 XAML 行迫使您滚动。除了提高可读性之外,将每个 XAML 属性放在新行上对版本控制更友好,因为一个属性更改只会影响一行。