1

I would like to visualize multiple circles, each of which has its own animation using a path. Now I have a button that I like to click to start animation for all these circles in the view. I am new to WPF and I would like to stick to MVVM as much as possible. What makes the most sense to me is to create ItemsControl which has Canvas in the view and binding the ItemSource to my viewmodel. The issue I have is I don't know set up RouteEvent to button click inside the ItemsControl. <EventTrigger RoutedEvent="Button.Click" SourceName=" PlayButton"> clearly it does not find the button with that name because the MyCircle object does not have the button. See the following for my source code.

my view (XMAL) source code

   <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Grid Grid.Row="0">
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <Button x:Name="PlayButton" Click="Play_Click" Content="Play" Grid.Row="0" Grid.Column="1"/>
            <Button x:Name="StopButton" Click="Stop_Click" Content="Stop" Grid.Row="1" Grid.Column="1"/>
        </Grid>
        <Grid Grid.Row="1">
            <Canvas>
                <ItemsControl ItemsSource="{Binding MyItemsModel, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <Canvas Background="Transparent"/>
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                    <ItemsControl.ItemContainerStyle>
                        <Style TargetType="ContentPresenter">
                            <Setter Property="Canvas.Left" Value="{Binding X}"/>
                            <Setter Property="Canvas.Top" Value="{Binding Y}"/>
                        </Style>
                    </ItemsControl.ItemContainerStyle>
                    <ItemsControl.ItemTemplate>
                        <DataTemplate x:Name="ClockwiseBobbinsDataTemplate">
                            <Path Fill="Red" x:Name="ClockwiseBobbinsPath">
                                <Path.Data>
                                    <EllipseGeometry x:Name="MyCircle" RadiusX="5" RadiusY="5"/>
                                </Path.Data>
                                <Path.Triggers>
                                    <EventTrigger RoutedEvent="Button.Click" SourceName="PlayButton">
                                        <BeginStoryboard>
                                            <Storyboard>
                                              <PointAnimationUsingPath
                                                Storyboard.TargetName="MyCircle"
                                                Storyboard.TargetProperty="Center"
                                                Duration="0:0:10"
                                                RepeatBehavior="Forever">
                                                    <PointAnimationUsingPath.PathGeometry>
                                                        <PathGeometry 
                                                        Figures="{Binding AnimationPath}"
                                                        PresentationOptions:Freeze="True" />
                                                    </PointAnimationUsingPath.PathGeometry>
                                                </PointAnimationUsingPath>
                                            </Storyboard>
                                        </BeginStoryboard>
                                    </EventTrigger>
                                </Path.Triggers>
                            </Path>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </Canvas>
        </Grid>
    </Grid>

my viewmodel source code

    public class ParentViewModel : BindableBase
    {
   
        private List<MyCircle> myItemsModel;

        public List<MyCircle> MyItemsModel
        {
            get { return myItemsModel; }
            set 
            { 
                SetProperty(ref myItemsModel, value);
            }
        }

        public ParentViewModel()
        {
            // instantiate MyItemsModel
        }

 
    }

MyCircle class

   public class MyCircle
    {
        public double X { get; set; }
        public double Y { get; set; }
        public PathFigureCollection AnimationPath { get; set; }

        public MyCircle(double x, double Y, PathFigureCollection path)
        {
            // do something
        }
    }

Rekshino
  • 6,954
  • 2
  • 19
  • 44
Yose
  • 11
  • 3

1 Answers1

0

To go around the issue you should use DataTrigger rather then EventTrigger in ItemsControl.ItemTemplate.

The thing is, that Path.Triggers does accept only EventTrigger so you should move the part with BeginStoryboard to the DataTemplate.Triggers.

Now is the question - which data must trigger the storyboard? You could try to add some property to the viewmodel and set and access it somehow, or add an invisible e.g. CheckBox and act with it.
So remove Path.Triggers and put its content to the DataTemplate.Triggers

<DataTemplate.Triggers>
    <DataTrigger Value="True">
        <DataTrigger.Binding>
            <Binding Path="IsChecked" ElementName="ChbPlay"/>
        </DataTrigger.Binding>
        <DataTrigger.EnterActions>
            <BeginStoryboard x:Name="sb1">
                <Storyboard>
                    <SomeAnimation/>
                </Storyboard>
            </BeginStoryboard>
        </DataTrigger.EnterActions>
        <DataTrigger.ExitActions>
            <StopStoryboard BeginStoryboardName="sb1"/>
        </DataTrigger.ExitActions>
    </DataTrigger>
</DataTemplate.Triggers>

Now you must add an invisible CheckBox and control it with your buttons:

<CheckBox x:Name="ChbPlay" Visibility="Hidden"/>
<Button x:Name="PlayButton" Content="Play" Grid.Row="0" Grid.Column="1">
    <Button.Triggers>
        <EventTrigger RoutedEvent="Button.Click">
            <EventTrigger.Actions>
                <BeginStoryboard>
                    <Storyboard>
                        <BooleanAnimationUsingKeyFrames Storyboard.TargetName="ChbPlay" Storyboard.TargetProperty="IsChecked">
                            <BooleanAnimationUsingKeyFrames.KeyFrames>
                                <DiscreteBooleanKeyFrame KeyTime="0" Value="True"/>
                            </BooleanAnimationUsingKeyFrames.KeyFrames>
                        </BooleanAnimationUsingKeyFrames>
                    </Storyboard>
                </BeginStoryboard>
            </EventTrigger.Actions>
        </EventTrigger>
    </Button.Triggers>
</Button>
<Button x:Name="StopButton" Content="Stop" Grid.Row="1" Grid.Column="1">
    <Button.Triggers>
        <EventTrigger RoutedEvent="Button.Click">
            <EventTrigger.Actions>
                <BeginStoryboard>
                    <Storyboard>
                        <BooleanAnimationUsingKeyFrames Storyboard.TargetName="ChbPlay" Storyboard.TargetProperty="IsChecked">
                            <BooleanAnimationUsingKeyFrames.KeyFrames>
                                <DiscreteBooleanKeyFrame KeyTime="0" Value="False"/>
                            </BooleanAnimationUsingKeyFrames.KeyFrames>
                        </BooleanAnimationUsingKeyFrames>
                    </Storyboard>
                </BeginStoryboard>
            </EventTrigger.Actions>
        </EventTrigger>
    </Button.Triggers>
</Button>
Rekshino
  • 6,954
  • 2
  • 19
  • 44
  • Thanks for replying. I tried your solution. But because my Storyboard has to bind to a property AnimationPath inside MyCircle class. I am getting "Cannot freeze this Storyboard timeline tree for use across threads" error. – Yose May 07 '21 at 20:07
  • The question was _"How to set animation trigger of an object inside ItemsControl to a button at upper level?"_ and was answered. If you have another issues try to find an answer on SO, or post new question with new issue. – Rekshino May 08 '21 at 09:53
  • You can take a look to the [WPF Animation “Cannot freeze this Storyboard timeline tree for use across threads”](https://stackoverflow.com/questions/1669750/wpf-animation-cannot-freeze-this-storyboard-timeline-tree-for-use-across-thread) – Rekshino May 08 '21 at 11:07
  • Thank you. I eventually go around this issue. – Yose May 10 '21 at 18:09