У меня есть ContentControl
который я разрабатываю с помощью DataTemplate
. Я хотел бы иметь возможность определять анимацию вне ContentControl
которая анимирует элементы в DataTemplate
. Этот XAML - небольшой, упрощенный пример моего сценария:
<UserControl x:Class="StoryboardTesting.Stage"
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"
mc:Ignorable="d">
<UserControl.Resources>
<DataTemplate x:Key="MyControlTemplate">
<StackPanel>
<TextBlock x:Name="TheBlock1" Text="Foo!" />
<TextBlock x:Name="TheBlock2" Text="Bar!" />
</StackPanel>
</DataTemplate>
</UserControl.Resources>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="ValueStates">
<VisualState Name="ToState">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="MyContentControl"
Storyboard.TargetProperty="(UIElement.Opacity)"
Duration="0:0:1"
To="0" />
</Storyboard>
</VisualState>
<VisualState Name="FromState" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid>
<Canvas>
<ContentControl x:Name="MyContentControl"
ContentTemplate="{StaticResource MyControlTemplate}" />
</Canvas>
</Grid>
</UserControl>
Я хотел бы, чтобы анимация могла нацеливать либо TextBox
в шаблоне (вместо "MyContentControl"), либо по положению, либо по имени. Я запускаю анимацию в кодовом коде UserControl
с вызовом следующим образом:
VisualStateManager.GoToElementState(this, "ToState", true);
Когда я запускаю это (заменяя "MyContentControl" на "TheBlock"), я получаю следующее:
InvalidOperationException: имя "TheBlock1" не может быть найдено в области имен "StoryboardTesting.Stage".
Это имеет смысл. Есть ли способ адресовать любой блок, используя имена свойств? Мне нужно избегать codebehind, поскольку это XAML, который создается во время выполнения.
Я настоятельно рекомендую вам изучить Blend при работе над проектами WPF. В то время как XAML с помощью навыков клавиатуры действительно полезен, Blend также очень полезен. Мне потребовалось около 5 минут, чтобы построить для вас следующий пример: это DataTemplate
котором есть состояния.
(сначала я создал пустой DataTemplate
, затем я редактировал в Blend)
Пользователь может нажать любую из двух кнопок внизу, и текущее состояние будет изменено.
Как вы увидите ниже, поведение оказалось действительно полезным для работы с состояниями, без кода.
XAML:
<Window x:Class="WpfApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:wpfApplication3="clr-namespace:WpfApplication3"
Title="MainWindow"
Width="525"
Height="350">
<Window.Resources>
<wpfApplication3:MyObject x:Key="MyObject1" />
<DataTemplate x:Key="Template1" DataType="wpfApplication3:MyObject">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="37*" />
<RowDefinition Height="13*" />
</Grid.RowDefinitions>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="VisualStateGroup">
<VisualState x:Name="Red">
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetName="button" Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)">
<EasingColorKeyFrame KeyTime="0" Value="Red" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Green">
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetName="button" Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)">
<EasingColorKeyFrame KeyTime="0" Value="Lime" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Button x:Name="button"
Grid.RowSpan="1"
Grid.ColumnSpan="2"
Width="100"
Height="100"
Margin="2"
Content="Button"
FontSize="26.667" />
<Button Grid.Row="1"
Width="Auto"
Margin="2"
Content="State1">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:GoToStateAction StateName="Red" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<Button Grid.Row="1"
Grid.Column="1"
Width="Auto"
Margin="2"
Content="State2">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:GoToStateAction StateName="Green" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</Grid>
</DataTemplate>
</Window.Resources>
<Grid>
<ContentControl Content="{StaticResource MyObject1}" ContentTemplate="{StaticResource Template1}" />
</Grid>
</Window>
Код-за:
namespace WpfApplication3
{
public partial class MainWindow
{
public MainWindow() {
InitializeComponent();
}
}
internal class MyObject
{
public string Category { get; set; }
public int Value { get; set; }
}
}
РЕДАКТИРОВАТЬ
Чтобы ответить на вопрос вашего вопроса, эти состояния принадлежат к DataTemplate
; определение этих состояний вне его не имеет никакого смысла, и, как вы испытали, это даже невозможно, и это по уважительной причине!
Представьте, что вы используете этот шаблон в двух разных местах, будут ли они иметь одинаковое состояние? Конечно, нет, поэтому в ней должны быть определены состояния, а не снаружи.