Betrachten Sie diesen Code:Zugriff auf ein Storyboard innerhalb einer Elementressourcen von XAML?
<UserControl x:Class="MyApp.MyControl"
...
xmlns:local="clr-namespace:MyApp"
DataContext="{Binding RelativeSource={RelativeSource Mode=Self}}">
<UserControl.Template>
<ControlTemplate>
<ControlTemplate.Resources>
<Storyboard x:Key="MyStory">
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)" Storyboard.TargetName="brdBase">
<SplineColorKeyFrame KeyTime="0:0:1" Value="Red"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</ControlTemplate.Resources>
<Border x:Name="brdBase" BorderThickness="1" BorderBrush="Cyan" Background="Black">
...
</Border>
<ControlTemplate.Triggers>
<Trigger SourceName="brdBase" Property="IsMouseOver" Value="True">
<Trigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource MyStory}"/>
</Trigger.EnterActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</UserControl.Template>
</UserControl>
Der obige Code funktioniert ohne Probleme. Nun, ich will binden Key-Frame-Wert von MyStory
an einen DP (benannt SpecialColor
) diese Benutzersteuerung wie folgt:
<Storyboard x:Key="MyStory">
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)" Storyboard.TargetName="brdBase">
<SplineColorKeyFrame KeyTime="0:0:1" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MyControl}}, Path=SpecialColor}"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
, die einen Fehler macht:
Kann nicht diese Storyboard Timeline Baumes einfrieren für Verwenden Sie über Threads.
Es ist möglich, dies mit Code hinter zu tun. Aber wie kann ich es nur in XAML machen?
-Code-Behind Aided Lösung:
► Schritt 1: Inbetriebnahme des MyStory
Storyboard in die brdBase
Ressourcen.
<UserControl.Template>
<ControlTemplate>
<Border x:Name="brdBase" BorderThickness="1" BorderBrush="Cyan" Background="Black">
<Border.Resources>
<Storyboard x:Key="MyStory">
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)" Storyboard.TargetName="brdBase">
<SplineColorKeyFrame KeyTime="0:0:1" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MyControl}}, Path=SpecialColor}"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</Border.Resources>
...
</Border>
<ControlTemplate.Triggers>
<Trigger SourceName="brdBase" Property="IsMouseOver" Value="True">
<Trigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource MyStory}"/>
</Trigger.EnterActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</UserControl.Template>
Fehler:Kann nicht Ressource finden namens 'MyStory'. Bei Ressourcennamen wird zwischen Groß- und Kleinschreibung unterschieden
► Schritt 2: Beseitigung Trigger
auf IsMouseOver
Eigenschaft und beginnen, die MyStory
von Code hinter.
<UserControl.Template>
<ControlTemplate>
<Border x:Name="brdBase" BorderThickness="1" BorderBrush="Cyan" Background="Black" MouseEnter="brdBase_MouseEnter">
<Border.Resources>
<Storyboard x:Key="MyStory">
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)" Storyboard.TargetName="brdBase">
<SplineColorKeyFrame KeyTime="0:0:1" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MyControl}}, Path=SpecialColor}"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</Border.Resources>
</Border>
</ControlTemplate>
</UserControl.Template>
C# -Code-Behind:
private void brdBase_MouseEnter(object sender, MouseEventArgs e)
{
Border grdRoot = (Border)this.Template.FindName("brdBase", this);
Storyboard story = grdRoot.Resources["MyStory"] as Storyboard;
story.Begin(this, this.Template);
}
► Schritt 3: Die Lösung wird bereits getan, aber es beim ersten Mal nicht funktioniert. Glücklicherweise gibt es eine Problemumgehung für dieses Problem. Es genügt, die ControlTemplate
in eine Style
zu legen.
(Ich brauche andere Trigger
Typen als EventTrigger
und müssen die UserControl
Elemente mit dem ControlTemplate
wickeln.)
Update:
Die Idee zur Verwendung von ObjectDataProvider
fehlgeschlagen.
- Eine Object Ressource kann nicht ein Storyboard verwendet werden, um !!! Der Fehlerbericht ist:
- XamlParseException: Set Eigenschaft 'System.Windows.Media.Animation.BeginStoryboard.Storyboard' eine Ausnahme ausgelöst hat.
- InnerException: 'System.Windows.Data.ObjectDataProvider 'ist kein gültiger Wert für die Eigenschaft' Storyboard '.
- Die AssociatedControl DP ist immer null. Hier
ist der Code:
<UserControl.Template>
<ControlTemplate>
<ControlTemplate.Resources>
<local:StoryboardFinder x:Key="StoryboardFinder1" AssociatedControl="{Binding ElementName=brdBase}"/>
<ObjectDataProvider x:Key="dataProvider" ObjectInstance="{StaticResource StoryboardFinder1}" MethodName="Finder">
<ObjectDataProvider.MethodParameters>
<sys:String>MyStory</sys:String>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</ControlTemplate.Resources>
<Border x:Name="brdBase" BorderThickness="1" BorderBrush="Cyan" Background="Black">
<Border.Resources>
<Storyboard x:Key="MyStory">
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)" Storyboard.TargetName="brdBase">
<SplineColorKeyFrame KeyTime="0:0:1" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MyControl}}, Path=SpecialColor}"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</Border.Resources>
...
</Border>
<ControlTemplate.Triggers>
<Trigger SourceName="brdBase" Property="IsMouseOver" Value="True">
<Trigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource dataProvider}"/>
</Trigger.EnterActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</UserControl.Template>
Die StoryboardFinder Klasse:
public class StoryboardFinder : DependencyObject
{
#region ________________________________________ AssociatedControl
public Control AssociatedControl
{
get { return (Control)GetValue(AssociatedControlProperty); }
set { SetValue(AssociatedControlProperty, value); }
}
public static readonly DependencyProperty AssociatedControlProperty =
DependencyProperty.Register("AssociatedControl",
typeof(Control),
typeof(StoryboardFinder),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None));
#endregion
public Storyboard Finder(string resourceName)
{
//
// Associated control is always null :(
//
return new Storyboard();
}
}
das ist nett, sollte es ohne Probleme funktionieren. Das einzige, was ich sehen kann, ist, dass es weniger effizient ist, da Sie Ihre Storyboard-Timeline nicht mehr einfrieren und es bedeutet, dass es nicht mehrere Threads verwendet. –
Sie haben Recht. Ich suche nach einer Möglichkeit, die 'Invoke' Methode von' TriggerAction' aufzurufen. Auf diese Weise sollten alle erforderlichen Dinge intern erledigt werden. Haben Sie eine Idee? (Ich habe es als "Konnte dieses Snippet nicht verwenden" markiert.) – Mimi
In einer anderen Ansicht, eigentlich aus der Dokumentation, startet Begin() das Einfrieren automatisch. Mich interessiert mehr, warum Xaml nicht richtig funktioniert? Warum versuchst du sowieso auf interne Sachen zuzugreifen? Es ist nie eine gute Idee, es sei denn, absolut keine andere Möglichkeit –