Šaman Bobo
Šaman Bobo

Reputation: 75

How to use style in xaml to move an ellipse on canvas

I'm practicing using xaml styles by making a simple traffic light controlled by radiobuttons. However, my code crashes because there is a reference error using Canvas.Top in the Ellipse style. Is there any way I could fix that while keeping everything in the style and not in the Ellipse itself?

Here is my code:

<Window x:Class="hw05.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:hw05"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">

    <Window.Resources>
        <Style TargetType="Ellipse">
            <Setter Property="Fill" Value="Red"></Setter>
            <Style.Triggers>

                <DataTrigger Binding="{Binding IsChecked, ElementName=RedRB}" Value="True">
                    <Setter Property="Fill" Value="Red"></Setter>
                    <DataTrigger.EnterActions>
                        <BeginStoryboard>
                            <Storyboard>
                                <DoubleAnimation
                                    Storyboard.TargetProperty="Canvas.Top" Duration="0:0:0.400" To="83">
                                </DoubleAnimation>
                            </Storyboard>
                        </BeginStoryboard>
                    </DataTrigger.EnterActions>
                </DataTrigger>

                <DataTrigger Binding="{Binding IsChecked, ElementName=YellowRB}" Value="True">
                    <Setter Property="Fill" Value="Orange"></Setter>
                    <DataTrigger.EnterActions>
                        <BeginStoryboard>
                            <Storyboard>
                                <DoubleAnimation
                                    Storyboard.TargetProperty="Canvas.Top" Duration="0:0:0.400" To="133">
                                </DoubleAnimation>
                            </Storyboard>
                        </BeginStoryboard>
                    </DataTrigger.EnterActions>
                </DataTrigger>

                <DataTrigger Binding="{Binding IsChecked, ElementName=GreenRB}" Value="True">
                    <Setter Property="Fill" Value="Green"></Setter>
                    <DataTrigger.EnterActions>
                        <BeginStoryboard>
                            <Storyboard>
                                <DoubleAnimation
                                    Storyboard.TargetProperty="Canvas.Top" Duration="0:0:0.400" To="183">
                                </DoubleAnimation>
                            </Storyboard>
                        </BeginStoryboard>
                    </DataTrigger.EnterActions>
                </DataTrigger>

            </Style.Triggers>
        </Style>
    </Window.Resources>
    
    <Canvas>
        <RadioButton Name="RedRB" GroupName="One" IsChecked="True" Canvas.Left="50" Canvas.Top="100">first</RadioButton>
        <RadioButton Name="YellowRB" GroupName="One" IsChecked="False" Canvas.Left="50" Canvas.Top="150">second</RadioButton>
        <RadioButton Name="GreenRB" GroupName="One" IsChecked="False" Canvas.Left="50" Canvas.Top="200">third</RadioButton>
        <Ellipse Name="Light" Width="50" Height="50" Canvas.Left="180"></Ellipse>
    </Canvas>
</Window>

EDIT included setter for a Canvas.Top value and used parentheses for (Canvas.Top):

<Window x:Class="hw05.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:hw05"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">

    <Window.Resources>
        <Style TargetType="Ellipse">
            <Setter Property="Fill" Value="Red"></Setter>
            <Style.Triggers>
                <DataTrigger Binding="{Binding IsChecked, ElementName=RedRB}" Value="True">
                    <Setter Property="Fill" Value="Red"></Setter>
                    <DataTrigger.EnterActions>
                        <BeginStoryboard>
                            <Storyboard>
                                <DoubleAnimation
                                    Storyboard.TargetProperty="(Canvas.Top)" Duration="0:0:0.400" To="83">
                                </DoubleAnimation>
                            </Storyboard>
                        </BeginStoryboard>
                    </DataTrigger.EnterActions>
                </DataTrigger>
                <DataTrigger Binding="{Binding IsChecked, ElementName=YellowRB}" Value="True">
                    <Setter Property="Fill" Value="Orange"></Setter>
                    <DataTrigger.EnterActions>
                        <BeginStoryboard>
                            <Storyboard>
                                <DoubleAnimation
                                    Storyboard.TargetProperty="(Canvas.Top)" Duration="0:0:0.400" To="133">
                                </DoubleAnimation>
                            </Storyboard>
                        </BeginStoryboard>
                    </DataTrigger.EnterActions>
                </DataTrigger>
                <DataTrigger Binding="{Binding IsChecked, ElementName=GreenRB}" Value="True">
                    <Setter Property="Fill" Value="Green"></Setter>
                    <DataTrigger.EnterActions>
                        <BeginStoryboard>
                            <Storyboard>
                                <DoubleAnimation
                                    Storyboard.TargetProperty="(Canvas.Top)" Duration="0:0:0.400" To="183">
                                </DoubleAnimation>
                            </Storyboard>
                        </BeginStoryboard>
                    </DataTrigger.EnterActions>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>
    
    <Canvas>
        <RadioButton Name="RedRB" GroupName="One" IsChecked="True" Canvas.Left="50" Canvas.Top="100">first</RadioButton>
        <RadioButton Name="YellowRB" GroupName="One" IsChecked="False" Canvas.Left="50" Canvas.Top="150">second</RadioButton>
        <RadioButton Name="GreenRB" GroupName="One" IsChecked="False" Canvas.Left="50" Canvas.Top="200">third</RadioButton>
        <Ellipse Name="Light" Width="50" Height="50" Canvas.Left="180" Canvas.Top="82"></Ellipse>
    </Canvas>
</Window>

Upvotes: 0

Views: 213

Answers (2)

mm8
mm8

Reputation: 169170

Canvas.Top is an attached property so you should add parentheses around it:

Storyboard.TargetProperty="(Canvas.Top)"

You should also set the From property of the DoubleAnimation to a valid double such as for example 0:

<DoubleAnimation
    Storyboard.TargetProperty="(Canvas.Top)" Duration="0:0:0.400" From="0" To="133">

Alternatively, you could set the Canvas.Top property of the element being animated to an initial value:

<Ellipse Name="Light" Width="50" Height="50" Canvas.Top="10" Canvas.Left="180"></Ellipse>

Or add a Setter to the Style:

<Setter Property="Canvas.Top" Value="10"/>

Upvotes: 1

Clemens
Clemens

Reputation: 128062

When you animate a property without setting the animation's From property, the animation starts from the current value of the property.

For Canvas.Top and the other Canvas attached properties, the default value (and hence the current value) is double.NaN, not 0.

Just set an initial value by a Style Setter:

<Style TargetType="Ellipse">
    <Setter Property="Canvas.Top" Value="0"/>
    ...
</Style>

Or directly on the Ellipse:

<Ellipse Canvas.Top="0" .../>

You would also need to use the correct animated property path for attached properties, which uses parentheses:

<DoubleAnimation Storyboard.TargetProperty="(Canvas.Top)" .../>

Upvotes: 1

Related Questions