Access ItemsControl Items and Animate One by One

2019-08-18 14:54发布

问题:

Today is a good day since I started with WPF, this for a launcher I'm creating. Using the following code, I managed to get the result to be seen in the screenshot:

<Grid>
        <ItemsControl ItemsSource="{Binding Programs}">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center" />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Button Content="{Binding Text}" Background="Transparent" Foreground="White" Width="128" Height="150" >
                        <Button.RenderTransform>
                            <TransformGroup>
                                <ScaleTransform />
                            </TransformGroup>
                        </Button.RenderTransform>
                        <Button.Template>
                            <ControlTemplate TargetType="Button">
                                <Grid>
                                    <Grid.RowDefinitions>
                                        <RowDefinition Height="*" />
                                        <RowDefinition Height="Auto" />
                                    </Grid.RowDefinitions>

                                    <Image Grid.Row="0" Source="{Binding Image}" Height="128" />
                                    <ContentPresenter Grid.Row="1" HorizontalAlignment="Center" Margin="3,10" />
                                    <Rectangle Grid.Row="0" Fill="{TemplateBinding Background}" />
                                    <Rectangle Grid.Row="1" Fill="{TemplateBinding Background}" />
                                </Grid>
                            </ControlTemplate>
                        </Button.Template>
                        <Button.Resources>
                            <Storyboard SpeedRatio="4" x:Key="MouseEnterStoryboard" x:Name="MouseEnterStoryboard">
                                <ColorAnimation Storyboard.TargetProperty="(Button.Background).(SolidColorBrush.Color)" To="#22FFFFFF"></ColorAnimation>
                            </Storyboard>
                            <Storyboard SpeedRatio="4" x:Key="MouseLeaveStoryboard" x:Name="MouseLeaveStoryboard">
                                <ColorAnimation Storyboard.TargetProperty="(Button.Background).(SolidColorBrush.Color)" To="Transparent"></ColorAnimation>
                            </Storyboard>
                            <Storyboard Duration="00:00:00.05" x:Key="MouseClickStoryboard" AutoReverse="True">
                                <DoubleAnimation To="0.8" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)"/>
                                <DoubleAnimation To="0.8" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)"/>
                            </Storyboard>
                            <Storyboard x:Key="WindowLoadedStoryboard">
                                <DoubleAnimation Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="00:00:01" />
                            </Storyboard>
                        </Button.Resources>
                        <Button.Triggers>
                            <EventTrigger RoutedEvent="Mouse.MouseEnter">
                                <BeginStoryboard Storyboard="{StaticResource MouseEnterStoryboard}" />
                            </EventTrigger>
                            <EventTrigger RoutedEvent="Mouse.MouseLeave">
                                <BeginStoryboard Storyboard="{StaticResource MouseLeaveStoryboard}" />
                            </EventTrigger>
                            <EventTrigger RoutedEvent="Button.Click">
                                <BeginStoryboard Storyboard="{StaticResource MouseClickStoryboard}" />
                            </EventTrigger>
                            <EventTrigger RoutedEvent="Window.Loaded">
                                <BeginStoryboard Storyboard="{StaticResource WindowLoadedStoryboard}"></BeginStoryboard>
                            </EventTrigger>
                        </Button.Triggers>
                    </Button>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </Grid>

Screenshot:

Now, for each item in the list bound to this control, it will create a button. How would I access this button programmatically, better yet, how would I access one of the Storyboards programatically since assigning a name (x:to them simply won't do the trick it seems...

Also, how can I animate the buttons one by one? Currently they each fade in at exact the same time (@ WindowLoadedStoryboard), but I would like to let each button fade in one by one with a short delay, to create a nice effect. How would I achieve this?

Hope someone can answer these 2 questions for me!

Greetings!

回答1:

Your problem with accessing the elements defined in the DataTemplate is caused because you defined those elements in a DataTemplate... those elements could be rendered in many different types of UI container controls. You can find the solution in the How to: Find DataTemplate-Generated Elements page from MSDN.

You first need to get hold of the relevant container control that contains the item that has had that DataTemplate applied to it. Next, you need to get the ContentPresenter from that container control and then you can get the DataTemplate from ContentPresenter. Finally, you can access the named elements from the DataTemplate. From the linked page:

// Getting the currently selected ListBoxItem 
// Note that the ListBox must have 
// IsSynchronizedWithCurrentItem set to True for this to work
ListBoxItem myListBoxItem = (ListBoxItem)(myListBox.ItemContainerGenerator.
    ContainerFromItem(myListBox.Items.CurrentItem));

// Getting the ContentPresenter of myListBoxItem
ContentPresenter myContentPresenter = FindVisualChild<ContentPresenter>(myListBoxItem);

// Finding textBlock from the DataTemplate that is set on that ContentPresenter
DataTemplate myDataTemplate = myContentPresenter.ContentTemplate;
TextBlock myTextBlock = 
    (TextBlock)myDataTemplate.FindName("textBlock", myContentPresenter);

// Do something to the DataTemplate-generated TextBlock
MessageBox.Show("The text of the TextBlock of the selected list item: " + 
    myTextBlock.Text);