Xaml transformation keep existing transformations non-affected transformations - wpf

I modified a toggle switch so I can use an icon ontop of it. Then I added a custom property which holds the image information and it's rotation. The definition of my image looks somewhat like this:
<Image x:Name="SwitchKnobActive" Source="{Binding Path=(common:FilterSwitchImageHolder.ActiveImage), RelativeSource={RelativeSource TemplatedParent}}" Width="50" Visibility="{Binding IsOn, RelativeSource={RelativeSource Mode=TemplatedParent}, Converter={StaticResource BoolToVisibilityConverter}, ConverterParameter=True}" RenderTransformOrigin="0.5, 0.5">
<Image.RenderTransform>
<TransformGroup>
<TranslateTransform x:Name="KnobActiveTranslateTransform"/>
<RotateTransform Angle="{Binding Path=(common:FilterSwitchImageHolder.Angle), RelativeSource={RelativeSource TemplatedParent}}" />
</TransformGroup>
</Image.RenderTransform>
</Image>
Now when I select the image I want to scale it by the factor of two, when I do this I loose the rotation. I tried setting the rotation again like above but it got ignored.. Is there a way how I can keep an existing transformation?
Edit:
The transformation on click XAML:
<VisualState x:Name="Pressed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="RenderTransform" Storyboard.TargetName="SwitchKnobActive">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value >
<ScaleTransform ScaleX="2" ScaleY="2"></ScaleTransform>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
FYI: All this code is within a control template of a toggle switch.

How are you applying scale transform?
Usual pattern for this would be to add dummy scale transform to the transform group and modify it later. This way all other transforms are not being replaced.
<Image.RenderTransform>
<TransformGroup>
<ScaleTransform x:Name="myScaleTransform" ScaleX="1.0" ScaleY="1.0">
<TranslateTransform x:Name="KnobActiveTranslateTransform"/>
<RotateTransform Angle="{Binding Path=(common:FilterSwitchImageHolder.Angle), RelativeSource={RelativeSource TemplatedParent}}" />
</TransformGroup>
</Image.RenderTransform>
You can later access it from xaml storyboard like this:
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="SwitchKnobActive"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)">
<SplineDoubleKeyFrame KeyTime="00:00:00" Value="1"/>

Related

One Storyboard for multiple triggers

