Как связать нажатие клавиши на клавиатуре и анимацию конкретной кнопки

Рейтинг: 0Ответов: 1Опубликовано: 05.06.2023

В чем соль: пытаюсь учиться программированию на C# для создания графических приложений под Windows. Вот захотел сделать калькулятор на WPF. Соответственно, хочу сделать все в более или менее приемлемом для меня виде, то есть с хоть какой-нибудь анимацией и т.д. И споткнулся на одном шаге: хочу связать нажатия клавиш и реакцию кнопок на эти нажатия. То есть как в калькуляторе в Windows: нажал на единичку - проигралась анимация нажатия кнопки с цифрой 1. И я не знаю, как связать нажатие клавиши на клавиатуре и проигрывание анимации кнопки, соответствующей клавише клавиатуры.

Таким образов выглядит калькулятор

<Window x:Class="SpaceFly.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:SpaceFly"
        xmlns:fd="clr-namespace:System.Collections.Generic;assembly=mscorlib"
        mc:Ignorable="d"
        Title="Калькулятор" Height="600" Width="400"
        Background="Black"
        KeyDown="Window_KeyDown">
    <Window.Resources>
        <ControlTemplate x:Key="ButtTemplate" TargetType="Button">
            <Border CornerRadius="10" Name="border" DataContext="">
                <Border.Background>
                    <SolidColorBrush Color="#ff8e00"/>
                </Border.Background>
                <ContentPresenter
                                  Margin="{TemplateBinding Padding}" 
                                  HorizontalAlignment="Center"
                                  VerticalAlignment="Center">
                </ContentPresenter>
            </Border>
            <ControlTemplate.Triggers>
                <EventTrigger RoutedEvent="MouseEnter">
                    <BeginStoryboard>
                        <Storyboard>
                            <ColorAnimation Storyboard.TargetName="border"
                                            Duration="0:0:0.05" 
                                            Storyboard.TargetProperty="Background.Color" 
                                            To="#ee8400">
                            </ColorAnimation>
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger>
                <EventTrigger RoutedEvent="MouseLeave">
                    <BeginStoryboard>
                        <Storyboard>
                            <ColorAnimation Storyboard.TargetName="border"
                                            Duration="0:0:0.02" 
                                            Storyboard.TargetProperty="Background.Color"
                                            To="#ff8e00">
                            </ColorAnimation>
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger>
                <EventTrigger RoutedEvent="MouseDown">
                    <BeginStoryboard>
                        <Storyboard>
                            <ColorAnimationUsingKeyFrames Storyboard.TargetName="border" 
                                                          Storyboard.TargetProperty="Background.Color" 
                                                          Duration="0:0:0.2">
                                <LinearColorKeyFrame Value="#d57700"
                                                     KeyTime="0:0:0.1"/>
                                <LinearColorKeyFrame Value="#ff8e00" 
                                                     KeyTime="0:0:0.2"/>
                            </ColorAnimationUsingKeyFrames>
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger>
            </ControlTemplate.Triggers>
        </ControlTemplate>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="2*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>

        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Viewbox Grid.ColumnSpan="4"
                 Grid.Row="0" 
                 Grid.Column="0" 
                 HorizontalAlignment="Right" 
                 VerticalAlignment="Center"
                 >
            <TextBox BorderThickness="0"
                     Name="DataText" 
                     IsReadOnly="True"  
                     Background="Black" 
                     MaxLength="16" 
                     FontSize="90"  
                     Foreground="White" 
                     HorizontalContentAlignment="Right" 
                     VerticalContentAlignment="Center"
                     >
                0
            </TextBox>
        </Viewbox>

        <Button x:Name="ButtProcent"
                Grid.Row="1" 
                Grid.Column="0" 
                Content="%" 
                FontSize="30"
                Margin="5,5,5,5" 
                Foreground="White"  
                Template="{StaticResource ButtTemplate}"
                Tag="Procent"
                >
        </Button>
        <Button x:Name="ButtCE"
                Grid.Row="1" 
                Grid.Column="1" 
                Content="CE" 
                FontSize="30" 
                Margin="5,5,5,5"
                Foreground="White"  
                Template="{StaticResource ButtTemplate}"
                Tag="Delete"
                />
        <Button x:Name="ButtDelete"
                Grid.Row="1" 
                Grid.Column="2" 
                Content="&lt;-" 
                Grid.ColumnSpan="2"
                FontSize="30" 
                Margin="5,5,5,5" 
                Foreground="White"  
                Template="{StaticResource ButtTemplate}"
                />
        <Button x:Name="ButtTurn"
                Grid.Row="2" 
                Grid.Column="0" 
                Content="1/x" 
                FontSize="30" 
                Margin="5,5,5,5" 
                Foreground="White"  
                Template="{StaticResource ButtTemplate}"
                Tag="Turn"
                />
        <Button x:Name="ButtSqr"
                Grid.Row="2" 
                Grid.Column="1" 
                Content="x^2" 
                FontSize="30" 
                Margin="5,5,5,5" 
                Foreground="White"  
                Template="{StaticResource ButtTemplate}"
                Tag="Sqr"
                />
        <Button x:Name="ButtSqrt"
                Grid.Row="2" 
                Grid.Column="2" 
                Content="x^0.5" 
                FontSize="30" 
                Margin="5,5,5,5" 
                Foreground="White"  
                Template="{StaticResource ButtTemplate}"
                />
        <Button x:Name="ButtDiv"
                Grid.Row="2" 
                Grid.Column="3" 
                Content="/" 
                FontSize="30" 
                Margin="5,5,5,5" 
                Foreground="White"  
                Template="{StaticResource ButtTemplate}"
                Tag="Division"
                />
        <Button x:Name="ButtNum1"
                Grid.Row="3" 
                Grid.Column="0" 
                Content="1" 
                FontSize="30" 
                Margin="5,5,5,5" 
                Foreground="White"  
                Template="{StaticResource ButtTemplate}"
                Tag="1"
                />
        <Button x:Name="ButtNum2"
                Grid.Row="3" 
                Grid.Column="1" 
                Content="2" 
                FontSize="30" 
                Margin="5,5,5,5" 
                Foreground="White"  
                Template="{StaticResource ButtTemplate}"
                Tag="2"
                 />
        <Button x:Name="ButtNum3"
                Grid.Row="3" 
                Grid.Column="2" 
                Content="3" 
                FontSize="30" 
                Margin="5,5,5,5" 
                Foreground="White"  
                Template="{StaticResource ButtTemplate}"
                Tag="3"
                />
        <Button x:Name="ButtMult"
                Grid.Row="3" 
                Grid.Column="3" 
                Content="*" 
                FontSize="30" 
                Margin="5,5,5,5" 
                Foreground="White"  
                Template="{StaticResource ButtTemplate}"
                Tag="Multiply"
                />
        <Button x:Name="ButtNum4"
                Grid.Row="4" 
                Grid.Column="0" 
                Content="4" 
                FontSize="30" 
                Margin="5,5,5,5" 
                Foreground="White"  
                Template="{StaticResource ButtTemplate}"
                Tag="4"
                />
        <Button x:Name="ButtNum5"
                Grid.Row="4" 
                Grid.Column="1" 
                Content="5" 
                FontSize="30" 
                Margin="5,5,5,5" 
                Foreground="White"  
                Template="{StaticResource ButtTemplate}"
                Tag="5"
                />
        <Button x:Name="ButtNum6"
                Grid.Row="4" 
                Grid.Column="2" 
                Content="6" 
                FontSize="30" 
                Margin="5,5,5,5" 
                Foreground="White"  
                Template="{StaticResource ButtTemplate}"
                Tag="6"
                />
        <Button x:Name="ButtDiff"
                Grid.Row="4" 
                Grid.Column="3" 
                Content="-" 
                FontSize="30" 
                Margin="5,5,5,5" 
                Foreground="White"  
                Template="{StaticResource ButtTemplate}"
                Tag="Difference"
                />
        <Button x:Name="ButtNum7"
                Grid.Row="5" 
                Grid.Column="0" 
                Content="7" 
                FontSize="30" 
                Margin="5,5,5,5" 
                Foreground="White"  
                Template="{StaticResource ButtTemplate}"
                Tag="7"
                />
        <Button x:Name="ButtNum8"
                Grid.Row="5" 
                Grid.Column="1" 
                Content="8" 
                FontSize="30" 
                Margin="5,5,5,5" 
                Foreground="White"  
                Template="{StaticResource ButtTemplate}"
                Tag="8"
                />
        <Button x:Name="ButtNum9"
                Grid.Row="5" 
                Grid.Column="2" 
                Content="9" 
                FontSize="30" 
                Margin="5,5,5,5" 
                Foreground="White"  
                Template="{StaticResource ButtTemplate}"
                Tag="9"
                />
        <Button x:Name="ButtSum"
                Grid.Row="5" 
                Grid.Column="3" 
                Content="+" 
                FontSize="30" 
                Margin="5,5,5,5" 
                Foreground="White"  
                Template="{StaticResource ButtTemplate}"
                Tag="Sum"
                />
        <Button x:Name="ButtPlusOrMin"
                Grid.Row="6" 
                Grid.Column="0" 
                Content="+/-" 
                FontSize="30" 
                Margin="5,5,5,5" 
                Foreground="White"  
                Template="{StaticResource ButtTemplate}"
                />
        <Button x:Name="ButtNum0"
                Grid.Row="6" 
                Grid.Column="1" 
                Content="0" 
                FontSize="30" 
                Margin="5,5,5,5" 
                Foreground="White"  
                Template="{StaticResource ButtTemplate}"
                Tag="0"
                />
        <Button x:Name="ButtPoint"
                Grid.Row="6" 
                Grid.Column="2" 
                Content="," 
                FontSize="30" 
                Margin="5,5,5,5" 
                Foreground="White"  
                Template="{StaticResource ButtTemplate}"
                Tag="Point"
                />
        <Button x:Name="ButtEqual"
                Grid.Row="6" 
                Grid.Column="3" 
                Content="=" 
                FontSize="30" 
                Margin="5,5,5,5" 
                Foreground="White"  
                Template="{StaticResource ButtTemplate}"
                Tag="Equal"
                />
    </Grid>
