Setting VisualState on ItemsControl item Templates - c#

I have an ItemsControl with an ItemTemplate that is bound to a data item. The ItemTemplate is quite complicated and I have added some visual state to it so that I can change it's appearance.
I want to be able to switch all the items VisualState to another state of my choice, on an event that occurs outside the ItemsControl.
How can I go about doing this? I've tried using the VisualStateManager.SetState, but this relies on a control, rather than a template, which seems to be all I can get via WaveItems.ItemContainerGenerator.ContainerFromIndex(i).
Regards
Tristan
Edit:
Here is my data template for the individual items. If you note the triggers I have set up, it handles the MouseEnter / MouseLeave of the template itself. I'd like to wire these up to the ItemsControl MouseEnter / MouseLeave, without writing any code. Is there a way to do this?
<DataTemplate x:Key="LineTemplate">
<Grid x:Name="LineGrid" HorizontalAlignment="Left" Height="500" VerticalAlignment="Center" Width="3">
<VisualStateManager.CustomVisualStateManager>
<ei:ExtendedVisualStateManager/>
</VisualStateManager.CustomVisualStateManager>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="VisualStateGroup">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0"/>
</VisualStateGroup.Transitions>
<VisualState x:Name="Expanded">
<Storyboard>
<DoubleAnimation Duration="0:0:1" By="-100" Storyboard.TargetProperty="(Line.Y2)" Storyboard.TargetName="lineTop"/>
<DoubleAnimation Duration="0:0:1" By="100" Storyboard.TargetProperty="(Line.Y2)" Storyboard.TargetName="lineBottom"/>
<DoubleAnimation Duration="0:0:1" To="0.5" Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="lineTop" d:IsOptimized="True"/>
<DoubleAnimation Duration="0:0:1" To="0.25" Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="lineBottom" d:IsOptimized="True"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Normal">
<Storyboard>
<DoubleAnimation Duration="0:0:1" To="0" Storyboard.TargetProperty="(Line.Y2)" Storyboard.TargetName="lineTop" d:IsOptimized="True"/>
<DoubleAnimation Duration="0:0:1" To="{Binding BottomValue}" Storyboard.TargetProperty="(Line.Y2)" Storyboard.TargetName="lineBottom" d:IsOptimized="True"/>
<DoubleAnimation Duration="0:0:1" To="1" Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="lineTop" d:IsOptimized="True"/>
<DoubleAnimation Duration="0:0:1" To="0.495" Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="lineBottom" d:IsOptimized="True"/>
</Storyboard>
</VisualState>
<VisualState x:Name="ExpandedHighlight"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseEnter" >
<ei:GoToStateAction x:Name="Expand" StateName="Expanded"/>
</i:EventTrigger>
<i:EventTrigger EventName="MouseLeave">
<ei:GoToStateAction x:Name="Collapse" StateName="Normal"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Line Grid.Row="0" x:Name="lineTop" VerticalAlignment="Bottom" StrokeThickness="3" Stroke="{Binding Brush}" Y1="{Binding TopValue}" Y2="0" RenderTransformOrigin="0.5,0.5">
<Line.RenderTransform>
<CompositeTransform ScaleY="1"/>
</Line.RenderTransform>
</Line>
<Line Grid.Row="1" x:Name="lineBottom" VerticalAlignment="Top" StrokeThickness="3" Stroke="{Binding Brush}" Y1="0" Y2="{Binding BottomValue}" RenderTransformOrigin="0.5,0.5" Opacity="0.5">
<Line.RenderTransform>
<CompositeTransform ScaleY="1"/>
</Line.RenderTransform>
</Line>
</Grid>
</DataTemplate>
I have also tried using the following binding:
SourceObject="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}}"
But this displays a message: "Type is not supported in a silverlight project".

In Thomas Danemar's Blog there's an example of class to hold and switch VisualState. It's implemented by binding VisualState to attached property inside your ViewModel and them just set it to desire value. You can even bind it TwoWay mode.

Fixed:
<i:Interaction.Triggers>
<i:EventTrigger
SourceObject="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=ItemsControl}}"
EventName="MouseEnter">
<ei:GoToStateAction x:Name="Expand" StateName="Expanded"/>
</i:EventTrigger>
<i:EventTrigger
SourceObject="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=ItemsControl}}"
EventName="MouseLeave">
<ei:GoToStateAction x:Name="Collapse" StateName="Normal"/>
</i:EventTrigger>
</i:Interaction.Triggers>

Related

C# WPF Animate button with VisualStateManager triggered by an event

