Rozważmy następujący kod:Jak uzyskać dostęp do storyboardu w zasobach elementu z 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>
Powyższy kod działa bez problemu. Teraz chcę powiązać wartości klucza klatek MyStory
do DP (o nazwie SpecialColor
) niniejszego łatwość sterowania tak:
<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>
który popełni błąd:
nie może zamarznąć ten Storyboard drzewa harmonogram używaj w wątkach.
Można to zrobić za pomocą kodu z tyłu. Ale jak mogę to zrobić tylko w XAML?
Code-Behind Aided Rozwiązanie:
► Krok 1: Umieszczenie MyStory
storyboard do zasobów brdBase
.
<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>
Błąd:Nie można znaleźć zasobu o nazwie 'MyStory'. W nazwach zasobów rozróżniana jest wielkość liter.
► Krok 2: Wyeliminowanie Trigger
na IsMouseOver
nieruchomości i rozpocząć MyStory
od kod.
<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);
}
► Krok 3: Rozwiązanie jest już zrobione, ale to nie działa na pierwszy raz. Na szczęście istnieje obejście tego problemu. Wystarczy umieścić ControlTemplate
w Style
.
(muszę inne Trigger
typy niż EventTrigger
i należy owinąć UserControl
elementy z ControlTemplate
.)
Aktualizacja:
Chodzi o użyciu ObjectDataProvider
powiodło się.
- ObjectDataProvider zasobów nie mogą być wykorzystywane w celu zapewnienia storyboard !!! Raport o błędzie jest:
- XamlParseException: Ustaw właściwość 'System.Windows.Media.Animation.BeginStoryboard.Storyboard' zwrócił wyjątek.
- InnerException: "System.Windows.Data.ObjectDataProvider "nie jest prawidłową wartością dla właściwości" Storyboard ".
- AssociatedControl DP jest zawsze zerowa.
Oto kod:
<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>
Klasa StoryboardFinder:
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();
}
}
To miło, powinno działać bez problemów. Jedyne, co widzę, to to, że może być mniej efektywny, ponieważ nie zamarzasz już osi czasu storyboardu i oznacza to, że nie używa wielu wątków. –
Masz rację. Szukam sposobu wywołania metody "Invoke" z "TriggerAction". W ten sposób wszystkie wymagane rzeczy powinny być wykonywane wewnętrznie. Czy masz jakiś pomysł? (Oznaczałem to jako "Nie mogłem użyć tego fragmentu".) – Mimi
W innym widoku, faktycznie, z dokumentów, Begin() robi zamrożenie automatycznie. Jestem bardziej zainteresowany tym, dlaczego nie działa Xaml? Dlaczego mimo wszystko próbujesz uzyskać dostęp do wewnętrznych treści? To nigdy nie jest dobry pomysł, chyba że w żaden inny sposób –