</Window>

Ответы

▲ 0Принят

Небольшой примерчик. Не смог нормально разобраться с анимациями по нажатию клавиши, но заставил работать это через код. Уверен, что существует более адекватное решение.

Поправил анимацию клика. Выбросил часть дублирующего кода.

Получилась такая разметка:

<Window.Resources>
    <Storyboard x:Key="StoryboardFocus">
        <ColorAnimation Storyboard.TargetName="border"
                        Duration="0:0:0.05" 
                        Storyboard.TargetProperty="Background.Color" 
                        To="#ffae10"/>
    </Storyboard>
    <Storyboard x:Key="StoryboardRestore">
        <ColorAnimation Storyboard.TargetName="border"
                        Duration="0:0:0.05" 
                        Storyboard.TargetProperty="Background.Color" 
                        To="#ff8e00"/>
    </Storyboard>
    <Storyboard x:Key="StoryboardHover">
        <ColorAnimation Storyboard.TargetName="border"
                        Duration="0:0:0.05" 
                        Storyboard.TargetProperty="Background.Color" 
                        To="#ee8400"/>
    </Storyboard>
    <ControlTemplate x:Key="ButtTemplate" TargetType="Button">
        <Border CornerRadius="10" Name="border" Background="#ff8e00" Padding="{TemplateBinding Padding}">
            <ContentPresenter HorizontalAlignment="Center"
                              VerticalAlignment="Center"
                              IsHitTestVisible="False"/>
        </Border>
        <ControlTemplate.Triggers>
            <Trigger Property="IsMouseOver" Value="True">
                <Trigger.EnterActions>
                    <StopStoryboard BeginStoryboardName="ButtonFocusBegin"/>
                    <StopStoryboard BeginStoryboardName="ButtonFocusRestore"/>
                    <BeginStoryboard Storyboard="{StaticResource StoryboardHover}"/>
                </Trigger.EnterActions>
                <Trigger.ExitActions>
                    <BeginStoryboard Storyboard="{StaticResource StoryboardRestore}"/>
                </Trigger.ExitActions>
            </Trigger>
            <Trigger Property="IsMouseDirectlyOver" Value="True">
                <Trigger.EnterActions>
                    <BeginStoryboard Name="ButtonFocusBegin" Storyboard="{StaticResource StoryboardFocus}"/>
                </Trigger.EnterActions>
                <Trigger.ExitActions>
                    <BeginStoryboard Name="ButtonFocusRestore" Storyboard="{StaticResource StoryboardRestore}"/>
                </Trigger.ExitActions>
            </Trigger>
        </ControlTemplate.Triggers>
    </ControlTemplate>
    <Style TargetType="{x:Type Button}">
        <Setter Property="Margin" Value="5"/>
        <Setter Property="Foreground" Value="White"/>
        <Setter Property="Template" Value="{StaticResource ButtTemplate}"/>
        <Setter Property="FontSize" Value="30"/>
        <Setter Property="Focusable" Value="False"/>
        <EventSetter Event="Click" Handler="Button_Click"/>
    </Style>
