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.
Related
I would like to gray out the image only instead of the entire button. The reason being is that the image on the button is partly transparent, so when the entire button is grayed out it just looks odd.
In the below image you can see the entire button is grayed out instead of just the visible image
related... sorta: Disabling Button with custom Content in Silverlight?
You will need to override the ControlTemplate for your button. How to do that?
Create a new style
In that style define the ControlTemplate
ControlTemplate find the definition for the visual state named "Disabled"
Change that so that it changes the image only, without greying out the entire button.
These are the relevant parts of your controls template
<ControlTemplate TargetType="Button">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<!-- other states go here -->
<VisualState x:Name="Disabled">
<Storyboard>
<DoubleAnimation Duration="0"
Storyboard.TargetName="DisabledImage"
Storyboard.TargetProperty="Opacity" To="1"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<!-- rest of the template goes here -->
</ControlTemplate>
The DisabledImage you reference in the visual state manager then would need to cover the image displayed on an enabled button entirely, so that when disabled, only the greyed out image is visible to the user.
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.
So, hello everybody.
I've made two test programs for creating a own control. One in Silverlight, one in WPF. I created a kind of RangeSlider. This Slider has of course two Orientations, Horizontal and Vertical.
First I used two different techniques to create my RangeSlider. In WPF I used Triggers, in Silverlight (u know there arent Triggers) I changed the Visibility of the Horizontal and Vertical Template in CodeBehind.This runs.
Now:
I'm trying to use one technique for both, Silverlight and WPF. Therefore I use VisualStateManager.
I've a Template defining two Sliders (one for left value, the other for right value). Simplified on the important values it looks like that:
...
<ControlTemplate>
<Grid x:Name="PART_Content">
<!-- VSM: See following code sequence -->
<Grid x:Name="PART_HorizontalTemplate">
<Slider x:Name="PART_HorizontalSliderLeft"
Template="{StaticResource HorizontalSliderTemplate}"
Orientation="{TemplateBinding Orientation}" />
...
</Grid>
<Grid x:Name="PART_VerticalTemplate">
...
</Grid>
</Grid>
</ControlTemplate>
Additionally there is the VSM to switch between Horizontal and Vertical look:
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="Vertical">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PART_HorizontalTemplate"
Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame Value="{x:Static Visibility.Collapsed}" KeyTime="0"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Horizontal" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
This still runs.
As you see in the code sequence, the visual of Slider is defined by a Template, here HorizontalSliderTemplate (I reduce code again):
<ControlTemplate x:Key="HorizontalSliderTemplate" TargetType="{x:Type Slider}">
<Border x:Name="Border" ...>
<!-- VSM here. Like above. -->
<Grid x:Name="Grid">
<Rectangle x:Name="PART_SelectionRange"/>
<Track x:Name="PART_Track">
...
</Track>
</Grid>
</Border>
</ControlTemplate>
Actually there is also a VerticalSliderTemplate.
But I want to combine both ControlTemplate in one Template and use VSM. Here we come to my problem:
I do not get running the VSM in the 'inner' ControlTemplate. It's nearly the same code as the running VSM-Part, just the TargetName is changed. I do not know how to debug what's running on GoToState, but I believe the VSM in the template is never found and from there never execute.
I can imagine that there's just a little detail missing, but I 'cant see the wood for the trees'. Maybe there is an important thing what I do not know about Templates or about VSM, and I'm off the track.
Or do I have to trigger the 'inner' VSM from outside, or there's a possibility to access onto Elements from 'outside VSM'?
Or is there no access on VSM in 'inner' Templates?
I hope I could explain my problem good enough and there is someone, who knows a solution or maybe a keyword what I can look for. Just entering keywords VSM, ControlTemplate, Storyboard, etc. in google gives no helping hand.
Thanks in advance.
I don't think you can have multiple VSMs inside one ControlTemplate.
Why don't you just use one VSM to toggle both.
<ControlTemplate>
<Grid x:Name="PART_Content">
<Grid x:Name="PART_HorizontalTemplate">
...
</Grid>
<Grid x:Name="PART_VerticalTemplate">
...
</Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="Vertical">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PART_HorizontalTemplate"
Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame Value="{x:Static Visibility.Collapsed}" KeyTime="0"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Horizontal">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PART_VerticalTemplate"
Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame Value="{x:Static Visibility.Collapsed}" KeyTime="0"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
So, I got a solution on it.
In the 'inner' template I added to the element I want to toggle a DataTrigger. This DataTrigger is bound to the PART_HorizontalTemplate Visibility and contains a Storyboard that do the action I need.
Maybe it's not really the kind of solution I was looking for, because it stretches the code a lot and makes it therefore looking more complex. But - and thats most important - it runs well.
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?
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?