I am trying to animate a button triggered by an event (not on that button) with VisualStateManager. This is my code but it doesn't work. Please help find the issue or suggest a different method to animate the button.
In the xaml file I have the following
<Grid Grid.Row="1" Grid.Column="1" DataContext="{Binding ScanningViewModel}">
<Button Height="Auto" Margin="10" x:Name="ScanButton"
HorizontalAlignment="Stretch"
Style="{DynamicResource MaterialDesignOutlinedButton}"
Command="{Binding ScanDocumentCommand}">
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="SearchAdd" Width="30" Height="30"/>
<TextBlock Margin="5,0,0,0" Text="Scanează" VerticalAlignment="Center"/>
</StackPanel>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState Name="Default">
<Storyboard/>
</VisualState>
<VisualState x:Name="MyAnimation">
<Storyboard>
<ColorAnimation
To="Pink"
Storyboard.TargetName="Stop1" Storyboard.TargetProperty="Color"
Duration="0:0:2" />
<ColorAnimation
To="Maroon"
Storyboard.TargetName="Stop2" Storyboard.TargetProperty="Color"
Duration="0:0:2" />
<ColorAnimation
To="Red"
Storyboard.TargetName="Stop1" Storyboard.TargetProperty="Color"
BeginTime="0:0:2" Duration="0:0:2" AutoReverse="True" RepeatBehavior="Forever" />
<ColorAnimation
To="Blue"
Storyboard.TargetName="Stop2" Storyboard.TargetProperty="Color"
BeginTime="0:0:2" Duration="0:0:2" AutoReverse="True" RepeatBehavior="Forever" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Button>
</Grid>
In ScanningViewModel.cs:
class ScanningViewModel : BindableBase
{
public FrameworkElement ScanButton { get; private set; }
public ScanningViewModel()
{
ScanButton = new FrameworkElement();
}
private void SomeOtherCommand_Execute()
{
VisualStateManager.GoToElementState(this.ScanButton, "MyAnimation", false);
}
}
The goal is to add animation to a button triggered by an independent event (basically to guide the user through the app with visual clues).

How to assign the height property of the parent container to the to parameter of DoubleAnimation in xaml?

As the title says, I want to know how to assign the height(or ActualHeight) of the container to the To parameter/attribute of DoubleAnimation in xaml?
Here is my code:
<Canvas Name="animCanvas">
<Rectangle
Name="animSquare"
Canvas.Left="0"
Fill="AliceBlue"
Stroke="Bisque"
StrokeThickness="5"
Width="100"
Height="100"
>
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Window.Loaded">
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever">
<DoubleAnimation
Storyboard.TargetName="animSquare"
Storyboard.TargetProperty="(Canvas.Top)"
From="0"
To="500" <!-- I want to set it to the containers height -->
Duration="0:0:1"
/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
</Canvas>
You can apply Binding to the To property of double animation like this :
To="{Binding ElementName=animSquare, Path=Height}"
I think this would solve your issue.

EventTrigger trigger on condition

I have a event trigger.
I want it to be enabled only if condition occures
for example only if Viewmodel.IsEnabled propery is true AND EventTrigger RoutedEvent="Window.Loaded" occured
my problem is that MultiDataTrigger and MultiTriggers cannot combine Event trigger with Data Trigger.
<DataTemplate.Triggers>
<EventTrigger RoutedEvent="Window.Loaded" SourceName="NotificationWindow">
<BeginStoryboard x:Name="FadeInStoryBoard">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="NotificationWindow" From="0.01" To="1" Storyboard.TargetProperty="Opacity" Duration="0:0:2"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</DataTemplate.Triggers>
In other words I have a trigger to load a story board when window is loaded.
I want to be able to enable/disable this trigger per item.
You can use Blend Interactivity for WPF for accomplishing you task. I do not know your whole DataTemplate, so in my sample I will use an invented one.
Let's suppose I have a collection of Person objects and I want to start the DoubleAnimation just per Person whose property IsEnabled is true. I bind my collection to an ItemsControl and I create a "conditional" DataTemplate:
<ItemsControl ItemsSource="{Binding Path=People}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Name="Border" BorderBrush="Gray" BorderThickness="1" CornerRadius="4" Margin="2">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:Interaction.Behaviors>
<ei:ConditionBehavior>
<ei:ConditionalExpression>
<ei:ComparisonCondition LeftOperand="{Binding IsEnabled}" RightOperand="True"/>
</ei:ConditionalExpression>
</ei:ConditionBehavior>
</i:Interaction.Behaviors>
<ei:ControlStoryboardAction ControlStoryboardOption="Play">
<ei:ControlStoryboardAction.Storyboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="Border" From="0.01" To="1" Storyboard.TargetProperty="Opacity" Duration="0:0:2"/>
</Storyboard>
</ei:ControlStoryboardAction.Storyboard>
</ei:ControlStoryboardAction>
</i:EventTrigger>
</i:Interaction.Triggers>
<TextBlock Text="{Binding Path=Surname, Mode=OneWay}" Margin="2" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Of course you have to declare those namespaces in your XAML:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
The ConditionBehavior object evaluates the ComparisonCondition: if the former is true, it allows the ControlStoryboardAction to run.
I hope this small sample can give you an hint for solving your issue.

