Silverlight: How to Create a VisualState for a Templated / Custom Control - silverlight

I'm trying to create a templated/custom control in Silverlight.
The base control can be a System.Windows.Controls.Button. The button has the following visual states:
<vsm:VisualStateManager.VisualStateGroups>
<!--Define the states for the common states. The states in a
VisualStateGroup are mutually exclusive to each other.-->
<vsm:VisualStateGroup x:Name="CommonStates">
<!--Define the VisualStates in this VistualStateGroup.-->
<vsm:VisualState x:Name="Normal"/>
<vsm:VisualState x:Name="MouseOver" />
<vsm:VisualState x:Name="Pressed" />
<vsm:VisualState x:Name="Disabled" />
</vsm:VisualStateGroup>
<!--Define the states for the focus states. The states in a
VisualStateGroup are mutually exclusive to each other.-->
<vsm:VisualStateGroup x:Name="FocusStates">
<!--Define the VisualStates in this VistualStateGroup.-->
<vsm:VisualState x:Name="Focused" />
<vsm:VisualState x:Name="Unfocused" />
</vsm:VisualStateGroup>
</vsm:VisualStateManager.VisualStateGroups>
My custom control requires another state, MouseButtonUp (MouseButtonDown can be represented by the predefined Pressed state). Here, the MouseButtonUp state will be interpreted as a MouseOver state by default, but I want the MouseButtonUp state
behaves differently from a MouseOver state.
How can I add this MouseButtonUp visual state? (MouseButtonUp states only exists after the user releases the mouse and before the user starts moving.
BTW: Should I use Custom Control or User Control? I've been very confused about these two. It seems both of them would work in lots of cases.
Thanks a lot.
UPDATE: Once we added this MouseButtonUp state, I could do the visual transitions like:
<vsm:VisualTransition From="Pressed" To="MouseButtonUp" GeneratedDuration="0:0:5" />
Or:
<vsm:VisualTransition From="MouseButtonUp" To="MouseOver" GeneratedDuration="0:0:5" />

The below line creates a Visual-State for you in your xaml.
<vsm:VisualState x:Name="MouseButtonUp">
...Your code for animation
</vsm:VisualState>
That's not all. It is useless unless you force your execution control to go to this Visual-State. Well, how do you do that? Here's how.
VisualStateManager.GoToState(this, "MouseButtonUp", true);
The above code will execute any animation that you might have defined in your 'MouseButtonUp' VisualState definition in your xaml. Call the above code statement wherever you feel your mouse had a MousebuttonUp state.

Related

Why is the ColorAnimation for Checked state not persisting color after the MouseOver state is triggered?

I encountered an issue with a ControlTemplate for ToggleButton I created.
When the button is Checked, a ColorAnimation is triggered and the control's background changes color. However, if the user enters the MouseOver state, another animation is triggered that affects the button's background as well.
When the mouse is no longer in the MouseOver state, the control does not return to the color it should be while it is in the Checked state. I'm not sure why this does not persist when the MouseOver state is triggered.
The VisualStateManager portion of my ControlTemplate looks sorta like this:
<VisualStateManger.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"></VisualState>
<VisualState x:Name="MouseOver">
<Storyboard>
<ColorAnimation Storyboard.TargetName="BackgroundBorder"
Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
To="Gold" Duration="0:0:0.3" />
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="CheckedStates">
<VisualState x:Name="Checked">
<Storyboard>
<ColorAnimation Storyboard.TargetName="BackgroundBorder"
Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
To="PaleGoldenrod" Duration="0:0:0.3" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
My workaround for the issue I was having involved creating a Grid that enclosed the Border.
For the CommonStates I made animation changes to the Border.Background and for the CheckedStates I made animation changes to the Grid.Background.
It achieves the visual effect I was looking for.

Add Visual State to nested controls with WPF 4

I'd like to know if is it possible to apply a Visual State (in WPF 4) to nested controls. I've got a stack panel that contains some elements I'd like to change according to variation state.
<StackPanel x:Name="panPremioRaggiunto">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="StatiComuni">
<VisualState Name="PremioNonRaggiunto" />
<VisualState Name="PremioRaggiunto">
<Storyboard>
<ColorAnimation Storyboard.TargetName="lblPremioRaggiunto" Storyboard.TargetProperty="Foreground" To="Green" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<TextBlock x:Name="lblPremioRaggiunto">TEXT</TextBlock>
</StackPanel>
When I try to change the state of entire stack panel with this code
VisualStateManager.GoToState(panPremioRaggiunto, "PremioRaggiunto", False)
nothing happens: nested textblock named lblPremioRaggiunto don't change his color according. Can I apply a visual state in this manner?
Thanks,
Danilo.
Yes, VisualStateManager can change the state of any control. You must have some other issue with your code.
Fixed issue: visual states are defined outside a control template so I have to use VisualStateManager.GoToElementState instead of GoToState.

VisualStateManager does nothing (silverlight)

I am building a custom control using studio 2010 and silverlight 4.
I am trying to use the visual state manager.
With the following xml:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:SilverView">
<Style TargetType="controls:ScaleImage">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:ScaleImage">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualStateGroup.Transitions>
<VisualTransition To="MouseOver"
GeneratedDuration="0:0:.5"/>
<VisualTransition To="Normal"
GeneratedDuration="0:0:.5"/>
</VisualStateGroup.Transitions>
<VisualState x:Name="Normal">
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="img"
Storyboard.TargetProperty="Width"
From="50" To="100"/>
</Storyboard>
</VisualState>
<VisualState x:Name="MouseOver">
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="img"
Storyboard.TargetProperty="Width"
From="50" To="100"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Image Name="img" Width="50">
<Image.RenderTransform>
<ScaleTransform x:Name="scale"/>
</Image.RenderTransform>
</Image>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Nothing happens when I mouse over the image.
How do I get the image to enlarge when the mouse is over it?
Thanks
The VisualStateManager.VisualStateGroups attached property defines the set of visual states however the names of the groups and the names of the states are just names, they do not actually enable the functionality they describe automatically.
It's up to code in your control to decide when it is in a specific state and then inform the VisualStateManager of that choice. You do that with code like this:-
VisualStateManager.GotoState(this, "MouseOver", true);
Typically you would collect information like whether the mouse is over the control via the various control events and have a central UpdateVisualState function that sets all the appropriate states.
In the XAML above you are only defining state groups and states with names like "MouseOver". You are not actually causing the state to change, as they are apparently not connected to any events.
If you are not already, try using GoToState behaviours to trigger the state changes of your control.
Do you have any more code or XML that triggers a state change?

Animating same property from mutually exclusive VisualStateGroups

My question is simply: is it even possible?
Suppose I want to style a ListBoxItem such that it has a black foreground by default, blue when selected, and red when the mouse is over it. I ended up with something like this:
<!-- assume the default foreground color is black -->
<ControlTemplate TargetType="ListBoxItem">
<Grid Background="{TemplateBinding Background}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver">
<Storyboard>
<ColorAnimation Duration="0:0:0.2" To="Red" Storyboard.TargetName="contentControl" Storyboard.TargetProperty="(Control.Foreground).(SolidColorBrush.Color)"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="SelectionStates">
<VisualState x:Name="Unselected"/>
<VisualState x:Name="Selected">
<Storyboard>
<ColorAnimation Duration="0:0:0.2" To="Blue" Storyboard.TargetName="contentControl" Storyboard.TargetProperty="(Control.Foreground).(SolidColorBrush.Color)"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentControl x:Name="contentControl" Foreground="{TemplateBinding Foreground}" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}"/>
</Grid>
</ControlTemplate>
The problem is that the ListBoxItem class has correctly placed selection states in their own visual state group, separate to common states such as mouse over. That means that a ListBoxItem can be in both the selected and mouse over state.
If the ListBoxItem is selected and correctly displayed in blue, mousing over it will revert it to black because it transitions back to the normal state.
Is there any way for me to handle this without resorting to subclassing ListBoxItem and adding my own custom states? Everything I've read suggests that it is not possible, but it seems ridiculously limiting to me. What am I missing?
You basically asking for Foreground to be black AND blue at the same time. Now that is just impossible. This conflict could be resolved if individual states had precedence, like MouseOver > Selected > Normal > Unselected. But it would introduce unnecessary complication to already complicated visual state manager. Typically this situation is resolved by adding new element and animating that element's properties in one of the conflicting state groups.
Custom styled listbox - how can I keep the style for a selected item?

