我有一个用户控件,我想在特定时间为其设置动画。我将其中两个用户控件添加到窗口中。我使用该窗口中的按钮来启动动画。其中一个用户控件工作得很好。第二个给出了 Cannot animate '(0).(1)' on an immutable object instance 错误。我根本不明白这个!为什么其中一个有效,而另一个却不起作用?我读到需要一个转换器 - 但我根本无法让它工作。我把代码留在这里,以防万一我确实需要它。感谢您在这方面能给我的任何帮助。我是 WPF 新手,所以我可能会在这里做一些愚蠢的事情 - 请原谅我。这是我的用户控件代码:
<UserControl x:Class="colorCompButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Local="clr-namespace:UserControlTests"
xmlns:custom="clr-namespace:UserControlTests"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" Height="179" Width="317">
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
<Storyboard x:Key="sb2" RepeatBehavior="Forever" x:Name="thisone">
<ColorAnimation Storyboard.TargetName="badge1"
Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)" From="Red" To="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=BadgeColor}"
Duration="0:0:1" />
</Storyboard>
</UserControl.Resources>
<Grid Margin="0,0,10,10">
<Rectangle x:Name="rect" HorizontalAlignment="Left" Height="135" VerticalAlignment="Top" Width="259" x:FieldModifier="public" Fill ="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=RectColor}"/>
<Image x:Name="image" Height="87" Margin="10,10,63,0" VerticalAlignment="Top" x:FieldModifier="public" Source ="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=ImageSource}"/>
<TextBlock x:Name="textBlock" Margin="0,102,10,38" Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=TxtBoxValue}" Background="Transparent" Foreground="White" FontSize="20" Padding="3"/>
<Grid Margin="222,81,0,0" x:Name="NumberIMG" Height="78" VerticalAlignment="Top" HorizontalAlignment="Left" Width="85" Visibility="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=BadgeVisibile}" >
<Border BorderBrush="Maroon" BorderThickness="1" CornerRadius="120" Background="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=BadgeColor}" HorizontalAlignment="Left" Height="76" VerticalAlignment="Top" Width="76" x:Name="badge1" x:FieldModifier="public"></Border>
<Border BorderBrush="black" Margin="2,2,0,0" BorderThickness="3" Opacity=".5" CornerRadius="120" HorizontalAlignment="Left" Height="74" VerticalAlignment="Top" Width="74"></Border>
<Border BorderBrush="White" BorderThickness="3" CornerRadius="120" HorizontalAlignment="Left" Height="78" VerticalAlignment="Top" Width="78">
<Label x:Name="resCount" Content="46" FontSize="25" Foreground="White" HorizontalContentAlignment="center" VerticalContentAlignment="center" Margin="0,0,-3,-3"/>
</Border>
</Grid>
</Grid>
该控件背后的代码:
Imports System.Windows.Media.Animation
Public Class colorCompButton
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
End Sub
'Public Shared Sub SetImage(obj As DependencyObject, value As ImageSource)
' obj.SetValue(ImageProperty, value)
'End Sub
'Public Shared Function GetImage(obj As DependencyObject) As ImageSource
' Return DirectCast(obj.GetValue(ImageProperty), ImageSource)
'End Function
Shared Sub New()
'register attached dependency property
Dim metadata = New FrameworkPropertyMetadata(DirectCast(Nothing, ImageSource))
'ImageProperty = DependencyProperty.RegisterAttached("Image", GetType(ImageSource), GetType(colorCompButton), metadata)
End Sub
Public Shared ReadOnly BadgeVisibileProperty As DependencyProperty = DependencyProperty.Register("BadgeVisibile", GetType(Visibility), GetType(colorCompButton), New PropertyMetadata(Visibility.Visible))
Public Property BadgeVisibile() As Visibility
Get
Return DirectCast(GetValue(BadgeVisibileProperty), Visibility)
End Get
Set(value As Visibility)
SetValue(BadgeVisibileProperty, value)
End Set
End Property
Public Shared ReadOnly BadgeColorProperty As DependencyProperty = DependencyProperty.Register("BadgeColor", GetType(Brush), GetType(colorCompButton), New PropertyMetadata(New SolidColorBrush(Colors.DarkGray)))
Public Property BadgeColor() As SolidColorBrush
Get
Return DirectCast(GetValue(BadgeColorProperty), SolidColorBrush)
End Get
Set(value As SolidColorBrush)
SetValue(BadgeColorProperty, value)
End Set
End Property
Public Shared ReadOnly buttonColorProperty As DependencyProperty = DependencyProperty.Register("buttonColor", GetType(Brush), GetType(colorCompButton), New PropertyMetadata(New SolidColorBrush(Colors.DarkGray)))
Public Property buttonColor() As SolidColorBrush
Get
Return DirectCast(GetValue(buttonColorProperty), SolidColorBrush)
End Get
Set(value As SolidColorBrush)
SetValue(buttonColorProperty, value)
End Set
End Property
Public Shared ReadOnly ImageSourceProperty As DependencyProperty = DependencyProperty.Register("ImageSource", GetType(BitmapSource), GetType(colorCompButton))
Public Property ImageSource() As ImageSource
Get
Return DirectCast(GetValue(ImageSourceProperty), ImageSource)
End Get
Set(value As ImageSource)
SetValue(ImageSourceProperty, value)
End Set
End Property
Public Shared ReadOnly RectColorProperty As DependencyProperty = DependencyProperty.Register("RectColor", GetType(Brush), GetType(colorCompButton), New PropertyMetadata(New SolidColorBrush(Colors.DarkGray)))
Public Property RectColor() As SolidColorBrush
Get
Return DirectCast(GetValue(RectColorProperty), SolidColorBrush)
End Get
Set(value As SolidColorBrush)
SetValue(RectColorProperty, value)
End Set
End Property
Public Shared TxtBoxValueProperty As DependencyProperty = DependencyProperty.Register("txtBoxValue", GetType([String]), GetType(colorCompButton))
Public Property TxtBoxValue() As [String]
Get
Return DirectCast(GetValue(TxtBoxValueProperty), [String])
End Get
Set(value As [String])
SetValue(TxtBoxValueProperty, value)
End Set
End Property
Public Sub StartLeafUp()
Dim sb As Storyboard = TryCast(Me.Resources("sb2"), Storyboard)
sb.Begin()
End Sub
Public Sub StartLeafDown()
Dim sb As Storyboard = TryCast(Me.Resources("sb2"), Storyboard)
sb.Stop()
End Sub
End Class
Friend Class MyCloneConverter
Implements IValueConverter
Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As Globalization.CultureInfo) As Object Implements IValueConverter.Convert
If TypeOf value Is Freezable Then
value = TryCast(value, Freezable).Clone()
End If
Return value
End Function
Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As Globalization.CultureInfo) As Object Implements IValueConverter.ConvertBack
Throw New NotSupportedException()
End Function
End Class
主窗口和后台代码:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:UserControlTests" x:Class="MainWindow"
Title="MainWindow" Height="437" Width="1057">
<Grid>
<local:colorCompButton x:Name="color" Margin="201,62,0,0" VerticalAlignment="Top" HorizontalAlignment="Left" BadgeColor="#FF833E1D" buttonColor="Sienna" RectColor="Sienna" ImageSource="pack://siteoforigin:,,,/images/deviation.png" TxtBoxValue="I WORK"/>
<Button x:Name="button" Content="WORKS FINE" HorizontalAlignment="Left" Margin="336,262,0,0" VerticalAlignment="Top" Width="160" Height="53"/>
<Button x:Name="button1" Content="DOES NOT WORK!!" HorizontalAlignment="Left" Margin="534,262,0,0" VerticalAlignment="Top" Width="144" Height="53"/>
<local:colorCompButton HorizontalAlignment="Left" Margin="534,62,0,0" VerticalAlignment="Top" BadgeColor="Indigo" RectColor="IndianRed" ImageSource="pack://siteoforigin:,,,/images/deviation.png" TxtBoxValue="I WON'T WORK" x:Name="res"/>
<Label x:Name="label" Content="EXACT SAME CONTROLS. WHY WILL THIS NOT WORK??" HorizontalAlignment="Left" Margin="375,365,0,0" VerticalAlignment="Top"/>
</Grid>
Imports System.Windows.Media.Animation
Class MainWindow
Private Sub button_Click(sender As Object, e As RoutedEventArgs) Handles button.Click
color.StartLeafUp()
End Sub
Private Sub button1_Click(sender As Object, e As RoutedEventArgs) Handles button1.Click
res.StartLeafUp()
End Sub
End Class
这可能不是最好的答案,但我通过添加另一个边框、将其背景设置为透明并为其设置动画而不是第一个边框自行解决了这个问题。这看起来更像是一种黑客而不是解决方案,但它确实有效。我仍然愿意听到一个真正的解决方案 - 如果有人有的话。
我这样做的原因是:
Background="{Binding ElementName=ToggleButton, Path=Background}"
而不是这个:
Background="{TemplateBinding Background}"
在我在控件模板内设置动画的控件上。
看来你也犯了同样的错误:-)
我在尝试对 TextBlock Foreground 属性进行动画处理时遇到了相同的错误/问题。
一种解决方法是创建一个 Color 类型 DependencyProperty 并将其设置为颜色动画的目标。然后,例如,将 TextBlock Foreground 属性绑定到托管 UserControl(或 Window)的 DependencyProperty,并使用转换器从 Color 创建 Brush。
这是一个闪烁 TextBlock 的示例,我将其命名为 SpectrumTextBlock,它在加载控件时启动动画:
XAML:
<UserControl x:Class="Common.SpectrumTextBlock"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Common"
mc:Ignorable="d"
x:Name="spectrumTextBlock">
<UserControl.Resources>
<local:ColorToBrushConverter x:Key="colorToBrushConverter"/>
<Storyboard x:Key="spectrumStoryboard" RepeatBehavior="Forever">
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(EffectColor)" Storyboard.TargetName="spectrumTextBlock">
<EasingColorKeyFrame KeyTime="0" Value="Black"/>
<EasingColorKeyFrame KeyTime="0:0:0.1" Value="Red"/>
<EasingColorKeyFrame KeyTime="0:0:0.2" Value="Orange"/>
<EasingColorKeyFrame KeyTime="0:0:0.3" Value="Yellow"/>
<EasingColorKeyFrame KeyTime="0:0:0.4" Value="Green"/>
<EasingColorKeyFrame KeyTime="0:0:0.5" Value="Cyan"/>
<EasingColorKeyFrame KeyTime="0:0:0.6" Value="Blue"/>
<EasingColorKeyFrame KeyTime="0:0:0.7" Value="Violet"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</UserControl.Resources>
<TextBlock Text="TEST TEXT" Foreground="{Binding ElementName=spectrumTextBlock, Path=EffectColor, Converter={StaticResource colorToBrushConverter}}"/>
以及包含转换器的该控件的 CS 文件:
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Animation;
namespace Common
{
public partial class SpectrumTextBlock : UserControl
{
private Storyboard _effestStoryboard;
public Color EffectColor
{
get { return (Color)GetValue(EffectColorProperty); }
set { SetValue(EffectColorProperty, value); }
}
public static readonly DependencyProperty EffectColorProperty =
DependencyProperty.Register("EffectColor", typeof(Color), typeof(SpectrumTextBlock), new PropertyMetadata(SystemColors.ControlTextColor));
public SpectrumTextBlock()
{
InitializeComponent();
this.Loaded += SpectrumTextBlock_Loaded;
}
private void SpectrumTextBlock_Loaded(object sender, RoutedEventArgs e)
{
_effestStoryboard = (Storyboard)FindResource("spectrumStoryboard");
_effestStoryboard.Begin();
}
}
internal class ColorToBrushConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return new SolidColorBrush((Color)value);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
另一种解决方法是使用 0px 大小(不可见)的矩形并为 Fill 属性设置动画,然后将 TextBlock Foreground 属性绑定到矩形的 Fill 属性。但第一个 DependencyProperty 解决方法看起来不那么“hacky”。
遇到此问题的任何人:
无法在不可变对象实例上为“(0).(1)”设置动画
添加
[VisualState x:Name="Normal"]
解决了问题。
以下代码是一个示例:
XAML:
<Style TargetType="RadioButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="RadioButton">
<Border x:Name="borderIn"
CornerRadius="0">
<Label>
<ContentPresenter/>
</Label>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<Storyboard>
<ColorAnimation Storyboard.TargetName="borderIn"
Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
FillBehavior="HoldEnd" Duration="0"/>
</Storyboard>
</VisualState>
<VisualState x:Name="MouseOver">
<Storyboard>
<ColorAnimation To="LightBlue" Storyboard.TargetName="borderIn"
Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
FillBehavior="HoldEnd" Duration="0"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="CheckStates">
<VisualState x:Name="Unchecked">
<Storyboard>
<ColorAnimation Storyboard.TargetName="borderIn"
Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
FillBehavior="HoldEnd" Duration="0"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Checked">
<Storyboard>
<ColorAnimation Storyboard.TargetName="borderIn"
Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
FillBehavior="HoldEnd" Duration="0"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="true">
<Setter Property="Background" TargetName="borderIn" Value="LightCyan" />
</Trigger>
<Trigger Property="IsChecked" Value="false">
<Setter TargetName="borderIn" Property="Background" Value="Transparent"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
希望这个解决方案可以帮助那些面临同样问题的人
遇到此问题的任何人:
无法在不可变对象实例上为“(0).(1)”设置动画
添加 [VisualState x:Name="Normal"] 解决了问题。
您只需删除所有触发器
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="true">
<Setter Property="Background" TargetName="borderIn" Value="LightCyan" />
</Trigger>
<Trigger Prop`enter code here`erty="IsChecked" `enter code here`Value="false">
<Setter TargetName="borderIn" Property="Background" Value="Transparent"/>`enter code here`
</Trigger>
</ControlTemplate.Triggers>