Windows 8.1 (also phone) c#/xaml elements not visible on this scrollviewer, why?

I'm having a problem with this code:
<Grid Canvas.ZIndex="1999" VerticalAlignment="Top" HorizontalAlignment="Left" x:Name="filter1" Margin="0,0,0,0" Background="#D8181818" Height="640" Width="400">
<Grid.Resources>
<Storyboard x:Name="FadeStoryboardbefore">
<DoubleAnimation From="0" To="1" Storyboard.TargetName="filter1"
Storyboard.TargetProperty="Opacity" Duration="0:0:0.4"
>
</DoubleAnimation>
</Storyboard>
<Storyboard x:Name="FadeStoryboardafter">
<DoubleAnimation From="1" To="0" Storyboard.TargetName="filter1"
Storyboard.TargetProperty="Opacity" Duration="0:0:0.4"
>
</DoubleAnimation>
</Storyboard>
</Grid.Resources>
<ScrollViewer Canvas.ZIndex="2000" VerticalAlignment="Top" HorizontalAlignment="Left" x:Name="scroll_filter" Height="641" Width="400">
<Grid Height="Auto" Canvas.ZIndex="2001" VerticalAlignment="Top" HorizontalAlignment="Left" x:Name="bottoni_filtro" >
</Grid>
</ScrollViewer>
</Grid>
The problem is that when I add elements with filter1.childrens.add in c#, it works. With the second grid, bottoni_filtro.childrends.add doesn't work. When I start the app, it doesn't show elements on bottoni_filtro..
How can I fix that??
P.S. properties like width are set through code (c#)

How to Zoom in and zoom out through animation

I have set of images which are placed in pivot control.i want zoom in the image to a specific point and then go back to its normal size through animation.how can i do that.Any help will be appreciated.
<StackPanel>
<StackPanel.Resources>
<Storyboard x:Name="story">
<DoubleAnimation Storyboard.TargetName="img"
Storyboard.TargetProperty="Height"
From="300"
To="600"
Duration="0:0:2">
</DoubleAnimation>
</Storyboard>
</StackPanel.Resources>
<Button Height="100" Width="100" Click="Button_Click"></Button>
<Image x:Name="img" Height="300" Width="200" Source="/Assets/10.png"></Image>
</StackPanel>
first tip:-
avoid writing Resources into specific control. Instead define all resources into <Page.Resources>
Now, if you want to animate some control, then you have to use Scaling property of an element.
Check below code. here, I'm pointing out how to zoom the image on any event handler.
XAML code of control
<Image x:Name="img" Source="/Assets/image1.png" RenderTransformOrigin="0.5 0.5" >
<Image.RenderTransform>
<CompositeTransform x:Name="compositeTransform" />
</Image.RenderTransform>
</Image>
Defining Storyborad
<phone:PhoneApplicationPage.Resources>
<Storyboard x:Name="story">
<DoubleAnimation Storyboard.TargetName="compositeTransform"
Storyboard.TargetProperty="ScaleX"
From="0" To="5"
Duration="0:0:2" />
<DoubleAnimation Storyboard.TargetName="compositeTransform"
Storyboard.TargetProperty="ScaleY"
From="0" To="5"
Duration="0:0:2" />
</Storyboard>
</phone:PhoneApplicationPage.Resources>
On any of the event handler
story.Begin();
Hope that helps..
If I understand your problem, then you should use the Storyboard, EventTrigger and ScaleX Instead of Height for Resize image.
Storyboarded and trigger animations
Here is one option for the loaded event.
<StackPanel>
<Button Height="100" Width="100" Click="Button_Click"></Button>
<Image x:Name="img" Height="300" Width="200" Source="/Assets/10.png">
<Image.RenderTransform>
<ScaleTransform x:Name="ImageScale" ScaleX="1" ScaleY="1"/>
</Image.RenderTransform>
<Image.Triggers>
<EventTrigger RoutedEvent="Image.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="ImageScale" Storyboard.TargetProperty="(ScaleTransform.ScaleX)" To="1.5" Duration="0:0:0.7" AutoReverse="True"/>
<DoubleAnimation Storyboard.TargetName="ImageScale" Storyboard.TargetProperty="(ScaleTransform.ScaleY)" To="1.5" Duration="0:0:0.7" AutoReverse="True"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Image.Triggers>
</Image>
</StackPanel>

Categories

Resources