</Window.Resources>
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="2*"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <Viewbox Grid.ColumnSpan="4" HorizontalAlignment="Right">
        <TextBox BorderThickness="0"
                 x:Name="DataText" 
                 IsReadOnly="True"
                 FontSize="90"  
                 Foreground="White" 
                 HorizontalContentAlignment="Right" 
                 VerticalContentAlignment="Center"
                 Background="{x:Null}"
                 Text="0"/>
    </Viewbox>
    <Button Grid.Row="1" Grid.Column="0" Content="%"/>
    <Button Grid.Row="1" Grid.Column="1" Content="CE"/>
    <Button Grid.Row="1" Grid.Column="2" Content="&lt;-" Grid.ColumnSpan="2"/>
    <Button Grid.Row="2" Grid.Column="0" Content="1/x"/>
    <Button Grid.Row="2" Grid.Column="1" Content="x^2"/>
    <Button Grid.Row="2" Grid.Column="2" Content="x^0.5"/>
    <Button Grid.Row="2" Grid.Column="3" Content="/"/>
    <Button Grid.Row="3" Grid.Column="0" Content="1"/>
    <Button Grid.Row="3" Grid.Column="1" Content="2"/>
    <Button Grid.Row="3" Grid.Column="2" Content="3"/>
    <Button Grid.Row="3" Grid.Column="3" Content="*"/>
    <Button Grid.Row="4" Grid.Column="0" Content="4"/>
    <Button Grid.Row="4" Grid.Column="1" Content="5"/>
    <Button Grid.Row="4" Grid.Column="2" Content="6"/>
    <Button Grid.Row="4" Grid.Column="3" Content="-"/>
    <Button Grid.Row="5" Grid.Column="0" Content="7"/>
    <Button Grid.Row="5" Grid.Column="1" Content="8"/>
    <Button Grid.Row="5" Grid.Column="2" Content="9"/>
    <Button Grid.Row="5" Grid.Column="3" Content="+"/>
    <Button Grid.Row="6" Grid.Column="0" Content="+/-"/>
    <Button Grid.Row="6" Grid.Column="1" Content="0"/>
    <Button Grid.Row="6" Grid.Column="2" Content=","/>
    <Button Grid.Row="6" Grid.Column="3" Content="="/>
