I have a WPF control that I want to animate, based on the value of a property IsInteracting defined on the containing UserControl (named "UserControl"). I have the following style defined in the UserControl, with the animation targeting a Grid also defined in the UserControl.
<UserControl.Resources>
<Style TargetType="Grid">
<Setter Property="Opacity" Value="0" />
<Setter Property="Height" Value=""></Setter>
<Style.Triggers>
<Trigger Property="IsInteracting" Value="True" SourceName="UserControl">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimationUsingKeyFrames
Storyboard.Target="{Binding ElementName=ControlGrid}"
Storyboard.TargetProperty="Opacity"
>
<EasingDoubleKeyFrame KeyTime="0:0:0.1" Value="1"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.8" Value="1"/>
<EasingDoubleKeyFrame KeyTime="0:0:1.3" Value="0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
This isn't working as the name "UserControl" isn't recognised. I believe there are scoping issues which prevent the referencing of elements outside of the style?
How do I do what I'm trying to do?
Try this instead
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=IsInteracting}" Value="True">
<DataTrigger.EnterActions>
</DataTrigger.EnterActions>
</DataTrigger>
I think that the style is not correct place to use some another (outer) control trigger to start the animation. I suggest you to look at the next solution here, there was a similar problem.
I'll be glad to help if you will have a problem with code.
Regards.
Related
I want to change the grid background via binding, if a condition is true.
I am using the MVVM light framework.
<Grid.Style>
<Style TargetType="Grid">
<Style.Triggers>
<DataTrigger Binding="{Binding SuccessBooked, UpdateSourceTrigger=PropertyChanged}"
Value="True">
<!--Setter Property="Background" Value="LimeGreen" />-->
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation To="LimeGreen"
Storyboard.TargetName="ActualWeightBg"
Storyboard.TargetProperty="Background"
FillBehavior="Stop"
Duration="0:0:12"/>
<!--<DoubleAnimation Storyboard.TargetProperty="Opacity"
From="0.0" To="1.0" Duration="0:0:15"/>-->
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding LowerBooked, UpdateSourceTrigger=PropertyChanged}"
Value="True">
<Setter Property="Background" Value="Red" />
</DataTrigger>
<DataTrigger Binding="{Binding HigherBooked, UpdateSourceTrigger=PropertyChanged}"
Value="True">
<Setter Property="Background" Value="DarkOrange" />
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
The compiler complains:
If I remove Storyboard.TargetName="ActualWeightBg":
<ColorAnimation To="LimeGreen"
Storyboard.TargetProperty="Background"
FillBehavior="Stop"
Duration="0:0:12"/>
I get this exception:
Exception thrown: 'System.InvalidOperationException' in PresentationFramework.dll
I want that background color is going to change for 5s, after that it should change back to the standard color.
Update
So when a background color is not set, it will throw the error:
Cannot resolve all property references in the property path
'Background.Color'. Verify that applicable objects support the
properties.
As you see here, the background color is not set
then the error will occur:
But when the background is set:
Then it will work as expected.
How can I set an animated background color, without background color being set.
It is an usercontrol.
First of all, StyleTriggers in general dont support TargetNames.
AFAIK only TemplateTriggers do support them.
Your DataTrigger should look like this.
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetProperty="Background.Color" To="LimeGreen"
FillBehavior="Stop" AutoReverse="True" Duration="0:0:5" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
Notice the Storyboard.TargetProperty="Background.Color". Also your TimeSpan was too high. Another thing is setting the AutoReverse to true.
EDIT
To get this Trigger to work, in the Grid's Style there has an initial Background to be set.
<Style TargetType="Grid">
<Setter Property="Background" Value="Red"/>
I have a ToggleButton which I click to popup a FlowDocumentReader as an Adorner. This FlowDocument is part of a ControlTemplate with a DataTrigger to show/hide the element.
Using the following Trigger everything works fine. I use a DataTrigger and some Setters, my element displays correctly with Height and Width I provide when I check my ToggleButton:
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding ElementName=adorner, Path=AdornedElement.IsChecked}" Value="True" >
<Setter TargetName="mainBorder" Property="Height" Value="437"></Setter>
<Setter TargetName="mainBorder" Property="Width" Value="537"></Setter>
</DataTrigger>
</ControlTemplate.Triggers>
I want to have some animation that occurs as my element appears, so I tried to used a Storyboard. This doesn't work, nothing seems to happen:
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding ElementName=adorner, Path=AdornedElement.IsChecked}" Value="True" >
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard Storyboard.TargetName="mainBorder">
<DoubleAnimation Duration="0:0:0.2" Storyboard.TargetProperty="Width" To="537" />
<DoubleAnimationUsingKeyFrames BeginTime="0:0:0.2" Duration="0:0:0.3" Storyboard.TargetProperty="Height">
<LinearDoubleKeyFrame Value="417" KeyTime="0:0:0.2" />
<LinearDoubleKeyFrame Value="437" KeyTime="0:0:0.24" />
<LinearDoubleKeyFrame Value="417" KeyTime="0:0:0.3" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard Storyboard.TargetName="mainBorder">
<DoubleAnimationUsingKeyFrames Duration="0:0:0.2" Storyboard.TargetProperty="Width">
<LinearDoubleKeyFrame Value="0" KeyTime="0:0:0.2" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="0:0:0.2" Duration="0:0:0.2" Storyboard.TargetProperty="Height">
<LinearDoubleKeyFrame Value="0" KeyTime="0:0:0.2" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</ControlTemplate.Triggers>
Is the Storyboard context completely different than the Setter? Why does it work in one place, but not the other?
Oddly enough, when I make this change, it causes a binding error to display in the Output Window. I haven't touched the actual binding for the DataTrigger, just the contents:
System.Windows.Data Error: 4 : Cannot find source for binding with
reference 'ElementName=adorner'.
BindingExpression:Path=AdornedElement.IsChecked; DataItem=null; target
element is 'Control' (Name=''); target property is 'NoTarget' (type
'Object')
Here is a general idea of the rest of the template:
<ControlTemplate x:Key="LocalHelpWindow">
<Grid>
<Grid.RowDefinitions>
...
</Grid.RowDefinitions>
<help:AdornedPlaceholder x:Name="adorner" Grid.Row="0"/>
<Border Grid.Row="1" x:Name="mainBorder">
...
</Border>
</Grid>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding ElementName=adorner, Path=AdornedElement.IsChecked}" Value="True" >
...
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
I think in your case you should use Trigger instead of DataTrigger. DataTrigger is using to bind to some data in DataContext of element in which DataTemplate was applied.
I suggest you try to use Trigger in that way:
<Trigger SourceName="adorner" Property="AdornedElement.IsChecked" Value="True">
...
</Trigger>
I've just started with WPF (I'm sorry if the question is too obvious), and I managed to put together this mouseover style. The background color animates to a darker color. I now want to also animate the text to white, so it's easier to read.
This is how I tried to add it, but it gives me the error "Cannot resolve all property references in the property path 'TextBlock.Foreground'. Verify that applicable objects support the properties" when I mouseover it.
<Border Background="#e6ebf3" CornerRadius="0,10,0,10" >
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="#e6ebf3" />
<Setter Property="TextBlock.Foreground" Value="Black"/>
<Style.Triggers>
<EventTrigger RoutedEvent="UIElement.MouseEnter">
<BeginStoryboard>
<Storyboard>
<ColorAnimation Duration="0:0:0.1" Storyboard.TargetProperty="Background.Color" To="#6d809b" />
<ColorAnimation Duration="0:0:0.1" Storyboard.TargetProperty="TextBlock.Foreground" To="white" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="UIElement.MouseLeave">
<BeginStoryboard>
<Storyboard>
<ColorAnimation Duration="0:0:0.1" Storyboard.TargetProperty="Background.Color" To="#e6ebf3" />
<ColorAnimation Duration="0:0:0.1" Storyboard.TargetProperty="TextBlock.Foreground" To="Black" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
</Border.Style>........
I found an alternative way without using storyboard or animations, so I'll post it just in case. Still wondering about the original one, though.
<Border CornerRadius="0,10,0,10" >
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="#e6ebf3" />
<Setter Property="TextBlock.Foreground" Value="Black"/>
<Style.Triggers>
<Trigger Property ="IsMouseOver" Value="True">
<Setter Property= "Background" Value="#6d809b"/>
<Setter Property= "TextBlock.Foreground" Value="White"/>
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
...
Indirect property targeting, that being TextBlock.Foreground is described here http://msdn.microsoft.com/en-us/library/ms742451.aspx. It's basically saying, "hey I couldn't find a property called TextBlock on type button." It works with Background.Color because the Background Property does exist on Button and it's of type ColorBrush which itself has a property of type Color.
Edit: In the original question i made some wrong assumptions about how setters work so i modified it to hopefully be more accurate and useful.
I have tried to make some menu items more interesting by having the icons appear half-transparent if the mouse is not over the item. If the mouse enters, the icon should be animated to become completely visible.
The animations work, Storyboard.TargetProperty allows direct access to the icon's opacity property:
<Style x:Key="MenuItemMouseOverStyle" TargetType="MenuItem">
<Style.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Icon.Opacity">
<EasingDoubleKeyFrame KeyTime="0" Value="0.5"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.3" Value="1"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Icon.Opacity">
<EasingDoubleKeyFrame KeyTime="0" Value="1"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.3" Value="0.5"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
If i try to use a setter for the initial icon-opacity the code won't compile:
<Setter Property="Icon.Opacity" Value="0.5"/>
Edit:
Setters do not work the way i tried to use them, you cannot access the properties of properties (see answers)
The only thing you can do is specify a target class if the target type of the style has not been set, the following styles should be equivalent:
<Style x:Key="Style1" TargetType="Image">
<Setter Property="Opacity" Value="0.5"/>
</Style>
<Style x:Key="Style2">
<Setter Property="Image.Opacity" Value="0.5"/>
</Style>
So my question is if there is a way to make this somehow work with a setter.
(My current work-around is a single-keyframe storyboard that is triggered with the Loaded event which works quite well)
I don't think you can access a Property of a Property like that so the casting itself isn't the problem. Even if Icon was of Type Image that still wouldn't work. You can try with Backgrounds Opacity for a Grid for example. Background is a Dependency Property for Grid and Opacity is a Dependency Property for Brush but the following line won't work
<Grid Background.Opacity="0.8"/>
You'll get an error saying
The attachable property 'Opacity' was
not found in type 'Background'.
You would have to set this in the Background itself like this
<Grid>
<Grid.Background>
<SolidColorBrush Opacity="0.8"/>
</Grid.Background>
</Grid>
So what this means as when you do something like this
<Grid TextBlock.Foreground="Red">
<TextBlock Text="Test"/>
</Grid>
you're actually using the Attached Property Foreground for TextBlock.
Image doesn't have an Attached Property called Opacity so you can't do this either
<MenuItem Image.Opacity="0.8" />
Another workaround besides the one you're already doing is to use something like this (top-most MenuItem or wherever you want to use it).
<MenuItem x:Name="topMenuItem"
...>
<MenuItem.Resources>
<Style TargetType="Image">
<Setter Property="Opacity" Value="0.5"/>
</Style>
</MenuItem.Resources>
<!-- ... -->
</MenuItem>
So after reading Meleak's answer and finding out that you can have a style within a style via resources what probably comes closest to doing this with a setter is an embedded style to access the icon's opacity. Here i assume the icon to be an image so i use that as the target type, the complete style hence looks like this:
<Style x:Key="MenuItemMouseOverStyle" TargetType="MenuItem">
<Style.Resources>
<Style TargetType="Image">
<Setter Property="Opacity" Value="0.5"/>
</Style>
</Style.Resources>
<Style.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Icon.Opacity">
<EasingDoubleKeyFrame KeyTime="0" Value="0.5"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.3" Value="1"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Icon.Opacity">
<EasingDoubleKeyFrame KeyTime="0" Value="1"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.3" Value="0.5"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
The only problem with this is that it does not actually set the Icon.Opacity but the opacity of all images that may occur within the MenuItem.
I'm developing an XBAP and i have a simple requirement.
The DataContext of the whole main page is set to an instance of my UserViewModel. The UserViewModel has a DependencyProperty called AuthenticationState which is an enum with values like 'Authenticated','NotAutheticated' and 'AuthenticationFailed'.
Now, i need to respond to any change in this value by hiding/displaying various elements on the page.
What (and where) is the best way of doing that?
As you mentioned you can't use a DataTrigger directly on a control. A work around would be to use a style on each Control that needs to be hidden.
<Grid>
<Rectangle Fill="Red" />
<Grid.Style>
<Style TargetType="Grid">
<Style.Triggers>
<DataTrigger Binding="{Binding Test}" Value="true">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
</Grid>
A preferable method would be to use a Converter called "AuthenticationStateToVisibilityConverter" that is used in binding the control's Visibility property to the data context's AuthenticationState property.
The best way would be to use a DataTrigger. So something like this:
<Window.Triggers>
<DataTrigger Binding="{Binding AuthenticationState}" Value="NotAuthenticated">
<Setter TargetName="nameOfControl" Property="Visibility" Value="Collapsed" />
</DataTrigger>
...
<TextBox x:Name="nameOfControl" />
</Window.Triggers>
As long as you UserViewModel object is in the DataContext of the Window then this should work!
Managed to sort it using styles. It's a pain but it works!
The full source is below.
<Grid x:Name="contentGrid" Grid.Row="1">
<!--login-->
<controls:LoginControl>
<controls:LoginControl.Style>
<Style>
<Setter Property="Control.Opacity" Value="0"/>
<Setter Property="Control.IsHitTestVisible" Value="False"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Source={StaticResource UserViewModel},Path=UserAuthenticationState}"
Value="{x:Static model:AuthenticationState.NotAuthenticated}">
<Setter Property="Control.IsHitTestVisible" Value="True"/>
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation To="1" Duration="0:0:2"
Storyboard.TargetProperty="Opacity"></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation To="0" Duration="0:0:2"
Storyboard.TargetProperty="Opacity"></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</controls:LoginControl.Style>
</controls:LoginControl>
<!--slider-->
<slider:PageSlider>
<Button>1</Button>
<Button>2</Button>
<Button>3</Button>
<slider:PageSlider.Style>
<Style>
<Setter Property="Control.Opacity" Value="0"/>
<Setter Property="Control.IsHitTestVisible" Value="False"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Source={StaticResource UserViewModel},Path=UserAuthenticationState}"
Value="{x:Static model:AuthenticationState.Authenticated}">
<Setter Property="Control.IsHitTestVisible" Value="True"/>
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation To="1" Duration="0:0:2"
Storyboard.TargetProperty="Opacity"></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation To="0" Duration="0:0:2"
Storyboard.TargetProperty="Opacity"></DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</slider:PageSlider.Style>
</slider:PageSlider>
</Grid>
Actually, the best way to do this is to expose the appropriate properties from your view model. This makes your logic more centralized and easier to test. Also, it performs better than converters. It is, after all, a view model. Therefore, it should model the view. If the view needs a property to tell it when to hide / show a panel, add such a property to your view model.