I'm developing a xaml/c# metro style app in windows 8. I'd like to emulate the Microsoft Calendar app comboBox Style (In the event details page). I mean, having that behavior of coloured box and border after selection. How can I do it using visual states?
There is no standard control for this, you have to create your own / extend the standard combobox
Something like this should work:
<Combobox.Template>
<ControlTemplate>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Unfocused"/> <!--leave the unfocused state empty if the control already looks "unfocused" -->
<VisualState x:Name="Focused">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="background" Storyboard.TargetProperty="Opacity" To="0.2" Duration="0"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="background" Background="Red" Opacity="0" />
<!--other stuff-->
</ControlTemplate>
</Combobox.Template>
The Combobox control automatically switches it's built-in states according to mouse/keyboard input like focused, pressed, mouse over etc. By switching a state the storyboard that was defined for the current state will be reversed and the storyboard that you have defined for the new state will be applied. You can review the available states here: http://msdn.microsoft.com/en-us/library/ms752094.aspx
(Using code-behind you can also implement your own states based on events and such, but this should be rarely needed.)
Related
This question is a follow-up to my previous question as well as this related question about how VisualStates work in WPF.
Currently, my understanding is that animating the same property within different VisualStateGroups can cause problems (see the linked questions).
To resolve these problems, it requires loop-holes to be taken advantage of. (Perhaps loop-hole isn't the correct term, but it appears that the solution isn't what the WPF designers intended.)
I'm wondering what is the correct way to animate the same property in multiple VisualStateGroups without causing adverse side-effects. If it is not possible, what is the correct route for achieving the same visual behavior for a control?
I was able to find some related documentation at MSDN:
The control is always in exactly one state per group. For example, a Button can have focus even when the mouse pointer is not over it, so a Button in the Focused state can be in the MouseOver, Pressed, or Normal state.
This leads me to a second question...
How can you provide a visual behavior that should only occur when two specific VisualStates are active?
Take for example a ToggleButton:
If the button is Checked, I would like to display Behavior 1.
If the button is Disabled, I would like to display Behavior 2.
Finally, if the button is Checked and Disabled, I would like to display Behavior 3.
In the above example, how would you go about rendering the third visual behavior?
For the first part of your question, you'll want each state to interact with a different individual object for each instead of hitting the same one with each VisualState, as example;
<VisualState x:Name="Disabled">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="DisabledState"
Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Checked">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="CheckedState"
Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<!-- Each state interacts with its own object in your ControlTemplate ideally -->
<Border x:Name="CheckedState" Visibility="Collapsed"
Background="Green"/>
<Border x:Name="DisabledState" Visibility="Collapsed"
Background="White" Opacity=".5"/>
Instead of one object sharing a property change like;
<VisualState x:Name="Disabled">
<Storyboard>
<ColorAnimation d:IsOptimized="True"
Duration="0"
Storyboard.TargetName="Background"
Storyboard.TargetProperty="(SolidColorBrush.Color)"
To="White" />
</Storyboard>
</VisualState>
<VisualState x:Name="Checked">
<Storyboard>
<ColorAnimation d:IsOptimized="True"
Duration="0"
Storyboard.TargetName="Background"
Storyboard.TargetProperty="(SolidColorBrush.Color)"
To="Green" />
</Storyboard>
</VisualState>
<Border x:Name="Background" Background="Blue"/>
As per your second question, A VisualState is going to act as a bool in that it either is, or it isn't in that state. To share a declaration of a state you'd have to add a little more finesse with a MultiTrigger or a converter somewhere or something.
Hope this helps. Cheers
EDIT ADDITION:
So you also have available to you VisualTransition you could employ like;
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualStateGroup.Transitions>
<VisualTransition From="Normal"
GeneratedDuration="0:0:0.2"
To="Checked">
<VisualTransition.GeneratedEasingFunction>
<ExponentialEase EasingMode="EaseIn" Exponent="7" />
</VisualTransition.GeneratedEasingFunction>
</VisualTransition>
<VisualTransition From="Checked"
GeneratedDuration="0:0:0.2"
To="Normal">
<VisualTransition.GeneratedEasingFunction>
<CircleEase EasingMode="EaseIn" />
</VisualTransition.GeneratedEasingFunction>
</VisualTransition>
</VisualStateGroup.Transitions>
<VisualState x:Name="Normal" />
<!-- etc, etc, etc -->
So you can play with your different eases, your generated duration times, etc.
I'm trying to achieve a nice animation while switching tabs in the TabControl.
At this point, my style animation xaml looks like this:
<EventTrigger RoutedEvent="SelectionChanged">
<BeginStoryboard x:Name="selectionChangedBeginStoryboard">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="borderScale"
Storyboard.TargetProperty="ScaleX">
<DoubleKeyFrameCollection>
<EasingDoubleKeyFrame Value="0" KeyTime="0:0:0.2"/>
<EasingDoubleKeyFrame Value="1" KeyTime="0:0:0.4"/>
</DoubleKeyFrameCollection>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
What I want to achieve is a rotating effect on the tab transmition. So it would look like the screen is turning away, and returns with the new tab page.
The problem is that when I switch to another tab,
The content is switched right away, and the animation is just rotating the new tab page.
Any Ideas, please? :)
Thank you!
I would recommend that you use a transition library, such as 'Transitionals'. You can download this library from the Transitionals page on CodePlex.
The reason why I say this is because in order to do what you want to do, you will need to capture the Visual of the old TabItem before you switch tabs, animate that instead of the TabItem and then remove that and restore the actual controls.
However, the aforementioned library already does this and provides a number of different transitions for you to use. You can get help with using the library by downloading the 'TransitionalsHelp_1_0.zip' file from the following link:
http://transitionals.codeplex.com/releases/view/12954
Instead of using third party programs, i recommend Blend.
Open your Solution there and work with the VisualStateManager. I did a transitional effect from Unselected to Selected in less than 30 seconds. It was simple (Opacity change), but Blend is very user friendly and you can integrate with Visual Studio natively.
Here's what it generated to be (not what you are asking):
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid x:Name="templateRoot" SnapsToDevicePixels="true">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates"/>
<VisualStateGroup x:Name="SelectionStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0:0:0.3"/>
</VisualStateGroup.Transitions>
<VisualState x:Name="Unselected">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="templateRoot">
<EasingDoubleKeyFrame KeyTime="0" Value="0.8"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Selected"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
Good luck.
I want to write a custom control and want it to have different Padding if the page is Portrait or Snapped. I noticed that pages inherit from LayoutAwarePage which creates support for the following view states:
FullScreenLandscape
Filled
FullScreenPortrait
Snapped
Do I need to add similar code to my new control (It inherits from Control). If not, why does LayoutAwarePage have to do this? Also, Can I just stick the following VisualStateManager into the ControlTemplate of my control and get it to respect the page layout or is this too easy.
<VisualStateGroup x:Name="ApplicationViewStates">
<VisualState x:Name="FullScreenLandscape"/>
<VisualState x:Name="Filled"/>
<VisualState x:Name="FullScreenPortrait">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Padding">
<DiscreteObjectKeyFrame KeyTime="0" Value="1,2,3,4"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Snapped">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Padding">
<DiscreteObjectKeyFrame KeyTime="0" Value="5,6,7,8"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
EDIT : It looks like controls do not support these states by default and they have to be added. It also appears that ButtonBase does support these states because it uses them in its style.
If you look on the default "details" page within the windows 8 projects you'll find that it expects you to subscribe to the events on the page. You'll find the following within the flipview's ItemTemplate:
<FlipView.ItemTemplate>
<UserControl Loaded="StartLayoutUpdates" Unloaded="StopLayoutUpdates">
<ScrollViewer x:Name="scrollViewer" Style="{StaticResource HorizontalScrollViewerStyle}"
<!-- "Child Controls Here" -->
<VisualStateManager.VisualStateGroups>
<!-- "Visual states manuiplating the child controls above" -->
</VisualStateManager.VisualStateGroups>
</ScrollViewer>
</UserControl>
</FlipView.ItemTemplate>
What I do with my user controls is to have them inherit LayoutAwarePage. Next place the content in a grid and move the VisualStateGroup inside this grid. Hope this helps, it works for me.
A designer has given me a Silverlight child window that was designed in Expression Blend and uses a Visual State Manager to toggle between two modes. In short, there's an expand button on the child window that, when clicked, slides down another Grid control with detailed information.
This interaction happens entirely in the XAML. However, I need to enhance it so that if certain conditions are met - say if the user chooses some option, "Always show expanded details" - that when the child window is opened the expanded visual state will be in effect.
I thought this was as simple as calling:
VisualStateManager.GoToState(this, "VisualStateDetails", false);
But that is not working. What am I missing? (I apologize if I'm overlooking something obvious, I'm relatively new to Silverlight and not at all familiar with the Visual State Manager.)
Here is the XAML that has a bit removed for brevity.
<Grid x:Name="LayoutRoot" Margin="2" DataContext="{Binding ProjectNode, Mode=TwoWay}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="EditorWindowStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0:0:0.5">
<VisualTransition.GeneratedEasingFunction>
<CubicEase EasingMode="EaseInOut"/>
</VisualTransition.GeneratedEasingFunction>
</VisualTransition>
</VisualStateGroup.Transitions>
<VisualState x:Name="VisualStateNormal"/>
<VisualState x:Name="VisualStateDetails">
<Storyboard>
<!-- snip -->
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<VisualStateManager.CustomVisualStateManager>
<ei:ExtendedVisualStateManager/>
</VisualStateManager.CustomVisualStateManager>
<!-- snip -->
And here is a button that, when clicked, expands the details:
<Button ...>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:GoToStateAction StateName="VisualStateDetails"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
Thanks!
The issue appears to be that the Visual State Manager does not play nice in a Child window.
This blog entry described the problem and provides two workarounds. The first one (using the ExtendedVisualStateManager class) did the trick for me:
Using Visual State Manager with Silverlight Toolkit’s Child Windows control
I have created a HeaderTemplate for my accordions where I want to display a text block on one side of the header and a hyperlink on the right side. The display is working correctly, but the click event is not called when the user clicks, I'm guessing b/c the header itself is trapping the click for expand/contract.
<layoutToolkit:Accordion>
<layoutToolkit:AccordionItem IsSelected="True">
<layoutToolkit:AccordionItem.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Height="20">
<TextBlock Margin="0,0,700,0">Cancel Postcards</TextBlock>
<HyperlinkButton Content="Next Call" Foreground="Blue" Click="NextCancel_Click" />
</StackPanel>
</DataTemplate>
</layoutToolkit:AccordionItem.HeaderTemplate>
..... more code ....
Is there a way to get the hyperlink to respond to events without practically creating a new control?
Update: It looks like the header sets all child controls to disabled when expanded which is why the link doesnt work. It will work when you collapse that accordionitem. So, the question now is, how do i prevent the hyperlink from being disabled?
Hey Charlie, I just happened to answer this same question for Epic720. You have to change the Locked VisualState.
Interactive items in Silverlight Accordion Header
Here is the LockedStates VisualStateGroup of the AccordionItem which you should alter. I can post the whole style if you need, though it's quite verbose.
<VisualStateGroup x:Name="LockedStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0"/>
</VisualStateGroup.Transitions>
<VisualState x:Name="Locked">
<Storyboard>
<!--
<ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetProperty="IsEnabled" Storyboard.TargetName="ExpanderButton">
<DiscreteObjectKeyFrame KeyTime="0" Value="False"/>
</ObjectAnimationUsingKeyFrames>
-->
</Storyboard>
</VisualState>
<VisualState x:Name="Unlocked">
<Storyboard>
<ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetProperty="IsEnabled" Storyboard.TargetName="ExpanderButton">
<DiscreteObjectKeyFrame KeyTime="0" Value="True"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>