Slide a panel from the side using Storyboard - wpf

I have 3 grids. canvas, main and sidebar
<Grid x:Name="canvas">
<Grid x:Name="main"/>
<Grid x:Name="sidebar"/>
</Grid>
I'm trying to create an animation in my WPF MVVM application in which when I click a certain button, the main panel should move to the left (outside of the canvas) and sidebar should take its place with the same animation. Basically, it should look like the sidebar is pushing the main outside of the canvas. And when I click another button the reverse of the exact animation should run. (like the main is pushing sidebar outside of the canvas.
I tried to create a Style and add some Triggers on Visibility property of the Grids. I was somewhat successful in half of the animation but couldn't get it to reverse and animation both panels at the same time.
<Style TargetType="Grid" x:Key="VisibleAnimation">
<Setter Property="Visibility" Value="Collapsed"/>
<Setter Property="RenderTransform">
<Setter.Value>
<TranslateTransform/>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="RenderTransform.(TranslateTransform.X)"
From="300" To="0" Duration="0:0:0.5"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
Please note that I'm looking for an MVVM solution so no code behind should be involved if possible.

Related

Blink animation style for period of time

So i have StackPanel that i want to show with blink Style for several seconds and after that i want it to disappear.
I do not want it to be Automatically but control it from code behind:
So currently this is what i have so far:
<Style x:Key="FaderStyle" TargetType="{x:Type StackPanel}">
<Style.Resources>
<Storyboard x:Key="FadeStoryboard">
<DoubleAnimation Storyboard.TargetProperty="(StackPanel.Opacity)"
From="0"
To="1" Duration="0:0:0.7"
RepeatBehavior="0:0:5"
AutoReverse="True"/>
</Storyboard>
</Style.Resources>
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Trigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource FadeStoryboard}"/>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
Code behind:
StackPanel sp;
Storyboard storyboard = Resources["FaderStyle"] as Storyboard;
if (storyboard != null)
storyboard.Begin(sp);
So currently my StackPanel Visibility is Collapsed and after i start the Animation i still cannot see it.
Your code is fine. But your approach to starting the animation is wrong. The trigger starts the animation when Visibility changes to Visible. Not the other way around (which your last piece of code indicates) starting the animation does not change visibility because you didn't write the logic to do that
So with your given code, you need to change the visibility in order to start the animation:
StackPanel sp;
sp.Visibility = Visibility.Visible;
Note that the animation only starts when it enters Visible state. Which means you need to make it collapsed or hidden first.

Time Delay on Trigger

I wish to attach a time delay to a mouseover event on a WPF expander I have on my form (xaml supported by VB.NET code behind). This mouseover event essentially triggers the expansion as oppose to clicking - but I'd like a short wait before the content is expanded. So far I have not managed to find anything to solve this via the wider internet.
The current xaml code to enable the trigger is:
<Style x:Key="HoverExpander" TargetType="{x:Type Expander}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="IsExpanded" Value="True" />
</Trigger>
</Style.Triggers>
</Style>
This style is then applied to:
<Expander Style="{StaticResource HoverExpander}"
HorizontalAlignment="Right"
ExpandDirection="Left"
Height="Auto"
Width="Auto">
<!-- Content here -->
</Expander>
Note that I've stripped out other aesthetics (such as borders, gridrefs etc for readability).
I think there should be some way to set a delay on the MouseOver Trigger but haven't had much luck finding it. This could either be set in xaml or perhaps as an event in the code behind.
I'm working on this currently, so when I find a solution I shall post it here. Grateful for any ideas meantime. Thanks!
Use an EventTrigger on the MouseOver event and a Storyboard with a BooleanAnimationUsingKeyFrames instead. In the Timeline of the Storyboard, you could have KeyFrames, so that the animation waits for some time before it affects the properties you want to change.
This was the code I settled on - based on the ideas already given:
<Style x:Key="HoverExpander" TargetType="{x:Type Expander}">
<Style.Setters>
<Setter Property="IsExpanded" Value="False"/><!-- Initially collapsed -->
</Style.Setters>
<Style.Triggers>
<!-- Impose a short delay (500ms) before expanding control -->
<EventTrigger RoutedEvent="Expander.MouseEnter">
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames
Storyboard.TargetProperty="IsExpanded"
Duration="0:0:0.5">
<DiscreteBooleanKeyFrame Value="True" KeyTime="100%"/><!-- I.E. after 500ms -->
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<!-- Collapse when mouse leaves control-->
<EventTrigger RoutedEvent="Expander.MouseLeave">
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames
Storyboard.TargetProperty="IsExpanded"
Duration="0:0:0.1">
<DiscreteBooleanKeyFrame Value="False" KeyTime="0%"/><!-- I.E. Immediately -->
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
Then apply as before. This was tested and works in .NET 4.0. Other neat tricks could be applied if you do so wish, I found the following to be quite helpful in getting ideas:
Animation Overview (MSDN)
Storyboards Overview (MSDN)

Change visibility in EventTrigger

