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
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
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>