22
\$\begingroup\$

This is my first time messing with non-rectangular windows and animations in WPF, so while I'm working on all the bugs I'm having with the actual game code, I'm submitting the XAML for review - I don't like how redundant it is, but I'm sure there's a clever way to simplify it.

<Window x:Class="SimonSays.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:simonSays="clr-namespace:SimonSays"
        mc:Ignorable="d"
        Height="480" Width="480" 
        AllowsTransparency="True" Background="Transparent" WindowStyle="None" 
        d:DataContext="{d:DesignInstance simonSays:SimonSaysRound}">
    <Window.Clip>
        <EllipseGeometry RadiusX="240" RadiusY="240" Center="242,242" />
    </Window.Clip>
    <Border Background="Black" BorderThickness="2">
        <Border.Clip>
            <EllipseGeometry Center="240,240" RadiusX="238" RadiusY="238" />
        </Border.Clip>
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="*" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>

            <StackPanel x:Name="MessageBar" Grid.Row="1" Grid.ColumnSpan="2" Margin="4" Height="0">
                <TextBlock x:Name="GameButton" Foreground="White" HorizontalAlignment="Center" FontSize="24" FontWeight="Bold" />
                <TextBlock x:Name="GameScoreLabel" Text="{Binding Score, StringFormat=Your score: \{0\}}" Foreground="White" HorizontalAlignment="Center" FontWeight="Bold" IsEnabled="False" />
            </StackPanel>

            <Border x:Name="Green" Margin="1" CornerRadius="16" MouseDown="Green_MouseDown">
                <Border.Clip>
                    <EllipseGeometry Center="240,240" RadiusX="240" RadiusY="240" />
                </Border.Clip>
                <Border.Style>
                    <Style>
                        <Setter Property="Border.Background">
                            <Setter.Value>
                                <RadialGradientBrush GradientOrigin="1,1" RadiusX="1" RadiusY="1">
                                    <GradientStop Color="LightGreen" Offset="0.0" />
                                    <GradientStop Color="DarkGreen" Offset="0.5" />
                                </RadialGradientBrush>
                            </Setter.Value>
                        </Setter>
                        <Style.Triggers>
                            <EventTrigger RoutedEvent="Border.MouseEnter">
                                <BeginStoryboard>
                                    <Storyboard TargetProperty="Background.GradientStops[1].Offset" Duration="0:0:0.25">
                                        <DoubleAnimation To="1.0" />
                                        <Storyboard TargetProperty="Background.GradientStops[0].Offset">
                                            <DoubleAnimation To="0.2" />
                                        </Storyboard>
                                    </Storyboard>
                                </BeginStoryboard>
                            </EventTrigger>
                            <EventTrigger RoutedEvent="Border.MouseLeave">
                                <BeginStoryboard>
                                    <Storyboard TargetProperty="Background.GradientStops[1].Offset" Duration="0:0:0.25">
                                        <DoubleAnimation To="0.5" />
                                        <Storyboard TargetProperty="Background.GradientStops[0].Offset">
                                            <DoubleAnimation To="0.0" />
                                        </Storyboard>
                                    </Storyboard>
                                </BeginStoryboard>
                            </EventTrigger>
                        </Style.Triggers>
                    </Style>
                </Border.Style>
            </Border>
            <Border Grid.Column="1" x:Name="Red" Margin="1" CornerRadius="16" MouseDown="Red_MouseDown">
                <Border.Clip>
                    <EllipseGeometry Center="-4,240" RadiusX="240" RadiusY="240" />
                </Border.Clip>
                <Border.Style>
                    <Style>
                        <Setter Property="Border.Background">
                            <Setter.Value>
                                <RadialGradientBrush GradientOrigin="0,1" RadiusX="1" RadiusY="1">
                                    <GradientStop Color="LightPink" Offset="0.0" />
                                    <GradientStop Color="DarkRed" Offset="0.5" />
                                </RadialGradientBrush>
                            </Setter.Value>
                        </Setter>
                        <Style.Triggers>
                            <EventTrigger RoutedEvent="Border.MouseEnter">
                                <BeginStoryboard>
                                    <Storyboard TargetProperty="Background.GradientStops[1].Offset" Duration="0:0:0.25">
                                        <DoubleAnimation To="1.0" />
                                        <Storyboard TargetProperty="Background.GradientStops[0].Offset">
                                            <DoubleAnimation To="0.2" />
                                        </Storyboard>
                                    </Storyboard>
                                </BeginStoryboard>
                            </EventTrigger>
                            <EventTrigger RoutedEvent="Border.MouseLeave">
                                <BeginStoryboard>
                                    <Storyboard TargetProperty="Background.GradientStops[1].Offset" Duration="0:0:0.25">
                                        <DoubleAnimation To="0.5" />
                                        <Storyboard TargetProperty="Background.GradientStops[0].Offset">
                                            <DoubleAnimation To="0.0" />
                                        </Storyboard>
                                    </Storyboard>
                                </BeginStoryboard>
                            </EventTrigger>
                        </Style.Triggers>
                    </Style>
                </Border.Style>
            </Border>
            <Border Grid.Row="2" x:Name="Yellow" Margin="1" CornerRadius="16" MouseDown="Yellow_MouseDown">
                <Border.Clip>
                    <EllipseGeometry Center="240,-4" RadiusX="240" RadiusY="240" />
                </Border.Clip>
                <Border.Style>
                    <Style>
                        <Setter Property="Border.Background">
                            <Setter.Value>
                                <RadialGradientBrush GradientOrigin="1,0" RadiusX="1" RadiusY="1">
                                    <GradientStop Color="LightYellow" Offset="0.0" />
                                    <GradientStop Color="Orange" Offset="0.5" />
                                </RadialGradientBrush>
                            </Setter.Value>
                        </Setter>
                        <Style.Triggers>
                            <EventTrigger RoutedEvent="Border.MouseEnter">
                                <BeginStoryboard>
                                    <Storyboard TargetProperty="Background.GradientStops[1].Offset" Duration="0:0:0.25">
                                        <DoubleAnimation To="1.0" />
                                        <Storyboard TargetProperty="Background.GradientStops[0].Offset">
                                            <DoubleAnimation To="0.2" />
                                        </Storyboard>
                                    </Storyboard>
                                </BeginStoryboard>
                            </EventTrigger>
                            <EventTrigger RoutedEvent="Border.MouseLeave">
                                <BeginStoryboard>
                                    <Storyboard TargetProperty="Background.GradientStops[1].Offset" Duration="0:0:0.25">
                                        <DoubleAnimation To="0.5" />
                                        <Storyboard TargetProperty="Background.GradientStops[0].Offset">
                                            <DoubleAnimation To="0.0" />
                                        </Storyboard>
                                    </Storyboard>
                                </BeginStoryboard>
                            </EventTrigger>
                        </Style.Triggers>
                    </Style>
                </Border.Style>
            </Border>
            <Border Grid.Row="2" Grid.Column="1" x:Name="Blue" Margin="1" CornerRadius="16" MouseDown="Blue_MouseDown">
                <Border.Clip>
                    <EllipseGeometry Center="-4,-4" RadiusX="240" RadiusY="240" />
                </Border.Clip>
                <Border.Style>
                    <Style>
                        <Setter Property="Border.Background">
                            <Setter.Value>
                                <RadialGradientBrush GradientOrigin="0,0" RadiusX="1" RadiusY="1">
                                    <GradientStop Color="LightSkyBlue" Offset="0.0" />
                                    <GradientStop Color="Blue" Offset="0.5" />
                                </RadialGradientBrush>
                            </Setter.Value>
                        </Setter>
                        <Style.Triggers>
                            <EventTrigger RoutedEvent="Border.MouseEnter">
                                <BeginStoryboard>
                                    <Storyboard TargetProperty="Background.GradientStops[1].Offset" Duration="0:0:0.25">
                                        <DoubleAnimation To="1.0" />
                                        <Storyboard TargetProperty="Background.GradientStops[0].Offset">
                                            <DoubleAnimation To="0.2" />
                                        </Storyboard>
                                    </Storyboard>
                                </BeginStoryboard>
                            </EventTrigger>
                            <EventTrigger RoutedEvent="Border.MouseLeave">
                                <BeginStoryboard>
                                    <Storyboard TargetProperty="Background.GradientStops[1].Offset" Duration="0:0:0.25">
                                        <DoubleAnimation To="0.5" />
                                        <Storyboard TargetProperty="Background.GradientStops[0].Offset">
                                            <DoubleAnimation To="0.0" />
                                        </Storyboard>
                                    </Storyboard>
                                </BeginStoryboard>
                            </EventTrigger>
                        </Style.Triggers>
                    </Style>
                </Border.Style>
            </Border>
        </Grid>
    </Border>
