wpf animation to hide and collapse panel on button click - wpf

I have a StackPanel and a button. Initallty the button content should be << When the button is clicked the StackPanel content should collapse and the button text should become >> where collapse is happening correctly. Again when the same button is pressed I want the content to show up and text should be << which I am unsure how to do.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" x:Name="stkEasyLocatePanel" Loaded="stkEasyLocatePanel_Loaded" HorizontalAlignment="Stretch" VerticalAlignment="Top" Margin="0,0,0,28"></StackPanel>
<Button Grid.Column="1" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,5,0,0" Panel.ZIndex="140" Height="20" Width="25" Background="Transparent" Content="<<">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard x:Name="HideStackPanel">
<DoubleAnimation Storyboard.TargetName="stkEasyLocatePanel" Storyboard.TargetProperty="Width" From="330" To="0" Duration="0:0:0.3">
<DoubleAnimation.EasingFunction>
<PowerEase EasingMode="EaseIn"></PowerEase>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
<BeginStoryboard>
<!--This part dosent work. The content collapses and shows on a single click. But I want it to happen on two clicks of same button-->
<Storyboard x:Name="ShowStackPanel">
<DoubleAnimation Storyboard.TargetName="stkEasyLocatePanel" Storyboard.TargetProperty="Width" From="0" To="330" Duration="0:0:0.3">
<DoubleAnimation.EasingFunction>
<PowerEase EasingMode="EaseIn"></PowerEase>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
</Grid>

Replace Button with ToggleButton , and use code below as it is, and see if this solves your problem.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" Width="330" x:Name="stkEasyLocatePanel" HorizontalAlignment="Stretch" VerticalAlignment="Top" Margin="0,0,0,28">
<Image Source="C:\Users\Public\Pictures\Sample Pictures\Chrysanthemum.jpg"/>
</StackPanel>
<ToggleButton Grid.Column="1" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,5,0,0" Panel.ZIndex="140" Height="20" Width="25" Background="Transparent" Content="<<">
<ToggleButton.Triggers>
<EventTrigger RoutedEvent="ToggleButton.Unchecked">
<BeginStoryboard>
<Storyboard x:Name="HideStackPanel">
<DoubleAnimation Storyboard.TargetName="stkEasyLocatePanel" Storyboard.TargetProperty="Width" From="0" To="330" Duration="0:0:0.3">
<DoubleAnimation.EasingFunction>
<PowerEase EasingMode="EaseIn"></PowerEase>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Content">
<DiscreteObjectKeyFrame KeyTime="0:0:0" Value="<<" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="ToggleButton.Checked">
<BeginStoryboard>
<!--This part dosent work. The content collapses and shows on a single click. But I want it to happen on two clicks of same button-->
<Storyboard x:Name="ShowStackPanel">
<DoubleAnimation Storyboard.TargetName="stkEasyLocatePanel" Storyboard.TargetProperty="Width" From="330" To="0" Duration="0:0:0.3">
<DoubleAnimation.EasingFunction>
<PowerEase EasingMode="EaseIn"></PowerEase>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Content">
<DiscreteObjectKeyFrame KeyTime="0:0:0" Value=">>" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ToggleButton.Triggers>
</ToggleButton>
</Grid>

I would suggest to keep a variable in code behind (something like isCollaped) and then in code behind hookup to the click event of the button. Inside the click method of the button place some logic to hide or show the stack panel.
For example:
XAML
<StackPanel Grid.Column="0" x:Name="stkEasyLocatePanel" Loaded="stkEasyLocatePanel_Loaded" HorizontalAlignment="Stretch" VerticalAlignment="Top" Margin="0,0,0,28"></StackPanel>
//Add the Click event.
<Button Grid.Column="1" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,5,0,0" Panel.ZIndex="140" Height="20" Width="25" Background="Transparent" Content="<<" Click=Button_Click>
CODE BEHIND
bool isCollapsed = false;
private void Button_Click(object sender, RoutedEventArgs e)
{
if (isCollapsed)
{
//Create and run show Stackpanel animation
//Change the content of the button to "<<"
}
else
{
//Create and run hide Stackpanel animation
//Change the content of the button to ">>"
}
isCollapsed = !isCollapsed;
}
EDIT
As requested in the comment, this is an example on how you would animate from code behind.
if (isCollapsed)
{
DoubleAnimation showAnim = new DoubleAnimation();
showAnim.Duration = TimeSpan.FromSeconds(0.3);
showAnim.EasingFunction = new PowerEase() { EasingMode = EasingMode.EaseIn };
showAnim.From = 330;
showAnim.To = 0;
stkEasyLocatePanel.BeginAnimation(StackPanel.WidthProperty, showAnim);
//Change the content of the button to "<<"
}