</Grid>

И вот такой код

public partial class MainWindow : Window
{
    private readonly Button[] buttons;
    private readonly Storyboard animFocus;
    private readonly Storyboard animRestore;

    public MainWindow()
    {
        InitializeComponent();
        buttons = ((Grid)Content).Children.OfType<Button>().ToArray();
        animFocus = (Storyboard)FindResource("StoryboardFocus");
        animRestore = (Storyboard)FindResource("StoryboardRestore");
    }

    private async void Window_KeyDown(object sender, KeyEventArgs e)
    {
        if (e.IsRepeat) return;
        try
        {
            Key key = e.Key;
            if ((key >= Key.D0 && key <= Key.D9) || (key >= Key.NumPad0 && key <= Key.NumPad9))
            {
                string digit = e.Key.ToString()[^1].ToString();
                Button button = buttons.First(b => (string)b.Content == digit);
                Task task = BlinkButtonAsync(button);
                button.RaiseEvent(new RoutedEventArgs(ButtonBase.ClickEvent)); // вызов обработчика события 
                await task;
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }

    private async Task BlinkButtonAsync(Button button)
    {
        FrameworkElement border = (FrameworkElement)button.Template.FindName("border", button);
        border.BeginStoryboard(animFocus, HandoffBehavior.SnapshotAndReplace, true);
        await Task.Delay(100);
        border.BeginStoryboard(animRestore, HandoffBehavior.SnapshotAndReplace, true);
        await Task.Delay(50);
        animFocus.Remove(border);
        animRestore.Remove(border);
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        Button btn = (Button)sender;
        string text = (string)btn.Content;
        Debug.WriteLine(text); // выводит в консоль студии текст с нажатой кнопки
    }
}

Здесь я для простоты примера сделал нажатие с клавы только для кнопок 0-9. Остальные уж сами допишите.

Обратите внимание, что ни Tag, не x:Name я не использую. Чтобы различить кнопки, достаточно одного Content.