VisualState inheritance in Silverlight

I'm writing a control that inherits from a RadioButton and doesn't do anything more spectacular than display an image and hide the default circle.
One thing I haven't been able to find out about is if I have to re-implement all the VisualStates again in my ControlTemplate, or can I simply put them as an empty element and they're inherited?
My XAML is below, the original RadioButton is on MSDN.
<Style TargetType="local:ImageRadioButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:ImageRadioButton">
<Grid>
<vsm:VisualStateManager.VisualStateGroups>
<vsm:VisualStateGroup x:Name="CommonStates">
<vsm:VisualState x:Name="Normal"/>
<vsm:VisualState x:Name="MouseOver">
<Storyboard/> <!-- mouseover -->
</vsm:VisualState>
<vsm:VisualState x:Name="Pressed" />
<vsm:VisualState x:Name="Disabled"/>
<!-- TODO -->
</vsm:VisualStateGroup>
<vsm:VisualStateGroup x:Name="CheckStates">
<vsm:VisualState x:Name="Checked">
<Storyboard/>
<!-- checked -->
</vsm:VisualState>
<vsm:VisualState x:Name="Unchecked"/>
</vsm:VisualStateGroup>
<vsm:VisualStateGroup x:Name="FocusStates">
<vsm:VisualState x:Name="Focused" />
<vsm:VisualState x:Name="Unfocused" />
</vsm:VisualStateGroup>
<vsm:VisualStateGroup x:Name="ValidationStates">
<vsm:VisualState x:Name="Valid"/>
<vsm:VisualState x:Name="InvalidUnfocused" />
<vsm:VisualState x:Name="InvalidFocused" />
</vsm:VisualStateGroup>
</vsm:VisualStateManager.VisualStateGroups>
<ContentPresenter/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
It's all or nothing. If you replace the template, you must provide all of it, including the visual states. Blend makes it easy to edit a copy of the default template.
You don't have to reimplement the visual states but they won't be "inherited" either. If you specify a new DefaultStyleKey for your new control you get nothing from the original style.
If you want your new control to modify its appearance to represent its current state such as whether it has the focus or whether its selected you will need to include a the appropriate set of VisualStateGroups. Then include in the VisualState elements the appropriate animations to change your new version of the button's UI.

Resources