I am wondering if any simple technique to run storyboard if textblock text string was changed. Thank you in advance!
Below is xaml for a user control that will animate the opacity of an item when the Text property of a TextBlock is changed.
It is using a PropertyChangedTrigger and ControlStoryboard action to cause this to happen. These items come from dlls that get installed with Blend, but you can install them separately if you don't have Blend: Blend 4 SDK
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
x:Class="TextboxAnimation.MainPage"
Width="640" Height="480">
<UserControl.Resources>
<Storyboard x:Name="AnAnimation">
<DoubleAnimationUsingKeyFrames
Storyboard.TargetProperty="(UIElement.Opacity)"
Storyboard.TargetName="animationTextBlock">
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="1"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</UserControl.Resources>
<StackPanel x:Name="LayoutRoot" Background="White">
<TextBlock x:Name="textBlock"
HorizontalAlignment="Left" Text="Click Me To Change Text"
MouseLeftButtonDown="TextBlockClicked">
<i:Interaction.Triggers>
<ei:PropertyChangedTrigger Binding="{Binding Text, ElementName=textBlock}">
<ei:ControlStoryboardAction Storyboard="{StaticResource AnAnimation}"/>
</ei:PropertyChangedTrigger>
</i:Interaction.Triggers>
</TextBlock>
<TextBlock
x:Name="animationTextBlock"
Text="Animate Me!" Margin="0,8,0,0" Opacity="0"/>
</StackPanel>
</UserControl>
Here is the code behind that is used for the click event, which changes the TextBlock Text property:
int times = 0;
private void TextBlockClicked(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
times++;
textBlock.Text = String.Format("I've been clicked and changed {0} times!", times);
}
Related
In one of the windows of my WPF application, the button width cannot be changed to smaller size whatever I do. I tried change the property of "width" in xaml, drag the button in designer, or pragmatically change it using c#. Even if I created a new button in that window, the width could only be changed to larger size but could not be changed to smaller size. Though there was no error or warning, none of the approaches successfully change the width. The weird thing was, I could change the height of the button smaller or larger easily by dragging, and in my other window, I can change the buttons width and height smaller or larger easily. I am using the same style for all the buttons in all the windows. If I drag the button, there will be no response of it unless I unlock the one of the litlle "knot" around it, but it will look like this:
The only thing is that I use a notification template for this window and there are some animation effects. But I didn't see any major difference between it and others. Here is the XAML code:
<Window x:Class="Timebreak.NotiWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Timebreak"
mc:Ignorable="d"
Title="TimeBreak" Height="450" Width="450"
WindowStyle="None" AllowsTransparency="True" Opacity="0.7" Background="#f9f9ff"
WindowStartupLocation="Manual" Left="0" Top="0">
<Grid>
<Button x:Name="button1" Content="OK" Margin="358,341,13,72" Click="Submit_Click" FontSize="16" VerticalAlignment="Top" HorizontalAlignment="Left"/>
<RadioButton x:Name="radioButton" Content="Yes. I want to stand up and take a break for (minutes)" HorizontalAlignment="Left" Height="26" Margin="31,105,0,0" VerticalAlignment="Top" Width="368" Checked="radioButton_Checked" FontSize="14"/>
<RadioButton x:Name="radioButton1" Content="No. I don't want to stand up and take a break because" HorizontalAlignment="Left" Margin="31,206,0,0" VerticalAlignment="Top" FontSize="14" Checked="radioButton1_Checked"/>
<Slider x:Name="slider" HorizontalAlignment="Left" Height="23" Margin="40,136,0,0" VerticalAlignment="Top" Width="374" IsSnapToTickEnabled="True" ValueChanged="slider_ValueChanged" Maximum="30" Minimum="1" Cursor="Hand" AutoToolTipPlacement="TopLeft" Interval="29" IsMoveToPointEnabled="True" TickPlacement="BottomRight"/>
<!-- Animation -->
<Grid.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleY)">
<SplineDoubleKeyFrame KeyTime="0:0:0" Value="0"/>
<SplineDoubleKeyFrame KeyTime="0:0:0.5" Value="1"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="0:0:2" Value="1"/>
<SplineDoubleKeyFrame KeyTime="0:0:4" Value="1"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Grid.Triggers>
<Grid.RenderTransform>
<ScaleTransform ScaleY="1" />
</Grid.RenderTransform>
</Grid>
Does ideas about it? Thanks in advance!
Does ideas about it?
You forgot to post the markup of the custom Style that you are obviously using but you could try to set the MinWidth property of the Button to 0:
<Button x:Name="button1" Content="OK" Margin="358,341,13,72" Click="Submit_Click" FontSize="16"
VerticalAlignment="Top" HorizontalAlignment="Left" MinWidth="0"/>
Please post your all relevants parts of your XAML markup and code if still cannot increase the width of the Button.
I'm pulling my hair out trying to get this to work. Im trying to learn to do transitions and struggling a bit. Basically im making a combo box, made up of a grid containing 2 rows. top row is a button, when clicked the bottom row opens up showing a scrollviewer of dynamically added buttons. When clicked, the bottom grid row will collapse.
The problem is that the Click event does not seem to fire from within the scroll viewer, or it can't find the visual state in the scope or something.
So it the SelectionMode works fine when Button11 is clicked, but nothing happens when I click on an item button. The buttons is the scrollviewer work perfectly with their own color animations and firing events etc
Id be open to a solution in code-behind since I can make routed click event fire no problem, but I had had no luck with
VisualStateManager.GoToState(gridContent, "HiddenMode", true);
Ideally I'd like this to be a custom user control I can add like local:CustomComboBox but It complicated for me at this stage having controls inside controls in a custom control.
MyButton1 is just a simple button but with color transitions etc
No exceptions / errors just nothing happens when I click the button
<Window.Resources>
<Storyboard x:Key="sb1">
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="gridContent" Storyboard.TargetProperty="Height">
<EasingDoubleKeyFrame KeyTime="0" Value="30" />
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="160" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="scrItems" Storyboard.TargetProperty="Height">
<EasingDoubleKeyFrame KeyTime="0" Value="0" />
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="130" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="sb2">
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="gridContent" Storyboard.TargetProperty="Height">
<EasingDoubleKeyFrame KeyTime="0" Value="160" />
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="30" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="scrItems" Storyboard.TargetProperty="Height">
<EasingDoubleKeyFrame KeyTime="0" Value="130" />
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="0" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Window.Resources>
<Grid Name="Grid1" Margin="0,22,0,0" RenderTransformOrigin="0.5,0.5">
<Grid Name="gridContent" HorizontalAlignment="Left" Margin="187,74,0,0" VerticalAlignment="Top" Width="140" Height="30" RenderTransformOrigin="0.5,0.5">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="VisualStateGroup">
<VisualState x:Name="SelectionMode" Storyboard="{StaticResource sb1}" />
<VisualState x:Name="HiddenMode" Storyboard="{StaticResource sb2}" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<local1:Button1 Name="Button11" Content="Click" Height="30" Grid.Row="0" Margin="0,0,0,30">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:GoToStateAction TargetName="gridContent" StateName="SelectionMode" />
</i:EventTrigger>
</i:Interaction.Triggers>
</local1:Button1>
<ScrollViewer Name="scrItems" VerticalScrollBarVisibility="Hidden" HorizontalScrollBarVisibility="Hidden" Margin="0" Width="140" Height="0" Grid.Row="1">
<ItemsControl x:Name="stkItems">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<local1:Button1 Content="Click" Height="30">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:GoToStateAction TargetName="gridContent" StateName="HiddenMode" />
</i:EventTrigger>
</i:Interaction.Triggers>
</local1:Button1>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
</Grid>
private void Window_Loaded(object sender, RoutedEventArgs e)
{
lstItems = new ObservableCollection<MyButton.Button1>();
for (int i = 0; i <= 999; i++)
{
MyButton.Button1 item1 = new Button1();
item1.Content = "Item " + i;
item1.Width = stkItems.Width;
item1.Height = 30;
//item1.Click += new RoutedEventHandler(Button_Item_Click);
lstItems.Add(item1);
}
stkItems.DataContext = this.stkItems;
stkItems.ItemsSource = lstItems;
}
SOLVED!!!
I moved the visualStateManager to the root element - Grid1, but dont know if that affects it
private void Button_Item_Click(object sender, RoutedEventArgs e)
{
bool g = ExtendedVisualStateManager.GoToElementState(this.Grid1 as FrameworkElement, "HiddenMode", true);
}
I have a WPF-View with an ItemsControl that binds an ObservableCollection from the ViewModel. Now i want to fade out slowly items which i remove from the ObservableCollection.
ViewModel:
public class ViewModel
{
public ObservableCollection<string> Items { get; set; }
}
View:
<Window x:Class="Sandbox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"
Name="mainWindow">
<Window.Resources>
<DataTemplate x:Key="mytemplate">
<DataTemplate.Resources>
<Storyboard x:Key="OnUnloaded">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)" Storyboard.TargetName="grid">
<EasingDoubleKeyFrame KeyTime="0" Value="1"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.3" Value="-0"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)" Storyboard.TargetName="grid">
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.3" Value="-10"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</DataTemplate.Resources>
<Grid x:Name="grid" RenderTransformOrigin="0.5,0.5">
<Grid.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform/>
</TransformGroup>
</Grid.RenderTransform>
<TextBox Text="{Binding Mode=OneWay}"/>
</Grid>
<DataTemplate.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Unloaded">
<BeginStoryboard Storyboard="{StaticResource OnUnloaded}"/>
</EventTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</Window.Resources>
<StackPanel>
<ItemsControl Grid.Row="1" ItemsSource="{Binding Items}" ItemTemplate="{StaticResource mytemplate}"/>
</StackPanel>
The problem i have is now, that the storyboard begins by removing an item from the collection, but at the same time the itemscontrol remove the item and for this, the animation is not in sight...
any idea how to prevent remove the item before animation terminate?
This is much more difficult than it should be. The problem with a "remove" animation is that once an item is removed from the databound collection, its corresponding visuals are automatically removed from the element tree. This means that there is nothing left to animate out.
To work around it, you need to find a way to queue the animation before you remove the item from the databound collection and once the animation is complete, notify the ViewModel that it is okay to remove the item.
The other solution is to modify the behavior of the ItemsControl to better control the lifetime of the container visuals.
Either way, it is unfortunately not a trivial task to accomplish.
I'm hoping what I'm trying to do is possible using Visual States...
What I want to do is have a button that toggles a stackPanel between two states: 'In' and 'Out'. I would then call VisualStateManager.GoToState on the button's click event, to toggle between the two states.
The problem I've run into is I can't figure out how to attach states to the StackPanel. It won't let me using Expression Blend. So I'm stuck... Is there anyway to animate in and out this panel using VisualStateManager? (I know I could use Storyboards and create and In and Out animation, but I'd preffer to use States if possible)
I really hope this is possible. Otherwise what is VisualStateManager good for besides doing gimmicky rollover effects on buttons?
Thanks for any help!
just fired up Blend and got this:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
x:Class="WpfApplication1.MainWindow"
x:Name="Window"
Title="MainWindow"
Width="640" Height="480">
<StackPanel x:Name="LayoutRoot">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="PanelState">
<VisualState x:Name="In"/>
<VisualState x:Name="Out">
<Storyboard>
<ThicknessAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Margin)" Storyboard.TargetName="stackPanel">
<EasingThicknessKeyFrame KeyTime="0" Value="-65,15,0,0"/>
</ThicknessAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<i:Interaction.Behaviors>
<ei:DataStateBehavior Binding="{Binding IsChecked, ElementName=toggleButton}" Value="True" TrueState="In" FalseState="Out"/>
</i:Interaction.Behaviors>
<Button Content="Button" Width="50" HorizontalAlignment="Left" Click="Button_Click"/>
<StackPanel x:Name="stackPanel" Height="100" HorizontalAlignment="Left" Margin="0,15,0,0">
<TextBlock><Run Text="Funnytext"/></TextBlock>
</StackPanel>
<ToggleButton x:Name="toggleButton" Content="Toggle" Width="50" HorizontalAlignment="Left"/>
</StackPanel>
</Window>
and code behind:
private void Button_Click(object sender, System.Windows.RoutedEventArgs e)
{
var sgs=VisualStateManager.GetVisualStateGroups(LayoutRoot);
var sg=sgs[0] as VisualStateGroup;
VisualStateManager.GoToElementState(LayoutRoot, ((VisualState) sg.States[sg.CurrentState == sg.States[0]?1:0]).Name,true);
}
(didn't know what stackpanel you meant so i just included two)
EDIT: my bad, didn't notice I didn't include the clickhandler. For for your convenience I included an example to use a Togglebutton to toggle the states...
I am animating some property using DoubleAnimation. Before animation is triggered any local or Setter changes are properly reflected in the property. After animation completes nothing seems to be able to change the value of the property. I have even tried ClearValue and InvalidateProperty as well set calling SetValue but the value leftover from animation persists. If animation is repeated, the property continues to be animated as expected so it only appears to be locked for non-animation changes.
Is there a way to rectify this behavior? I want to use the animation to change the property value but still be able to change it manually or via a Setter to anything else. I know a thing or two about Dependency Property Value Precedence but the behavior I am currently experiencing is a bit strange. I'd hate to have to use "manual animations".
EDIT: Added sample XAML + code.
<Window x:Class="WpfApplication7.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300"
x:Name="_this"
Background="Red">
<DockPanel>
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" HorizontalAlignment="Center" >
<Button Click="ToggleOnClick">Toggle!</Button>
<Button Click="SetHalfOnClick">Set to 0.5!</Button>
</StackPanel>
<TextBox DockPanel.Dock="Bottom" IsReadOnly="True" Text="{Binding ElementName=_viewbox,Path=Opacity}" />
<Viewbox x:Name="_viewbox">
<Viewbox.Style>
<Style TargetType="{x:Type FrameworkElement}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsToggled,ElementName=_this,Mode=OneWay}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation To="0.2"
Duration="0:0:0.5"
Storyboard.TargetProperty="(UIElement.Opacity)" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation To="1"
Duration="0:0:0.5"
Storyboard.TargetProperty="(UIElement.Opacity)" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Viewbox.Style>
<TextBlock Text="Sample!" />
</Viewbox>
</DockPanel>
</Window>
Here is the code:
using System.Windows;
namespace WpfApplication7
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1
{
public bool IsToggled
{
get { return (bool)GetValue(IsToggledProperty); }
set { SetValue(IsToggledProperty, value); }
}
public static readonly DependencyProperty IsToggledProperty = DependencyProperty.Register("IsToggled", typeof(bool), typeof(Window1), new UIPropertyMetadata(false));
public Window1()
{
InitializeComponent();
}
private void ToggleOnClick(object sender, RoutedEventArgs e)
{
IsToggled = !IsToggled;
}
private void SetHalfOnClick(object sender, RoutedEventArgs e)
{
_viewbox.Opacity = 0.5;
}
}
}
Edit 2 in response to comments:
In your example you can work around the problem by:
Setting FillBehaviour to Stop on the animation
Adding a handler in code to the Completed event:
<Storyboard Completed="FadeOut_Completed">
Finally, set the desired 'final' value in the Completed handler (either explicitly or by using the current value of the property
private void FadeOut_Completed(object sender, EventArgs e)
{
_viewbox.Opacity = _viewbox.Opacity; //this sets the DP value to the animated value
}
This works in your sample; hopefully it will work in your problem!
Original Answer
If you set the FillBehaviour property of the Storyboard to Stop (instead of the default value of HoldEnd) it will revert to the pre-animation value of the property once the animation completes. HoldEnd causes the animation to maintain its final value on the property
Update in response to comments:
As noted in the comments, the animation value will override the value set against the property when HoldEnd is specified as the FillBehaviour.
This makes it slightly tricky to set the value to something else.
I am not sure if there is a better way to achieve this, but the example below shows one way to work around it. Its hard to judge how applicable this is without a sample usage from the OP, but in this example I am animating the width of a Rectangle on load, and then resetting it to another value when a button is clicked:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Triggers>
<EventTrigger RoutedEvent="Window.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="Target" Storyboard.TargetProperty="Width"
From="10" To="100" Duration="0:00:01" FillBehavior="HoldEnd" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Window.Triggers>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Rectangle Height="10" Width="10" Fill="Red" x:Name="Target"/>
<Button Grid.Row="1" Content="Resize">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="Target" Storyboard.TargetProperty="Width">
<DiscreteDoubleKeyFrame Value="50" KeyTime="0:00:00" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
</Grid>
</Window>
This works because the new animation overrides the value set in the original.
You can use UIElement.BeginAnimation with the animation parameter set to null. It will clear all animations attached to your property.