I can not dynamically GoToState in WPF. What is wrong? - wpf

I created the simplest WPF application with Blend for Visual Studio.
I created the simplest visual state groups for a single textbox:
My problem is that I can not dynamically GoToState using this code:
public MainWindow()
{
InitializeComponent();
textBox.MouseEnter+=new System.Windows.Input.MouseEventHandler(textBox_MouseEnter);
}
private void textBox_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
{
// This will be an empty list
var states = VisualStateManager.GetVisualStateGroups(textBox);
// This will be false
bool thisReturnsFalse = VisualStateManager.GoToState(textBox, "VisualState1", true);
}
This is my XAML:
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="VisualStateGroup">
<VisualState x:Name="VisualState">
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)" Storyboard.TargetName="textBox">
<EasingColorKeyFrame KeyTime="0" Value="#FFE40404"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="VisualState1">
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)" Storyboard.TargetName="textBox">
<EasingColorKeyFrame KeyTime="0" Value="#FF23FF00"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<TextBox x:Name="textBox" HorizontalAlignment="Left" Height="23" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="120">
<i:Interaction.Triggers>
<i:EventTrigger EventName="KeyUp">
<ei:GoToStateAction StateName="VisualState1"/>
</i:EventTrigger>
<i:EventTrigger EventName="KeyDown">
<ei:GoToStateAction StateName="VisualState"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>

Your VisualStateGroups are not defined in the TextBox, so the call
var states = VisualStateManager.GetVisualStateGroups(textBox);
won't return what you expect. If you want to get the defined states, pass the FrameworkElement that contains the VisualStateGroups (I'm assuming the Window here):
var states = VisualStateManager.GetVisualStateGroups(this);
The same should probably be used for the call to VisualStateManager.GoToState:
bool shouldReturnTrue = VisualStateManager.GoToState(this, "VisualState1", true);

Related

How to change visual state from scroll viewer in wpf xaml

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);
}

Animating visibility AND opacity with wpf VisualStateManager

I am trying to animate a control so that it's visibility is set to visible then animating the opacity from 0 to 1
However nothing happens, then after 1 second the control is show with an opacity of 1... I cannot see what i am doing wrong
This is the code i have tried
<Grid x:Name="layout_root" Margin="10">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="Filtering">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0:0:1">
<VisualTransition.GeneratedEasingFunction>
<ElasticEase EasingMode="EaseInOut"/>
</VisualTransition.GeneratedEasingFunction>
</VisualTransition>
</VisualStateGroup.Transitions>
<VisualState x:Name="Disabled"/>
<VisualState x:Name="Enabled">
<Storyboard>
<ObjectAnimationUsingKeyFrames BeginTime="0:0:0" Duration="0:0:0" Storyboard.TargetName="filter_control" Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{x:Static Visibility.Visible}"/>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimation Storyboard.TargetName="filter_control" Storyboard.TargetProperty="(UIElement.Opacity)" To="1"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBox x:Name="filter_control" Margin="0,0,0,10" Text="Filtering" Visibility="Collapsed" Opacity="0"/>
<ListView Grid.Row="1" ItemsSource="{Binding Posts}">
<ListView.View>
<GridView>
<GridViewColumn Width="100" Header="Date" DisplayMemberBinding="{Binding Date, StringFormat={}{0:dd/MM/yyyy}}"/>
<GridViewColumn Width="100" Header="Text" DisplayMemberBinding="{Binding Text}"/>
<GridViewColumn Width="100" Header="Value" DisplayMemberBinding="{Binding Value, StringFormat=F2}"/>
</GridView>
</ListView.View>
</ListView>
<Button Grid.Row="1" Content="v" FontFamily="Marlett" FontSize="14" VerticalAlignment="Top" HorizontalAlignment="Left" Click="ShowFilterClick"/>
</Grid>
As to the question of what you're doing wrong or why you see the behavior that you see: the storyboard for the Enabled state is the storyboard that the VSM uses while the VSGroup is in that state. You specify a transition storyboard for the group, though, and the VSM applies that when transitioning between states. So, when you put the VSGroup into the Enabled state, the VSM first plays the transition storyboard then uses the steady-state storyboard that you specify for the Enabled state. The transition storyboard is 1 sec, and that's why you're seeing the 1 sec delay and then the pop.
Something like the following is probably what you want. Note that the transition storyboard does the action/animation that you want, and the state storyboards just state the final values at which the animated properties should be held. Also, I apply the easing function to the double animation rather than to the entire VisualTransition -- it doesn't make sense to try to interpolate Visibility with an easing function.
<VisualStateGroup x:Name="Filtering">
<VisualStateGroup.Transitions>
<VisualTransition From="Disabled" To="Enabled" GeneratedDuration="0:0:1">
<Storyboard>
<ObjectAnimationUsingKeyFrames Duration="0:0:0" Storyboard.TargetName="filter_control" Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{x:Static Visibility.Visible}"/>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimation Duration="0:0:1" Storyboard.TargetName="filter_control" Storyboard.TargetProperty="(UIElement.Opacity)" To="1">
<DoubleAnimation.EasingFunction>
<ElasticEase EasingMode="EaseInOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</VisualTransition>
<!-- you could also have a transition from Enabled to Disabled -->
</VisualStateGroup.Transitions>
<VisualState x:Name="Disabled">
<Storyboard>
<ObjectAnimationUsingKeyFrames Duration="0:0:0" Storyboard.TargetName="filter_control" Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{x:Static Visibility.Collapsed}"/>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimation Duration="0:0:0" Storyboard.TargetName="filter_control" Storyboard.TargetProperty="(UIElement.Opacity)" To="0"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Enabled">
<Storyboard>
<ObjectAnimationUsingKeyFrames Duration="0:0:0" Storyboard.TargetName="filter_control" Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{x:Static Visibility.Visible}"/>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimation Duration="0:0:0" Storyboard.TargetName="filter_control" Storyboard.TargetProperty="(UIElement.Opacity)" To="1"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
The Visibility enum is not an inherently animatable property. Generally only numeric properties are truly animatable, since WPF can fill in the spaces between keyframes. For example, it knows that an opacity halfway between the value of 0 and 1 is 0.5. It knows every possible value based on the current time.
If you animate from Visibility.Collapsed to Visibility.Visible over 1 second, it has no idea what to do at the 0.5 second mark or any other point in between. It only knows you're changing an enum from 1 value to another. If your transition time is 1 second, it waits till that second is up and then changes the value, so you never get to see the opacity animation happening.
You can try using FluidLayout. You enable it like so:
<VisualStateGroup x:Name="Filtering" ei:ExtendedVisualStateManager.UseFluidLayout="True">
You can also enable it using a toggle in the Blend UI.
FluidLayout animates layout changes for you. Collapsing or expanding an element affects the layout, so it can automatically animate those layout changes.

