I have following wpf code
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary>
<Image x:Key="MyImage" Source="../Assets/icon.ico"/>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
<Storyboard x:Key="AkomiLogoAnimation">
<DoubleAnimation
Storyboard.TargetProperty="(Image.RenderTransform).(RotateTransform.Angle)"
By="360"
SpeedRatio="0.5"
RepeatBehavior="Forever"
FillBehavior="Stop"
/>
</Storyboard>
</ResourceDictionary>
</Window.Resources>
<TextBox x:Name="txtAuto"/>
<Image Source="../Assets/icon.ico" Width="20" Height="20" RenderTransformOrigin=".5,.5">
<Image.Resources>
<converter:IsNullConverter x:Key="IsNullConverter"/>
</Image.Resources>
<Image.RenderTransform>
<RotateTransform Angle="0" />
</Image.RenderTransform>
<Image.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=txtAuto, Path=Text, Converter={StaticResource IsNullConverter}, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}" Value="False">
<DataTrigger.EnterActions>
<BeginStoryboard Name="AkomiRotation" Storyboard="{StaticResource AkomiLogoAnimation}">
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=txtAuto, Path=Text, Converter={StaticResource IsNullConverter}, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}" Value="True">
<DataTrigger.EnterActions>
<StopStoryboard BeginStoryboardName="AkomiRotation" >
</StopStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
The goal is to rotate the image as soon as some text has been entered. But stopping the storyboard lets the image angle jump into it's initial value.
I tried to add FillBehavior="HoldEnd" to the Storyboard definition, but that doesn't work.
Any suggestions? Maybe I can access the angle-value at the end of the animation anyhow and set this value in RotateTransform
Have you tried PauseStoryboard, instead of stop? Also, you should change fillbehavior on the animation
Related
I'm trying to implement color animation on border brush with Style.Triggers.
The ViewModel changes the collection which each Border color is bounded.
I guess the UI elements are not initialized but the condition is fulfilled and then the exception is thrown:
"Additional information: Cannot resolve all property references in the property path 'BorderBrush.Color'. Verify that applicable objects support the properties."
Here the View.cs.xaml code:
<HierarchicalDataTemplate DataType="{x:Type model:SchemaNode}"
ItemsSource="{Binding Children}">
<Border CornerRadius="40">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="{DynamicResource MaterialDesignPaper}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Closed}" Value="True">
<Setter Property="Background" Value="{Binding Outline}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<Border.InputBindings>
<MouseBinding MouseAction="LeftDoubleClick"
Command="{Binding OnDoubleClick}" CommandParameter="{Binding GlobalId}"/>
</Border.InputBindings>
<Border.ContextMenu>
<ContextMenu ItemsSource="{Binding ContextActions}" Width="150" Placement="Mouse">
<ContextMenu.ItemTemplate>
<DataTemplate DataType="{x:Type helper:ContextAction}">
<MenuItem Header="{Binding Header}" Command="{Binding Command}"/>
</DataTemplate>
</ContextMenu.ItemTemplate>
</ContextMenu>
</Border.ContextMenu>
<Border BorderThickness="3" CornerRadius="360"
BorderBrush="{Binding Outline}" Width="{Binding Width}" Height="{Binding Height}">
<Border.Style>
<Style TargetType="Border">
<Style.Triggers>
<DataTrigger Binding="{Binding Located}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard x:Name="LocatedBeginStoryBoard">
<Storyboard Duration="0:0:0.5" RepeatBehavior="Forever" AutoReverse="True">
<ThicknessAnimation Storyboard.TargetProperty="BorderThickness" Duration="0:0:0.4"
FillBehavior="HoldEnd"
To="5"/>
<ThicknessAnimation Storyboard.TargetProperty="BorderThickness"
FillBehavior="HoldEnd"
BeginTime="0:0:0.4"
To="5"/>
<ColorAnimation Storyboard.TargetProperty="BorderBrush.Color"
To="White"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<RemoveStoryboard BeginStoryboardName="LocatedBeginStoryBoard"/>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<Image Height="50"
Width="50" Source="{Binding ImageSource, Converter={StaticResource imagePathToImageConverter}}"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Image.InputBindings>
<MouseBinding MouseAction="LeftDoubleClick" Command="{Binding OnDoubleClick}" CommandParameter="{Binding GlobalId}"/>
</Image.InputBindings>
</Image>
</Border>
</Border>
</HierarchicalDataTemplate>
Hopefully I have provided enough code do resolve this problem.
Most likely the BorderBursh you bind via Outline is not a SolidColorBrush but a GradientBrush or simply unassigned and therefore doesn't have a Color property.
You can test this by assigning a SolidColorBrush (directyl or via style).
<Style TargetType="Border">
<Style.Setters>
<Setter Property="BorderBrush" Value="Blue"/>
</Style.Setters>
<!-- etc -->
I am trying to create a storyboard, so when a user clicks on a textbox, its copies the text to their clipboard and shows a tooltip saying copied, which then fades away.
Here is my attempt:
xaml:
<TextBox Name="PolyValue" Text="{Binding .}" IsReadOnly="True" BorderThickness="0" Background="White"
VerticalAlignment="Center" PreviewMouseDown="CopyTextBox" >
<TextBox.ToolTip>
<ToolTip Style="{StaticResource TooltipPopupFadeAway}" IsOpen="True" Opacity="0" Background="Transparent" BorderThickness="0">
<Border Background="White" BorderBrush="Black" BorderThickness="1" CornerRadius="3" >
<Label Content="Copied" Padding="5, 2" />
</Border>
</ToolTip>
</TextBox.ToolTip>
</TextBox>
Here is the Storyboard:
<Style x:Key="TooltipPopupFadeAway" TargetType="ToolTip">
<Style.Triggers>
<DataTrigger Binding="{Binding Opacity, RelativeSource={RelativeSource Self}}" Value="1">
<DataTrigger.EnterActions>
<BeginStoryboard x:Name="ClosePopupStoryBoard">
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" Duration="00:00:01" From="3" To="0" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
Code behind:
private void CopyTextBox(object sender, MouseButtonEventArgs e)
{
if(sender is TextBox textBox)
{
Clipboard.SetText(textBox.Text);
(textBox.ToolTip as ToolTip).IsOpen = false;
(textBox.ToolTip as ToolTip).IsOpen = true; //this recalculates the position
(textBox.ToolTip as ToolTip).Opacity = 1;
}
}
This works exactly how I want it to, however it only works once, after it has been shown and the user clicks again nothing happens.
After setting a break point in the CopyTextBox method, the of the ToolTip opacity is 0 even after programmatically setting it to 1.
I am not sure what I am doing wrong?
This is the sort of approach I mean.
Just binding the text property means you need no code.
I'm not sure this does exactly what you want because you seem to have previewmousedown showing the tooltip. Which is a bit odd for a tooltip since mouseover shows them.
<Window.Resources>
<ControlTemplate x:Key="TooltipPopupFadeAway" TargetType="ToolTip">
<Border Background="Yellow">
<TextBlock Text="{Binding PlacementTarget.Text, RelativeSource={RelativeSource AncestorType={x:Type ToolTip}}}"
Name="TheText"
/>
</Border>
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="ToolTip.Opened">
<BeginStoryboard>
<Storyboard TargetProperty="Opacity">
<DoubleAnimation From="1.0" To="0" Duration="0:0:2" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Window.Resources>
<Grid>
<TextBox>
<TextBox.ToolTip>
<ToolTip Template="{StaticResource TooltipPopupFadeAway}"/>
</TextBox.ToolTip>
</TextBox>
</Grid>
And you could set that template and tooltip etc via style if it suited you better. I used a yellow background so I can see it easy. The tooltip probably doesn't exactly match what you had.
You should trigger directly on the IsOpen property. Make sure to set the default Opacity to 0, and do not explicitly set it to 1 afterwards.
<Style x:Key="TooltipPopupFadeAway" TargetType="ToolTip">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Opacity" Value="0"/>
<Setter Property="IsOpen" Value="False"/>
<Style.Triggers>
<Trigger Property="IsOpen" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
Duration="0:0:1" From="3" To="0"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
In code behind, do not to set the Opacity:
private void CopyTextBox(object sender, MouseButtonEventArgs e)
{
if (sender is TextBox textBox)
{
Clipboard.SetText(textBox.Text);
((ToolTip)textBox.ToolTip).IsOpen = false;
((ToolTip)textBox.ToolTip).IsOpen = true;
}
}
I have a problem with animation on an image Mouse Over Event trigger in WPF. When the mouse touches the boundaries of the Image and stands still, the animation restarts repeatedly forever until moving mouse. What should I do in this case to stop it and the trigger fires just one time?
My code is:
<Image Source="/Project_12;component/Images/cancel-64.png" RenderTransformOrigin=".5,.5" Stretch="Uniform">
<Image.RenderTransform>
<RotateTransform x:Name="AnimatedRotatex" Angle="0" />
</Image.RenderTransform>
<Image.Style>
<Style TargetType="Image">
<Style.Triggers>
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard Name="sb">
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="RenderTransform.(RotateTransform.Angle)"
By="0"
To="90"
Duration="0:0:.5"
FillBehavior="HoldEnd" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<RemoveStoryboard BeginStoryboardName="sb" />
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
The basic problem of your animation is, that you use the IsMouseOver property of the animated object. As soon as the image gets rotated, you move the HitTestArea of the image "out of the way" which causes the IsMouseOver property to become false, ending the animation and therefore causing an infinite loop. I added a blue border to show the HitTestArea of the image to highlight the problem.
What you need to do, is create another, static area (with a transparent Background to make sure it has a HitTestArea) and use the IsMouseOver property of that are instead (red border).
That way, even if the image "moves away" from the mouse cursor, the IsMouseOver property of the other element will be unaffected by the moving image.
An alternative approach would be to use a circle as the HitTestArea since it won't be affected by rotation.
Source Code of above example:
<Border Background="Transparent" BorderThickness="1" BorderBrush="Red" Width="40" Height="40">
<Border BorderThickness="1" BorderBrush="Blue" Width="36" Height="36" RenderTransformOrigin=".5,.5" IsHitTestVisible="False">
<Border.RenderTransform>
<RotateTransform x:Name="AnimatedRotatex" Angle="0" />
</Border.RenderTransform>
<Border.Style>
<Style TargetType="Border">
<Style.Triggers>
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource AncestorType=Border}}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="RenderTransform.(RotateTransform.Angle)"
By="0"
To="90"
Duration="0:0:.2"
FillBehavior="HoldEnd" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="RenderTransform.(RotateTransform.Angle)"
By="90"
To="0"
Duration="0:0:.2"
FillBehavior="HoldEnd" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<Image VerticalAlignment="Center" HorizontalAlignment="Center" Width="32" Height="32" Stretch="Uniform" Source="cancel.png"/>
</Border>
</Border>
I am trying to open popup according to data change in my ViewModel:
Popup is defined as this:
<Popup x:Name="popup"
AllowsTransparency="True"
Focusable="False"
IsHitTestVisible="False"
Placement="Bottom"
PopupAnimation="Slide"
StaysOpen="False">... </Popup>
And there I have user control
<UserControl>
...
<ControlTemplate>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding PopupOpened}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource ShowPopup}"/>
</DataTrigger.EnterActions>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</UserControl>
And my animation is defined as (in resources):
<Storyboard x:Key="ShowPopup">
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="popup" Storyboard.TargetProperty="(Popup.IsOpen)">
<DiscreteBooleanKeyFrame KeyTime="00:00:00.00" Value="True" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
If I trigger this animation from EventTrigger on some control's triggers (button for example) it works correctly.
It doesn't work correctly inside DataTrigger.
EDIT:
I found an issue:
If you are controlling Popup.IsOpen property with Animation + binding, Binding to that property will work only till you will use animation to change that property. After that Binding will not work anymore. Therefore you have to always change IsOpen property through Animation or Binding not mixing!
There are several errors in your example which obscure the real issue.
Your DataTrigger element is not closed and 'StaticResources' should be StaticResource.
But ultimately, template for UserControl is not used right, you missed UserControl.Template setting. If we fix it, we'll see the exception:
<UserControl>
<UserControl.Template>
<ControlTemplate>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding PopupOpened}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource ShowPopup}" />
</DataTrigger.EnterActions>
</DataTrigger>
</ControlTemplate.Triggers>
<!-- Some content for template to have substance -->
<TextBlock />
</ControlTemplate>
</UserControl.Template>
</UserControl>
Here's the result:
'popup' name cannot be found in the name scope of
'System.Windows.Controls.ControlTemplate'.
The easiest way to actually do that, IMO, is a simple direct binding:
<Popup x:Name="popup"
IsOpen="{Binding PopupOpened, Mode=TwoWay}">
Or, if you really need the trigger, you can move it to Popup itself:
<Popup x:Name="popup">
<Popup.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding PopupOpened}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="IsOpen">
<DiscreteBooleanKeyFrame KeyTime="00:00:00.00" Value="True" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Popup.Style>
<TextBlock />
</Popup>
I have a listbox with templated listboxitem which is binded to an object.
one of the listboxitem controls is an image and it's source is also binded to a property inside the datacontext, So the pictur inside the Image control is different according to that property.
I want to animate the image but only specific one (when the binded property value is some specific value only).
I guess there are other ways to do that, maybe using code behind. But I would realy like use it in the xaml code, so I thought using animation inside DataTrigger, it makes sense to me because the starting/ending the animation depends on the DataContext:
<DataTrigger Binding="{Binding Path=Value.SomeProperty}"
Value="SomePropertyValue">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard TargetName="SomePropertyIcon">
<StaticResource ResourceKey="SomePropertyValueAnimation"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard TargetName="SomePropertyIcon">
<StaticResource ResourceKey="StopSomePropertyValueAnimation"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
But I cannot make it work any way.
I tried to put that code inside a style of the listBox and then apply that style to the image, it didnt work, I also tried to put inside the listBox.ItemTamplate, and other places but nothing helped.
Here is the code. maybe it can help to understand (I pasted only relevan code here):
<UserControl ...>
<UserControl.Resources>
<local:SomePropertyToImageConverter x:Key="somePropertyToImageConverter"/>
<DoubleAnimation x:Key="SomePropertyValueAnimation" Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)" To="45" Duration="0:0:2" RepeatBehavior="Forever"/>
<DoubleAnimation x:Key="StopSomePropertyValueAnimation" Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)" To="0" Duration="0:0:2"/>
</UserControl.Resources>
<Grid>
<ListBox Name="myListBox"
ItemsSource="{Binding Path=myDataContext}" IsEnabled="True" Focusable="True" SelectionMode="Extended">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Image Width="20" Height="20" Name="SomePropertyIcon"
Source="{Binding Path=Value.SomeProperty, Converter={StaticResource somePropertyToImageConverter}}"/>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=Value.SomeProperty}"
Value="SomePropertyValue">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard TargetName="SomePropertyIcon">
<StaticResource ResourceKey="SomePropertyValueAnimation"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard TargetName="SomePropertyIcon">
<StaticResource ResourceKey="StopSomePropertyValueAnimation"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Value.SomeProperty}"
Value="SomePropertyValue">
<Setter Property="Background" Value="Bisque"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</Grid>
Animation cannot change property values for a property path that does not exist. The "Image" you are trying to manipulate does not have "Rotate transform" you should add it and it should work:
<Image Width="20" Height="20" Name="SomePropertyIcon"
Source="{Binding Path=Value.SomeProperty, Converter={StaticResource somePropertyToImageConverter}}">
<Image.RenderTransform >
<RotateTransform />
</Image.RenderTransform>
</Image>