Related

Expander getting stuck after IsExpanded set to true by storyboard

So I have an expander that I want to have the normal functionality (open and close with its own button) but I also want a different button to expand it when pressed (this button is in the header of the expander). I'm using a storyboard in an event trigger for the Button.Click which works, but after it is expanded this way the normal button doesn't work, it just stays expanded. My xaml is below, I would really prefer to keep this all in the xaml, I could come up with a way to do it in the codebehind/viewmodel myself.
<Expander x:Name="IndexExpander" IsExpanded="True" Grid.Row="4" Grid.ColumnSpan="5" Margin="10" MaxHeight="150">
<Expander.Triggers>
<EventTrigger SourceName="btnAddIndex" RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="IndexExpander" Storyboard.TargetProperty="IsExpanded" BeginTime="0:0:0.25" Duration="0:0:0.20" >
<DiscreteBooleanKeyFrame KeyTime="0" Value="True" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Expander.Triggers>
<Expander.Header>
<StackPanel DockPanel.Dock="Left" Orientation="Horizontal">
<TextBlock Text="Indexes" FontWeight="Bold"/>
<!-- Add/Delete Buttons-->
<Grid Margin="10,0,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50*"/>
<ColumnDefinition Width="50*"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" x:Name="btnAddIndex" Command="{Binding AddIndexCommand}" Template="{StaticResource AddButtonTemplate}" IsEnabled="{Binding IsEditable}" Margin="0,0,5,0" />
</Grid>
</StackPanel>
</Expander.Header>
Alright, so for anyone following in my footsteps here's what I did. I got the idea from here, and adapted it until it worked correctly.
<Expander.Triggers>
<EventTrigger SourceName="btnAddCol" RoutedEvent="Button.Click">
<BeginStoryboard x:Name="ColumnExpanderStory">
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="ColumnExpander" Storyboard.TargetProperty="IsExpanded">
<DiscreteBooleanKeyFrame KeyTime="0" Value="True" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="ToggleButton.PreviewMouseUp">
<RemoveStoryboard BeginStoryboardName="ColumnExpanderStory" />
</EventTrigger>
</Expander.Triggers>
<Expander.Header>
The problem was that the storyboard overrides any other bindings to the IsExpanded property, so it has to be removed to restore them (read more here). The suggestion was to use the ToggleButton.Checked event to remove the storyboard, but that didn't work for me, only the "Preview" events seemed to have the right timing. I started with PreviewMouseDown, but it would remove the storyboard, then on mouse up toggle the expander, meaning the first click would just flip states back and forth quickly. Using PreviewMouseUp got around that issue.

XAML animation of height of control with dynamic content

I have a panel that should be minimized unless the user hovers the mouse over the panel. It is implemented using a storyboard that lets the height of the panel grow when the use puts the mouse over the control. At the moment the target height is hard coded to 400 which is a bad solution as the content of the panel will be different each time the application starts (it is static during execution).
How do you create an animation that lets the panel grow to the size of the current content?
<Window x:Class="MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="500" Width="525">
<Grid>
<Border Margin="10,0" Background="LightGray" HorizontalAlignment="Left" VerticalAlignment="Top" CornerRadius="0,0,8,8">
<Border.Effect>
<DropShadowEffect Opacity="0.5"/>
</Border.Effect>
<Border.Triggers>
<EventTrigger RoutedEvent="Border.MouseEnter">
<BeginStoryboard>
<BeginStoryboard.Storyboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Height"
From="25"
To="400"
Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard.Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="Border.MouseLeave">
<BeginStoryboard>
<BeginStoryboard.Storyboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Height"
From="400"
To="25"
Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard.Storyboard>
</BeginStoryboard>
</EventTrigger>
</Border.Triggers>
<StackPanel Margin="5">
<TextBlock Height="25" Text="My items panel" />
<ListBox MinWidth="150" MinHeight="100" ItemsSource="{Binding MyItems}" />
</StackPanel>
</Border>
</Grid>
Edit: I have tried with binding to the Height of the StackPanel but that didn't really help as it didn't take the margins of the stackpanel into account thus making the panel shorter than needed.
<DoubleAnimation Storyboard.TargetProperty="Height"
From="{Binding ElementName=NameOfStackPanel, Path=ActualHeight}"
To="25"
Duration="0:0:0.2" />
You could create a converter to handle adding the margins to the ActualHeight of your StackPanel. You could even use a multivalue convertor so you could bind the margin too and not have to hardcode a fudge factor. Finally, you could probably wrap your stackpanel in another panel (without margins) and bind to the height of that instead.

WPF event trigger not working

