I'm trying to programmatically trigger a color animation on a button, but getting a System.InvalidOperationException
when I do. It seems it can't resolve the TargetProperty
because I've styled the button using a template/content presenter.
The reason I style <Button>
this way is to force the style to be consistent (and avoid the default blue hover effect). I also have a DataTrigger that animates the background color of this button whenever it's shown (ie made Visible). Here's the full <Style>
definition:
<Style x:Key="PopUnder" TargetType="Button">
<Setter Property="Foreground" Value="Black" />
<Setter Property="FontSize" Value="34" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Name="Border" Background="White" BorderBrush="Red" BorderThickness="0,1,0,1">
<TextBlock TextWrapping="Wrap" TextAlignment="Center" VerticalAlignment="Center" LineStackingStrategy="BlockLineHeight" LineHeight="40" Text="{TemplateBinding Content}" />
</Border>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Visibility}" Value="Visible">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetName="Border" Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)" RepeatBehavior="Forever">
<LinearColorKeyFrame Value="White" KeyTime="0:0:0" />
<LinearColorKeyFrame Value="LightCoral" KeyTime="0:0:1" />
<LinearColorKeyFrame Value="White" KeyTime="0:0:2" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
But I also want to be able to trigger a different animation on this same button, from code. So I have a separate <Storyboard>
defined elsewhere (outside the Style definition), which I trigger using TryFindResource()
... Begin()
. However, because the button is using a Template, I'm having trouble getting the Background color property to properly resolve in my XAML (below).
<Storyboard x:Key="StatusGood" Completed="AnimationStatusGood_Completed">
<ColorAnimationUsingKeyFrames Storyboard.TargetName="PopUnderMessage" Storyboard.TargetProperty="(Border).(Background).(SolidColorBrush.Color)">
<LinearColorKeyFrame Value="LightGreen" KeyTime="0:0:0" />
<LinearColorKeyFrame Value="White" KeyTime="0:0:1" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
I've tried a couple of different values for the Storyboard.TargetProperty
attribute here:
But all of them yield different variations on the same exception. I confess I'm kind of throwing darts on a board blindly as my knowledge of XAML templates and property paths is lacking. How can I get this to work?
Try this, omitting Storyboard.TargetName
entirely, and omitting Border
from Storyboard.TargetProperty
:
<Storyboard x:Key="StatusGood" Completed="AnimationStatusGood_Completed">
<ColorAnimationUsingKeyFrames
Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)">
<LinearColorKeyFrame Value="LightGreen" KeyTime="0:0:0" />
<LinearColorKeyFrame Value="White" KeyTime="0:0:1" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
Give the styled button a name:
<Button
x:Name="button"
Content="Hello, world!"
Style="{StaticResource PopUnder}" Click="ButtonBase_OnClick" />
To activate the storyboard:
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
Storyboard storyboard = (Storyboard)this.TryFindResource("StatusGood");
Border border = FindVisualChild<Border>(button, "Border");
storyboard.Begin(border);
}
For reference, the FindVisualChild
helper function:
private static T FindVisualChild<T>(
DependencyObject parent,
string name = null)
where T : DependencyObject
{
if (parent != null)
{
int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childrenCount; i++)
{
DependencyObject child = VisualTreeHelper.GetChild(parent, i);
T candidate = child as T;
if (candidate != null)
{
if (name == null)
{
return candidate;
}
FrameworkElement element = candidate as FrameworkElement;
if (name == element?.Name)
{
return candidate;
}
}
T childOfChild = FindVisualChild<T>(child, name);
if (childOfChild != null)
{
return childOfChild;
}
}
}
return default(T);
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.