Context: I have two Storyboards in my UserControl. One of them is for sliding the UserControl in and one is for sliding it out.
All I'm doing for the slide in/out is setting the margin to a negative value for sliding out and to zero for sliding in.
Now, I want the sbShowLeftMenu storyboard to be executed when the UserControl's visibility is set to Visible. Also, I want to be able to manually slide the UserControl in/out using the Buttons BtnHide & BtnShow.
Now if the UserControl becomes visible the sbShowLeftMenu is activated and the UserControls gets moved in. Switching the visibility between Collapsed and Visible this behaviour continues, as I want it to.
Now if I hit the BtnHide to move the UserControl out of viewport everything works fine until I start switching the Visibility of the UserControl again. Now the Storyboard doesn't work anymore. I can still move the UserControls in/out with the Buttons but the 'Visible' Trigger does not start the Storyboard.
Here are gif examples of the behavior:
With the click on 'Database Search' I set the Visibility of the UserControl to Visible (because its bound to the 'IsExpanded' property of the ExpanderControl) and it works just fine:
Here I demonstrate what happens after I manually click the 'BtnHide':
This is the code for the UserControl:
<UserControl.Resources>
<Style x:Key="TextBlockStyle">
<Setter Property="TextBlock.FontSize" Value="10"></Setter>
<Setter Property="TextBlock.Margin" Value="1"></Setter>
<Setter Property="TextBlock.VerticalAlignment" Value="Center"></Setter>
</Style>
<Storyboard x:Key="sbShowLeftMenu">
<ObjectAnimationUsingKeyFrames BeginTime="0:0:0" Storyboard.TargetName="BtnShow" Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="0:0:0.01" Value="{x:Static Visibility.Collapsed}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames BeginTime="0:0:0" Storyboard.TargetName="BtnHide" Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="0:0:0.01" Value="{x:Static Visibility.Visible}"/>
</ObjectAnimationUsingKeyFrames>
<ThicknessAnimation Storyboard.TargetProperty="Margin" Storyboard.TargetName="pnlLeftMenu" From="-650,0,0,0" To="0,0,0,0" DecelerationRatio=".9" Duration="0:0:1" />
</Storyboard>
<Storyboard x:Key="sbHideLeftMenu">
<ObjectAnimationUsingKeyFrames BeginTime="0:0:0" Storyboard.TargetName="BtnHide" Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="0:0:0.01" Value="{x:Static Visibility.Collapsed}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames BeginTime="0:0:0" Storyboard.TargetName="BtnShow" Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="0:0:0.01" Value="{x:Static Visibility.Visible}"/>
</ObjectAnimationUsingKeyFrames>
<ThicknessAnimation Storyboard.TargetProperty="Margin" Storyboard.TargetName="pnlLeftMenu" From="0,0,0,0" To="-650,0,0,0" AccelerationRatio=".9" Duration="0:0:1" />
</Storyboard>
</UserControl.Resources>
<UserControl.Template>
<ControlTemplate>
<Grid Background="Red">
<StackPanel Panel.ZIndex="2" Name="pnlLeftMenu" Orientation="Horizontal" HorizontalAlignment="Left" Margin="-650,0,0,0" Height="500">
<!-- Content -->
<Border>The Content is in here</Border>
<Grid>
<Button x:Name="BtnShow" Height="25" Width="25" VerticalAlignment="Top" HorizontalAlignment="Left" >
<Button.Content>
<Path Stroke="Black"
StrokeThickness="2"
Data="M 0,0 L 0.5,0.5 L 0,1"
Stretch="Uniform"></Path>
</Button.Content>
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard Storyboard="{StaticResource sbShowLeftMenu}"></BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
<Button x:Name="BtnHide" Height="25" Width="25" VerticalAlignment="Top" HorizontalAlignment="Left" Visibility="Collapsed" >
<Button.Content>
<Path Stroke="Black"
StrokeThickness="2"
Data="M 1,1 L 0.5,0.5 L 1,0"
Stretch="Uniform"></Path>
</Button.Content>
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard Storyboard="{StaticResource sbHideLeftMenu}"></BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
</Grid>
</StackPanel>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsVisible"
Value="True"
my:TriggerTracing.TriggerName="BoldWhenMouseIsOver"
my:TriggerTracing.TraceEnabled="True">
<Trigger.EnterActions>
<BeginStoryboard Name="sbShowLeftMenu" Storyboard="{StaticResource sbShowLeftMenu}"/>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard Name="xy" Storyboard="{StaticResource sbHideLeftMenu}"/>
</Trigger.ExitActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</UserControl.Template>
You seem to have some complex logic which is interfering with the operations of the story boards.
I would recommend that you not try do this in triggers but instead in code behind. Create a state machine operation and then open/close/make visible/make invisible depending on the state.
The following code is what I use for similar logic of moving a panel based on boolean state via storyboards. You can expand it to handle visibility as well with other states.
private bool moveRight = true; // Start out on the left side, then move right.
public void MoveRight()
{
try
{
if (moveRight)
{
(Resources["MoveToOpen"] as Storyboard)?.Begin(this, false);
(Resources["FlipArrowClose"] as Storyboard)?.Begin(this, false);
}
else
{
(Resources["MoveToClose"] as Storyboard)?.Begin(this, false);
(Resources["FlipArrowOpen"] as Storyboard)?.Begin(this, false);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
moveRight = !moveRight;
}

how to start the animation if the storyboard definition and the corresponding grid inside canvas is in windows.resources tag

i want to start animation in the grid.the grid is inside the canvas tag.both the story board definition and the grid to animate is inside the windows.resources tag.
the code is given below
<VisualBrush x:Key="Passport" x:Shared="false" Stretch="Uniform">
<VisualBrush.Visual>
<Canvas RenderTransformOrigin="0.5,0.5" >
<Grid Margin="5.397,45.106,5.239,0" VerticalAlignment="Top" Height="7.801" x:Name="side_scan_strip" RenderTransformOrigin="0.5,0.5">
<Grid.RenderTransform >
<TransformGroup>
<ScaleTransform ScaleX="1" ScaleY="1"/>
<SkewTransform AngleX="0" AngleY="0"/>
<RotateTransform Angle="0"/>
<TranslateTransform X="0" Y="0"/>
</TransformGroup>
</Grid.RenderTransform>
</Grid>
</Canvas>
</VisualBrush.Visual>
</VisualBrush>
<Storyboard x:Key="Storyboard1" x:Name="Storyboard1">
<DoubleAnimationUsingKeyFrames x:Name="dak1" BeginTime="00:00:00" Storyboard.TargetName=" side_scan_strip" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)">
<SplineDoubleKeyFrame KeyTime="00:00:01" Value="109.75"/>
<SplineDoubleKeyFrame KeyTime="00:00:02" Value="0"/>
<SplineDoubleKeyFrame KeyTime="00:00:03" Value="109.75"/>
<SplineDoubleKeyFrame KeyTime="00:00:04" Value="0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
the exception i got is :
' side_scan_strip' name cannot be found in the name scope of 'WpfApplication1.MainWindow'.
It might be because of a little error :) In the Storyboard you have written: " side_scan_strip" while it should be "side_scan_strip". See the little space in the beginning of the first? I think that is what is triggering the exception.