Accessing a style's storyboard of a button

My problem is that I have a Button and I want to access the Storyboard, which is a part of the assigned style.
<Button x:Name="pictureFolderButton" Content="Pictures" Style="{StaticResource ImageTileButtonStyle}" Click="pictureFolderButton_Click" />
The style is very comprehensive so I'll post only a part of it:
<Style x:Key="ImageTileButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<ControlTemplate.Resources>
<Storyboard x:Key="OnLoaded1"/>
</ControlTemplate.Resources>
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
...
</VisualStateGroup>
<VisualStateGroup x:Name="AnimationStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0:0:1">
<VisualTransition.GeneratedEasingFunction>
<CircleEase EasingMode="EaseOut"/>
</VisualTransition.GeneratedEasingFunction>
</VisualTransition>
</VisualStateGroup.Transitions>
<VisualState x:Name="ExpandedFull">
<Storyboard x:Name="expandStoryBoard" >
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Height)" Storyboard.TargetName="border1">
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="130"/>
<EasingDoubleKeyFrame KeyTime="0:0:3" Value="130"/>
<EasingDoubleKeyFrame KeyTime="0:0:4" Value="47"/>
<EasingDoubleKeyFrame KeyTime="0:0:8" Value="47"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentPresenter RecognizesAccessKey="True" VerticalAlignment="Stretch" Margin="0,47,0,0" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I just want to get notified when the "ExpandedFull" animation has ended. Therefore, I thought I have to get the "expandStoryBoard" programmatically and add a Completed event handler.
The only thing I got working is to access the button's style at runtime:
Style style = pictureFolderButton.FindResource("ImageTileButtonStyle") as Style;
How do I have to proceed?
Thank you very much!
In theory you should be able to go down the visual and logical tree of your button to get to the storyboard, but that is rather tedious, if you name the Grid in the template "grid", something like the following might work:
Grid grid = pictureFolderButton.FindName("grid") as Grid;
IList groups = VisualStateManager.GetVisualStateGroups(grid);
VisualStateGroup targetGroup = null;
foreach (var group in groups)
{
if (group is VisualStateGroup && (group as VisualStateGroup).Name == "AnimationStates")
{
targetGroup = group as VisualStateGroup;
break;
}
}
if (targetGroup != null)
{
IList states = targetGroup.States;
VisualState targetState = null;
foreach (var state in states)
{
if (state is VisualState && (state as VisualState).Name == "ExpandedFull")
{
targetState = state as VisualState;
break;
}
}
if (targetState != null)
{
targetState.Storyboard.Completed += new EventHandler(Expansion_Completed);
}
else throw new Exception("VisualState not found.");
}
else throw new Exception("VisualStateGroup not found.");
Another way that comes to mind is extracting your storyboard to a resource, but i am not sure if this will have any side effects, i.e.:
<ControlTemplate.Resources>
...
<Storyboard x:Key="expandStoryBoard" x:Name="expandStoryBoard">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Height)" Storyboard.TargetName="border1">
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="130"/>
<EasingDoubleKeyFrame KeyTime="0:0:3" Value="130"/>
<EasingDoubleKeyFrame KeyTime="0:0:4" Value="47"/>
<EasingDoubleKeyFrame KeyTime="0:0:8" Value="47"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</ControlTemplate.Resources>
...
<VisualState x:Name="ExpandedFull" Storyboard="{StaticResource expandStoryBoard}"/>
Then you should be able to use FindResource on the button to get the storyboard.
Hopefully some of that works or at least helps a bit.
Just try with this with StoryBoard name "OnLoaded1":
<Button Height="75" Width="120" Style="{StaticResource ImageTileButtonStyle}" Click="Button_Click" >Hello</Button>
private void Button_Click(object sender, RoutedEventArgs e)
{
Button btn=(Button)sender;
Storyboard stb = btn.TryFindResource("OnLoaded1") as Storyboard;
}
If you add the Storyboard to the resources, you can set the event handler for the Timeline.Completed event in the XAML file and implement the handler in the corresponding class.
Define the Storyboard in the Resources section of your control like this:
<UserControl.Resources>
<Storyboard x:Key="expandStoryBoard" Completed="OnExpandCompleted"> ... </Storyboard>
...
</UserControl.Resources>
Reference the Storyboard as a static resource:
<VisualState x:Name="ExpandedFull" Storyboard="{StaticResource expandStoryBoard}" />
Implement the Completed event handler in the corresponding class:
void OnExpandCompleted(object sender, EventArgs e)
{
...
}