i have style that have rectangle which visibility=hidden.
i want change visibility when mouse enter rectangle.
forasmuch as rectangle doesn't have 'IsMouseOver' property i cant use trigger.
how i can do that? (how can change property with animation)
thanks.
I've looking for an button to write a comment, but i dont found it.
So here comes an answer.
Two things:
How should it possible to set Visisbility of an Element to Visible, if it is hidden? The MouseEnter and MouseLeave events will not be called. So the IsMouseOver Property is always False.
Second thing is, that i'm wondering that the IsMouseOver Property will not work in a trigger (i've tried it, too and....got an exception).
An alternative way is to use EventTriggers on MouseEnter and MouseLeave.
kr
sb
<Rectangle Width="400" Height="400" Fill="Red" Opacity="0">
<Rectangle.Style>
<Style TargetType="Rectangle">
<Style.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard TargetProperty="Opacity">
<DoubleAnimation From="0" To="1" Duration="0:0:2" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard TargetProperty="Opacity">
<DoubleAnimation From="1" To="0" Duration="0:0:2" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
Ok, to sum up and add to what others wrote:
The rectangle does have an IsMouseOver property. So it is possible to create a trigger (inside a style) that will work with this property. However, this will not work. Why? Because as far as WPF is concerned, if the element is not visible, the mouse is never over it. In other words, is the element is hidden, IsMouseOver will always be false. Therefore, you can't use it to make the element visible when the user puts the mouse over the place where it should be.
If you are working, with a Rectangle, there is another way: instead of making it not visible, you can change the Rectangle's color to be transparent. That way, it IsMouseOver will work as it should and the following code (as an example) will do what you want:
<Rectangle Width="200" Height="200">
<Rectangle.Style>
<Style TargetType="Rectangle">
<Setter Property="Fill" Value="Transparent"></Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Fill" Value="Yellow"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
Of course, the usage depends on what exactly you want to do, which your question doesn't mention. Another way might be to create another Rectangle with the same dimensions and position as the one you need to hide/show. This new Rectangle would be transparent, but always visible. Then, you can bind your Rectangle's Visibility to this new Rectangle's IsMouseOver.
Visibility has three enumeration, Visible Hidden and Collapsed, therefore you cant directly bind to a bool property or for that matter any property that is not a Visibility property. You can write or find a converter, search on WPF Visibility Converter. Or you can try this:
Use the tag property and bind it to the visibility property, it works fine, it is simple and it is entirely in your style setters and triggers. Of course if your using your tag for something else oh well..
In this case I have two TextBlocks, I want one textblock visible when the mouse enters the other, So when the mouse is over the first, I change its tag property to Visible and bind the second text box Visibility property to the firsts tag property.
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal ">
<TextBlock Name="TextBlockTitle" Text="{Binding Title}">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock }">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Tag" Value="Visible"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="False">
<Setter Property="Tag" Value="Hidden"/>
</Trigger>
</Style.Triggers>
</Style>
</TextBlock.Style></TextBlock>
<TextBlock Name="TextBlockAdd" Text=" + Add New" MouseLeftButtonDown="TextBlockAdd_OnMouseLeftButtonDown">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock }">
<Setter Property="Visibility" Value="{Binding ElementName=TextBlockTitle,Path=Tag}"></Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</Trigger>
<EventTrigger RoutedEvent="MouseLeftButtonDown" ></EventTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>

Datagrid MouseOver and Selected States when using arrow keys

I have a wpf datagrid. I have added styling to show a mouseover color on a row.
What I am trying to achieve is when the mouseover appears, and a user starts using the arrow keys to navigate up and down, the mouseover needs to disappear and only the row that the user used arrow keys to get to, is the highlighted one.
The issue is the mouse cursor has been left on the grid while the user navigates with the arrow keys and the row under the cursor holds the highlight as well as the row the went to using arrows.
Here is my sample xmal:
<DataGrid AutoGenerateColumns="True" Height="277" HorizontalAlignment="Left" Margin="0,311,0,0" Name="dataGrid1"
VerticalAlignment="Top"
Width="478" ItemsSource="{Binding Path=Persons}"
RowHeight="20"
RowHeaderWidth="35" Grid.ColumnSpan="2" >
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Style.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter Property="Background"
Value="Green" />
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
</DataGrid>
Thanks
You'll need to set some kind of flag when the user hits an Arrow key so that the background only changes if IsMouseOver and IsUsingArrowKeys is false. You might even be able to use the Mouse Visibility as a condition instead of using a flag
I'm not positive the exact syntax, but it should be something like this
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<!-- May need to reference RelativeSource here, not sure -->
<Condition Property="IsMouseOver" Value="False" />
<Condition Binding="{Binding IsUsingArrowKeys}" Value="False" />
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="Green" />
</MultiDataTrigger>
</Style.Triggers>
I would suggest highlighting the row based on thier focus triggers.
Something like this:
<EventTrigger RoutedEvent="GotFocus">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="dataGrid1" Storyboard.TargetProperty="Background" Duration="0:0:0.1" To="Green"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="LostFocus">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="dataGrid1" Storyboard.TargetProperty="Background" Duration="0:0:0.1" To="White"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
And giving focus to them manually, something like this:
private void Btn_Click(object sender, RoutedEventArgs e)
{
dataGrid1.Focus();
}
So when another row gets focus, the current row loses focus & automatically falls back to non highlighted color background.

How can I get a trigger to fire when the contents of a StackPanel changes?

The following code makes the contents of a StackPanel fade in when it is loaded (StackPanel.Loaded).
What do I have to change to get the fade it to start when the contents of the StackPanel change, e.g. every time a message in the StackPanel changes I want it to fade in again?
<Style x:Key="MessageStyle" TargetType="StackPanel">
<Style.Triggers>
<DataTrigger Binding="{Binding SaveStatus}" Value="Failed">
<Setter Property="StackPanel.Background" Value="Red"/>
</DataTrigger>
<EventTrigger RoutedEvent="StackPanel.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="(StackPanel.Opacity)"
From="0.0" To="1.0" Duration="0:0:5"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
You should be able to do what you're looking for with the StackPanel.SourceUpdated/StackPanel.TargetUpdated event after setting the accompanying
NotifyOnSourceUpdated/NotifyOnTargetUpdated property to True on the binding that binds the contents of the StackPanel.

Resources