Troubles with GoToStateAction

I have had several problems with the GoToStateAction in different scenarios, and I'm beginning to believe that either the feature is buggy, or that my understanding of it is off.
In this case, I have a datatemplate with an ellipse that is representing a connector. The connector has an IsConnected property... I am using VisualStates and the GoToStateAction with a DataTrigger to switch between the 2 states 'Connected' and 'NotConnected'. However, in this case the state is never set.
I know the model is set up correctly, as trying other binding scenarios with IsConnected works fine. What am I doing wrong?
<DataTemplate x:Key="ConnectorTemplate">
<Grid x:Name="grid">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="ConnectionStates">
<VisualState x:Name="Connected">
<Storyboard>
<ColorAnimation Duration="0" To="#FFEAFFDD" Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[1].(GradientStop.Color)" Storyboard.TargetName="ellipse" d:IsOptimized="True"/>
<ColorAnimation Duration="0" To="#FF56992B" Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[0].(GradientStop.Color)" Storyboard.TargetName="ellipse" d:IsOptimized="True"/>
</Storyboard>
</VisualState>
<VisualState x:Name="NotConnected"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<VisualStateManager.CustomVisualStateManager>
<ei:ExtendedVisualStateManager/>
</VisualStateManager.CustomVisualStateManager>
<Ellipse x:Name="ellipse"
Height="8"
Width="8">
<i:Interaction.Triggers>
<ei:DataTrigger Binding="{Binding IsConnected}" Value="true">
<ei:GoToStateAction StateName="Connected"/>
</ei:DataTrigger>
<ei:DataTrigger Binding="{Binding IsConnected}" Value="false">
<ei:GoToStateAction StateName="NotConnected"/>
</ei:DataTrigger>
</i:Interaction.Triggers>
<Ellipse.Fill>
<RadialGradientBrush Center="0.275,0.262"
GradientOrigin="0.275,0.262"
RadiusX="0.566"
RadiusY="0.566">
<GradientStop Color="#FF333333"
Offset="1" />
<GradientStop Color="#FFC4C4C4" />
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
</Grid>
</DataTemplate>
I think you should set TargetName in GoToStateAction, because by default,if my memory serves me right, Target is associated with GoToStateAction object, in your case - ellipse
GoToStateAction is not triggered when the item is loaded, it only comes in play when the related property is changed (PropertyChanged event is fired).

Scale transform in xaml (in a controltemplate) on a button to perform a "zoom"