I'm new in programming (changing area, leaving Design) and pretty interested in WPF.
So I'm studying and doing some codes.
In this one, I'm trying to make an "invisible" grid (the gridNovoCliente, opacity = 0) to show up at a button click event (opacity = 1), with all its elements.
I want the grid/elements invisible until the user clicks the button.
But it's not working.
I've been Googling it for 3 days before decided to post here.
I get the error 'Set property 'System.Windows.Media.Animation.Storyboard.Target' threw an exception.' at the EventTrigger line.
There will be more buttons once I get this one working, so I created a style.
Thanks in advance!
Here's the code.
<Grid>
<Button Style="{StaticResource templateButton}" Name="novoCliente" Content="Novo
Cliente" Margin="20,41,0,598" TextBlock.TextAlignment="Center">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.Target="gridNovoCliente"
Storyboard.TargetProperty="Opacity"
From="0"
To="1"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
<Grid Height="705" HorizontalAlignment="Left" Margin="131,12,0,0" Name="gridNovoCliente" VerticalAlignment="Top" Width="859" Background="#FF6FAA6F" Opacity="0">
<TextBox Height="70" HorizontalAlignment="Left" Margin="270,250,0,0" Name="textBox1" VerticalAlignment="Top" Width="358" />
</Grid>
</Grid>
<Canvas>
<Button Name="novoCliente" Content="Novo
Cliente" Margin="20,41,0,598" TextBlock.TextAlignment="Center">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click" >
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="gridNovoCliente"
Storyboard.TargetProperty="Opacity"
From="0"
To="1"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Button.Triggers>
</Button>
<Grid Height="705" HorizontalAlignment="Left" Margin="131,12,0,0" Name="gridNovoCliente" VerticalAlignment="Top" Width="859" Background="#FF6FAA6F" Opacity="0">
<TextBox Height="70" HorizontalAlignment="Left" Margin="270,250,0,0" Name="textBox1" VerticalAlignment="Top" Width="358" />
</Grid>
</Canvas>
It's very simple, please try above xaml, it's works for me. Thanks

XamlParseException occured - animating an ellipse

I have the following code to animate the width of the ellipse forever until the window is closed. However, it throws XamlParseExceptionError. Could someone point out to me where I made the mistake?
`
</TextBlock>
<TextBlock x:Uid="ProductName" Localization.Attributes="$Content(Readable Unmodifiable)">
<TextBlock.ToolTip>
<TextBlock x:Uid="ProductNameTip" Localization.Attributes="$Content(ToolTip Readable Modifiable)">
A photo editor that will make everyone look beautiful
</TextBlock>
</TextBlock.ToolTip>
Amazing Photo Editor
</TextBlock>
<Ellipse Name="Circle"
Width="100"
Height="100"
Fill="Red">
<Ellipse.Triggers>
<EventTrigger RoutedEvent="Ellipse.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.Target="Circle"
Storyboard.TargetProperty="Width"
From="1"
To="100"
Duration="0:0:5"
AutoReverse="True"
RepeatBehavior="Forever">
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Ellipse.Triggers>
</Ellipse>
</StackPanel>
</Grid>
`
Use Storyboard.TargetName Attached Property instead of Storyboard.Target.
<DoubleAnimation Storyboard.TargetName="Circle"

Creating button content with centered text and animated border

The following XAML code creates a button with a border as its content. The border has a TextBlock as it's child. When you CLICK on the button, the border width gradually grows.
Since the text block is inside the border it is not seen until you click the button.
Now my requirements are:
There should be a text displayed on the button even before you click it.
Even after you click the button, only the border should animate while the button text remains in its center position.
<Button Name="button5" Width="100" Margin="10" HorizontalContentAlignment="Left">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="myBorder" Storyboard.TargetProperty="Width" From="0" To="94" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Button.Triggers>
<Button.Content>
<Border Background="Gray" Width="0" Name="myBorder">
<TextBlock>Search</TextBlock>
</Border>
</Button.Content>
</Button>
I will appreciate if anyone is able to provide answers with working XAML code.
I've modified your XAML only slightly:
<Button Name="button5" Width="100" Margin="10" HorizontalContentAlignment="Left">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="myRectangle" Storyboard.TargetProperty="Width" From="0" To="94" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Button.Triggers>
<Button.Content>
<Grid>
<Rectangle x:Name="myRectangle" Fill="Gray" Width="0" HorizontalAlignment="Left" />
<TextBlock Text="Search" />
</Grid>
</Button.Content>
</Button>
For the Button's content, you could use a Grid which contains your Border and the TextBlock. Since no columns or rows are specified, the TextBlock and the Border are drawn on top of each other. It situations like this where the Border does not have content, I prefer to use a Rectangle instead since it is bit lighter. Hope this helps!

Resources