How to choose which storyboard to run on click of a Radio Button and Next button?

I am looking for the code which will allow running a certain storyboard based on which radio button is selected. The scenario is to select a radio button first and then click ‘Next” button to run storyboard. For example, if the first radio button is selected and “Next’ button clicked, then the first storyboard should be played. Any ideas are highly appreciated. Thank you in advance.
XAML:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ClickCount.MainWindow"
x:Name="Window"
Title="MainWindow"
Width="640" Height="480">
<Window.Resources>
<Storyboard x:Key="Storyboard1">
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)" Storyboard.TargetName="border">
<EasingColorKeyFrame KeyTime="0" Value="Red"/>
<EasingColorKeyFrame KeyTime="0:0:0.5" Value="#FF00FFED"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="Storyboard2">
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)" Storyboard.TargetName="border">
<EasingColorKeyFrame KeyTime="0" Value="Red"/>
<EasingColorKeyFrame KeyTime="0:0:0.5" Value="#FF5AFF00"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="Storyboard3">
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)" Storyboard.TargetName="border">
<EasingColorKeyFrame KeyTime="0" Value="Red"/>
<EasingColorKeyFrame KeyTime="0:0:0.5" Value="#FFFFF500"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</Window.Resources>
<Grid x:Name="LayoutRoot">
<RadioButton GroupName="Os" Content="Storyboard 1" IsChecked="True" FontSize="16" Margin="10,10,10,0"/>
<RadioButton GroupName="1" Content="Storyboard 2" Margin="10,31,0,0" FontSize="16" />
<RadioButton GroupName="1" Content="Storyboard 3" Margin="10,50,0,0" FontSize="16" />
<Button x:Name="Next" Content="Next>" Width="75.06" Height="23" IsDefault="True"
Click="ClickNextButton" VerticalAlignment="Top" />
<Border x:Name="border" BorderBrush="Black" BorderThickness="1"
HorizontalAlignment="Center" Height="100" VerticalAlignment="Center"
Width="100" Background="Red"/>
</Grid>
C#:
public partial class MainWindow : Window
public MainWindow()
{
this.InitializeComponent();
}
private void ClickNextButton(object sender, System.Windows.RoutedEventArgs e)
{
Counter += 1;
if (Counter == 1) { }
if (Counter == 2) { }
}
}
If you put your Storyboards inside a VisualStateManager you can use the GoToState method:
VisualStateManager.GoToState(this, "Storyboard1", useTransitions);
Your XAML will become something like this:
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="MyStates">
<VisualState Name="Storyboard1">
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)" Storyboard.TargetName="border">
<EasingColorKeyFrame KeyTime="0" Value="Red"/>
<EasingColorKeyFrame KeyTime="0:0:0.5" Value="#FF00FFED"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
....
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
Note that the name has moved from the Storyboard to a VisualState.
Your code behind is then something like this:
if (Counter == 1)
{
VisualStateManager.GoToState(this, "Storyboard1", useTransitions);
}
if (Counter == 2)
{
VisualStateManager.GoToState(this, "Storyboard2", useTransitions);
}
(Though this is from memory so there might be syntax errors)