I've got a button with an image in it and it's being styled by the following:
<ControlTemplate x:Key="IconButton" TargetType="Button">
<Border>
<ContentPresenter Height="80" Width="80" />
</Border>
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard TargetProperty="Opacity">
<DoubleAnimation From="1" To="0.5" Duration="0:0:0.5" />
<DoubleAnimation From="0.5" To="1" Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseEnter">
<BeginStoryboard>
<Storyboard TargetProperty="Width">
<DoubleAnimation From="80" To="95" Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Cursor" Value="Hand"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
Button is as follows:
<Button Template="{StaticResource IconButton}" Name="btnExit">
<Image Source="Images/Exit.png" />
</Button>
The problem is that the width doesn't change when my mouse goes over. (Or at least - the width of the image does not...)
I believe there is a "scale" transform I can use to enlarge the button and all it's contents? how would I do that here...?
Thanks.
Your template seems to be pretty minimal but I'm assuming your only just getting started on it, however this will help you get started with using a ScaleTransform as opposed to animating the width.
The ScaleTransform can be applied to the RenderTransform property of either the Button itself or just the Border of your template. This could be a TransformGroup if you want to do more than just Scale (i.e. a composite transform consisting of other tranforms such as Translate, Rotate, Skew) but to keep it simple and for examples sake something like the following applies a single ScaleTransform value to the Button:
<Button Template="{StaticResource IconButton}" Name="btnExit">
<Button.RenderTransform>
<ScaleTransform ScaleX="1.0" ScaleY="1.0"></ScaleTransform>
</Button.RenderTransform>
<Image Source="Images/Exit.png" />
</Button>
or this to apply to the Border of the ControlTemplate:
<ControlTemplate x:Key="IconButton" TargetType="Button">
<Border Background="Blue" x:Name="render">
<Border.RenderTransform>
<ScaleTransform ScaleX="1.0" ScaleY="1.0"></ScaleTransform>
</Border.RenderTransform>
<ContentPresenter Height="80" Width="80" />
</Border>
...
...
Next you will want to change your MouseEnter trigger to target that property and for width you will want to target the ScaleX property of the ScaleTransform. The following Storyboard will scale the Button 2.5 times in the X direction (add TargetName="render" to <Storyboard... if you have chosen to apply the Transform to the Border as opposed to the Button).
<EventTrigger RoutedEvent="Mouse.MouseEnter">
<BeginStoryboard>
<Storyboard TargetProperty="RenderTransform.ScaleX">
<DoubleAnimation To="2.5" Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
If you were to use a TransformGroup with a number of transforms you would change the TargetProperty value to something like RenderTransform.(TransformGroup.Children)[0].ScaleX assuming the ScaleTransform is the first child of the group.
This should get you up and running with what you need and you can take it where you want from there...
HTH

scale animation for wpf popup

I have a nice little popup, when it shows, I d'like it to growth from 0 to 1x scaley,
but I don't get it right, when I click multiple times, it looks like i "catch" the animation at
various states during the "growth".
<Window.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.MouseRightButtonDown"
>
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="SimplePopup"
Storyboard.TargetProperty="(FrameworkElement.LayoutTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)">
<SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="1"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Window.Triggers>
and the popup:
<Popup Name="SimplePopup"
AllowsTransparency="True"
StaysOpen="False">
<Popup.LayoutTransform>
<TransformGroup>
<ScaleTransform ScaleX="1" ScaleY="1" />
<SkewTransform AngleX="0" AngleY="0" />
<RotateTransform Angle="0" />
<TranslateTransform X="0" Y="0" />
</TransformGroup>
</Popup.LayoutTransform>
<Border> some Content here
</Border>
</Popup>
I'm not sure what you mean by
"catch" the animation at various states during the "growth".
Maybe that it won't finish the animation but restart when you click before storyboard is completed?
If so, you need to prevent the animation from restarting by firing action only when it's not running. I think it can be done with more complex behaviors or triggers available in Blend.
by "catch" the animation at various states during the "growth".
I mean that there is no animation at all, all I see is the static popup
scaled at different sizes, but not a visible animation.

Resources