</Window>

The result is a circular window with 4 quadrants and a black horizontal band that serves as a handle for dragging the window around:

Simon-Says quadrants

I'm also using it for displaying messages, and the player's score:

"Start!" "Well done! ready? | Your score: 5"

The buttons/quadrants are disabled while the message band is expanded (cannot be clicked; they still react to mouse-over though), and re-enabled when it's collapsed after clicking on the message.

Without a control box, the window can only be closed by clicking on the "Game Over" label:

"Game Over! | Your score: 0"

I have this C# code (among other code that isn't ready for review) in the code-behind, for animating the button clicks and the message band:

    private void AnimateMessageBand(double height)
    {
        var animation = new DoubleAnimation(height, new Duration(TimeSpan.FromMilliseconds(200)));
        RegisterName(MessageBar.Name, MessageBar);
        Storyboard.SetTargetName(animation, MessageBar.Name);
        Storyboard.SetTargetProperty(animation, new PropertyPath("Height"));
        var story = new Storyboard();
        story.Children.Add(animation);
        story.Begin(MessageBar);
        story.Remove();
    }

    private readonly IDictionary<SimonButton, Border> _buttons;

    public void HighlightSimonButton(SimonButton button)
    {
        var animation = new DoubleAnimation(0, 0.75, new Duration(TimeSpan.FromMilliseconds(100)));
        RegisterName(button.ToString(), _buttons[button]);
        Storyboard.SetTargetName(animation, button.ToString());
        Storyboard.SetTargetProperty(animation, new PropertyPath("Background.GradientStops[1].Offset"));
        var story = new Storyboard();
        story.Children.Add(animation);
        story.Begin(_buttons[button]);
        story.Remove();
    }
\$\endgroup\$
6
\$\begingroup\$
  1. You use Border to create a button, instead of using an actual Button class with custom template. Because of this you'll have to manually manage all the interactions, which are already implemented in regular Button (Clicked, IsPressed, Command, their interaction with IsEnabled property and keyboard, etc.). So i suggest you use an actual button.
  2. Well, your storyboards are clearly copy-pasted. :) Why don't you move them to resources?
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.