How to animate an Image in a button to shake every 30 seconds in WPF?

后端 未结 2 711
清歌不尽
清歌不尽 2021-01-17 02:37

I not good when it comes to dealing with anything with styles and animations.

I was hoping to be able to get some help on making an Image that is the only content o

相关标签:
2条回答
  • 2021-01-17 03:18

    Create a WPF custom control by adding a new item in VS and then navigating to the WPF templates. This will allow you to select "Custom Control (WPF)". Name it "ShakyImageControl". This will create a Themes folder with a generic.xaml in it and a "ShakyImageControl.cs" class file. In the generic.xaml replace the existing style with the following:

    <Style TargetType="{x:Type local:ShakyImageControl}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:ShakyImageControl}">
                    <Image x:Name="image" Source="{TemplateBinding ImageSource}" RenderTransformOrigin="0.5,0.5">
                        <Image.RenderTransform>
                            <TransformGroup>
                                <RotateTransform x:Name="Rotaty"/>
                            </TransformGroup>
                        </Image.RenderTransform>       
                    </Image>
                    <ControlTemplate.Triggers>
                        <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Button}, Path=Visibility}" Value="Visible">
                            <DataTrigger.EnterActions>
                                <BeginStoryboard Name="fred">
                                    <Storyboard AutoReverse="False" RepeatBehavior="Forever" Duration="0:0:30" Storyboard.TargetName="Rotaty" Storyboard.TargetProperty="Angle">
                                        <DoubleAnimationUsingKeyFrames>
                                            <EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="-12.0"/>
                                            <EasingDoubleKeyFrame KeyTime="0:0:0.4" Value="12.0"/>
                                            <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="0"/>
                                        </DoubleAnimationUsingKeyFrames>
                                    </Storyboard>
                                </BeginStoryboard>
                            </DataTrigger.EnterActions>
                            <DataTrigger.ExitActions>
                                <StopStoryboard BeginStoryboardName="fred"/>
                            </DataTrigger.ExitActions>
                        </DataTrigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    

    In the ShakyImageControl class add a dependency property as follows:

    static ShakyImageControl()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(ShakyImageControl), new FrameworkPropertyMetadata(typeof(ShakyImageControl)));
        }
    
        public ImageSource ImageSource
        {
            get { return (ImageSource)GetValue(ImageSourceProperty); }
            set { SetValue(ImageSourceProperty, value); }
        }
    
        // Using a DependencyProperty as the backing store for ImageSource.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ImageSourceProperty =
            DependencyProperty.Register("ImageSource", typeof(ImageSource), typeof(ShakyImageControl), new UIPropertyMetadata(null));
    

    To use the shakyImage in a button just do:

    <Button Height="50" Width="500" Name="showy" Visibility="Collapsed"> 
            <local:ShakyImageControl ImageSource="\Expand.png"/>
        </Button>
    

    local is an xml namespace like "xmlns:local="clr-namespace:WpfApplication6"

    NB: your custom control can be in a seperate assembly if you want

    0 讨论(0)
  • 2021-01-17 03:22

    Here it is an attached behaviour. Just be careful as it may be destructive if your control has existing render transforms

    public class Wibble
    {
        public static bool GetWobble(DependencyObject obj)
        {
            return (bool)obj.GetValue(WobbleProperty);
        }
    
        public static void SetWobble(DependencyObject obj, bool value)
        {
            obj.SetValue(WobbleProperty, value);
        }
    
        public static readonly DependencyProperty WobbleProperty = DependencyProperty.RegisterAttached("Wobble", typeof(bool), typeof(Wibble), new UIPropertyMetadata(false, new PropertyChangedCallback(OnWobbleChanged)));
    
        private static void OnWobbleChanged(object sender, DependencyPropertyChangedEventArgs args)
        {
            var image = sender as Image;
    
            if (image == null)
                throw new InvalidOperationException("only images can wobble!");
    
            // don't really need this check (the find ancestor binding would still find the button), but the spec said the image should be the only child of the button
            var button = LogicalTreeHelper.GetParent(image) as Button;
            if (button == null)
                throw new InvalidOperationException("only images that are the only child of a button can wobble!");
    
            var previousStyle = image.Style;
    
            var newStyle = new Style(image.GetType(), previousStyle);
    
            // this will override any existing render transform + origin on the button, hope they didn't already have one (and I'm too lazy to check)
            newStyle.Setters.Add(new Setter(Image.RenderTransformProperty, new RotateTransform(0)));
            newStyle.Setters.Add(new Setter(Image.RenderTransformOriginProperty, new Point(0.5, 0.5)));
    
            var trigger = new DataTrigger();
    
            var binding = new Binding();
    
            var relativeSource = new RelativeSource();
            relativeSource.Mode = RelativeSourceMode.FindAncestor;
            relativeSource.AncestorType = typeof(Button);
    
            binding.RelativeSource = relativeSource;
            binding.Path = new PropertyPath(Button.VisibilityProperty);
    
            trigger.Binding = binding;
            trigger.Value = Visibility.Visible;
    
            var storyboard = new Storyboard();
    
            var animation = new DoubleAnimationUsingKeyFrames();
            animation.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("(0).(1)", Image.RenderTransformProperty, RotateTransform.AngleProperty));
            animation.Duration = new Duration(TimeSpan.FromSeconds(5)); // spec said 30, but i wanted to actually see it happen!
            animation.KeyFrames.Add(new LinearDoubleKeyFrame(-12, KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0.2))));
            animation.KeyFrames.Add(new LinearDoubleKeyFrame(12, KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0.4))));
            animation.KeyFrames.Add(new LinearDoubleKeyFrame(0, KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0.5))));
    
            storyboard.Children.Add(animation);
            storyboard.RepeatBehavior = RepeatBehavior.Forever;
    
            var beginStoryboard = new BeginStoryboard();
            beginStoryboard.Storyboard = storyboard;
            beginStoryboard.Name = "its_wobble_time"; // it is
    
            trigger.EnterActions.Add(beginStoryboard);
    
            var removeStoryboard = new RemoveStoryboard();
            removeStoryboard.BeginStoryboardName = beginStoryboard.Name;
    
            trigger.ExitActions.Add(removeStoryboard);
    
            newStyle.Triggers.Add(trigger);
    
            image.Style = newStyle;
        }
    }
    

    here is how it would be used:

    <Button Width="100" Height="25" >
            <Image Source="Untitled.png" xmlns:local="clr-namespace:WpfApplication17" local:Wibble.Wobble="True" />
        </Button>
    
    0 讨论(0)
提交回复
热议问题