DataTrigger is not using GoToStateAction in Silverlight

The follow XAML represents an object I am trying to build in Expression Blend. I am having trouble with the DataTrigger in the StackPanel - the application does not go to Empty when the trigger matches the data. Further explanation is after this code:
<DataTemplate x:Key="SampleTemplate">
<StackPanel x:Name="SampleStack" Style="{StaticResource DefaultSampleStyle}" Width="64" Height="60">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="VisualStateGroup">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0">
<Storyboard>
<ColorAnimation Duration="0" To="#FFDFE04B" Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)" Storyboard.TargetName="SampleStack" d:IsOptimized="True"/>
</Storyboard>
</VisualTransition>
</VisualStateGroup.Transitions>
<VisualState x:Name="Empty">
<Storyboard>
<ColorAnimation Duration="0" To="#FF4B6FE0" Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)" Storyboard.TargetName="SampleStack" d:IsOptimized="True"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<VisualStateManager.CustomVisualStateManager>
<ei:ExtendedVisualStateManager/>
</VisualStateManager.CustomVisualStateManager>
<i:Interaction.Triggers>
<ei:DataTrigger Binding="{Binding IsActive}" Value="False">
<ei:GoToStateAction StateName="Empty" UseTransitions="False"/>
</ei:DataTrigger>
</i:Interaction.Triggers>
<TextBlock x:Name="StartOn" Text="{Binding StartOn, StringFormat=hh:mm}"/><TextBlock x:Name="textBlock" Text="-" />
<TextBlock x:Name="EndOn" Text="{Binding EndOn, StringFormat=hh:mm}"/>
</StackPanel>
</DataTemplate>
If I use an EventTrigger with a Loaded value, the Empty state is correctly applied based on the IsActive binding.
If I use the existing DataTrigger and change a Property on the Stackpanel, such as Height, based on the binding of IsActive this also works.
Am I doing something fundamentally wrong in the XAML? Do you need a more complete example of the XAML to understand the issue?
do you need the GoToStateAction?
I guess, the problem is the Binding "at startup". I added a dispatcher and threw the NotifyPropertyChanged again after one second. Then it works. Propably you can workaround it like this. You wait till the control is loaded and then throw the PropertyChanged again. This is not a nice way and similar to your idea (If I use an EventTrigger with a Loaded value,...)
I would recommend you to use a DataStateBehaviour. If you hav a boolean to decide in which satte you have to go, this is great. It is a behaviour where you can bind the condition to a property and then set a true and a false state.
It would look like this (I did a few adjustments just for testing at my computer):
<DataTemplate x:Key="SampleTemplate">
<StackPanel x:Name="SampleStack" Width="64" Height="60" Background="White">
<i:Interaction.Behaviors>
<ei:DataStateBehavior Binding="{Binding IsChecked}" Value="True" TrueState="Empty" FalseState="Base"/>
</i:Interaction.Behaviors>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="VisualStateGroup">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0"/>
</VisualStateGroup.Transitions>
<VisualState x:Name="Empty">
<Storyboard>
<ColorAnimation Duration="0" To="Red" Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)" Storyboard.TargetName="SampleStack" d:IsOptimized="True"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Base"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<VisualStateManager.CustomVisualStateManager>
<ei:ExtendedVisualStateManager/>
</VisualStateManager.CustomVisualStateManager>
<TextBlock x:Name="StartOn" Text="Test"/>
</StackPanel>
</DataTemplate>
As you can see I added a second state to the VisualStateGroup (There is now empty and base). I would recommend this not only because the DataStateBehaviour needs at least two states in one group. If you have only one state, you have no chance to change the state of this group back to normal, e.g.
I hope this answer helps you.
BR,
TJ

Resources