I have a border element which contains a path element and when the border is tapped I want to change the fill property of the path to another color.
I have searched for an answer but everybody seems to use control templates and styles (generally for buttons) for that kind of behaviour. Is there a way to change the color without using control templates and styles?
My code so far is as follows,
<Border>
<Canvas Width="32" Height="32" Canvas.Left="0" Canvas.Top="0">
<Path x:Name="MyIcon" Width="30.5" Height="30.5"
Canvas.Left="0.75" Canvas.Top="0.750008"
Stretch="Fill" StrokeThickness="3"
StrokeLineJoin="Round" Stroke="{StaticResource PhoneForegroundBrush}"
Data="F1 M 15.9707,2.25001L 19.167,12.7919L 29.75,12.8197L 21.2578,19.1225L 24.4292,29.75L 16.0879,23.2829L 7.51911,29.6986L 10.831,19.2255L 2.25,12.7983L 12.7665,12.8177L 15.9707,2.25001 Z "/>
</Canvas>
<i:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Tapped">
<core:InvokeCommandAction Command="{Binding ChangeCommand}"/>
</core:EventTriggerBehavior>
</i:Interaction.Behaviors>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Pressed">
<Storyboard>
<ColorAnimation Storyboard.TargetName="MyIcon"
Storyboard.TargetProperty="(Path.Fill).(SolidColorBrush.Color)"
To="#FFFF0000" Duration="0:0:0.5" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Border>
Thanks for your helps in advance.
In your 'Pressed' VisualState, you are animating the Color property of the SolidColorBrush which is assigned to the Fill property of the 'MyIcon' Path. However, you haven't assigned a SolidColorBrush object to the Fill property of 'MyIcon'. Hence if you try to go to 'Pressed' state, it won't be able to find the SolidColorBrush reference and will throw exception. The simplest solution would be to assign Fill="Transparent" in your 'MyIcon' Path.
Also I'm assuming you have put your Border inside a Control, and you are calling VisualStateManager.GoToState in appropriate places.
Related
I have been struggling with this for a few days now unable to find solution after lot of effort. This is the code where I'm facing the issue. SO I have a ItemsControl where each element is Button and each Button has an Image and TextBlock. On hovering over Button I could see the Background of Button being changed to Red as expected. But I'm unable to change the Foreground of TextBlock to Green. Any help in this regard is highly appreciated
<ControlTemplate x:Key="ButtonTemplate" TargetType="ContentControl">
<Grid Background="Transparent" x:Name="Mini">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="PointerOver">
<VisualState.Setters>
<Setter Target="Mini.Background" Value="Red" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentPresenter x:Name="MiniContent" />
</Grid>
</ControlTemplate>
<DataTemplate x:Key="ListItemTemplate" x:DataType="local:DataModel">
<Button
Template="{StaticResource ButtonTemplate}">
<Button.ContentTemplate>
<DataTemplate x:DataType="local:DataModel">
<UserControl>
<Grid >
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="PointerOver">
<VisualState.Setters>
<Setter Target="Value.Foreground" Value="Green" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<TextBlock x:Name="Value"
Foreground="Yellow"
Text="{x:Bind DisplayName, Mode=OneWay}"/>
<Image
Width="16"
Source="{x:Bind ImageBitmap, Mode=OneWay}"/>
</Grid>
</UserControl>
</DataTemplate>
</Button.ContentTemplate>
</Button>
</DataTemplate>
<ItemsControl
ItemTemplate="{StaticResource ListItemTemplate}"
ItemsSource="{x:Bind DataModelVector, Mode=OneWay}" />
You need something to actually transition between the VisualStates, the PointerOver state isn't triggered automatically. The easiest way would be to factor the content of your DataTemplate into its own file (UserControl with markup and code-behind) and handle pointer events in code to transition between visual states using VisualStateManager.GoToState.
You could also create a reusable "StateTrigger" for transitioning any control into a "PointerOver" visual state, but often some code-behind is simpler.
I added a ProgressBar to my application and I wanted it to have a transparent background, so I did it like this:
<ProgressBar IsIndeterminate="True" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<ProgressBar.Background>
<SolidColorBrush Color="Black" Opacity="0.5" />
</ProgressBar.Background>
</ProgressBar>
And in the Preview Window everything looks fine, however, when I run my app, the Background is simply not there. The solution I found to this is to put the ProgressBar in a Grid and set the Background property in the Grid, but since the Preview shows it right, and the property is there, shouldn't it work?
UPDATE:
Based on #Chris W. suggestion, I tried to override the default style of the ProgressBar element, like so:
<ProgressBar IsIndeterminate="True" Background="#FF000000" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="50">
<ProgressBar.Style>
<Style TargetType="ProgressBar">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ProgressBar">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Indeterminate">
<Storyboard RepeatBehavior="Forever">
<DoubleAnimation Storyboard.TargetName="DeterminateRoot"
Storyboard.TargetProperty="Opacity"
To="0.5"
Duration="0" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ProgressBar.Style>
</ProgressBar>
But still, no juice.
Need to get rid of two (2) StoryBoard Animations
Document Outline > Right Click Progress Bar > Edit Template -> Edit A Copy
<!--
<FadeOutThemeAnimation Storyboard.TargetName="DeterminateRoot"/>
<DoubleAnimation Duration="0" To="0" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="DeterminateRoot"/>
-->
And as #ricochete suggested if using Opacity = 1 change up the Z-Order of DeterminateRoot to be on top of the EllipseGrid
<Border x:Name="DeterminateRoot" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" MinHeight="{TemplateBinding MinHeight}">
<Rectangle x:Name="ProgressBarIndicator" Fill="{TemplateBinding Foreground}" HorizontalAlignment="Left" Margin="{TemplateBinding Padding}"/>
</Border>
<Grid x:Name="EllipseGrid" Opacity="0">
<!-- ... more XAML Style -->
<Grid Background="#FFFF0000">
<ProgressBar IsIndeterminate="True" Style="{StaticResource ProgressBarStyle1}" Height="50" >
<ProgressBar.Background>
<SolidColorBrush Color="Black" Opacity="0.5"/>
</ProgressBar.Background>
</ProgressBar>
</Grid>
If you go look at the default template you'll see at the bottom of the template the Background only has a TemplateBinding in one spot for x:Name="DeterminateRoot" so that's the only place you'd see your color set from the Background property.
Then if you climb up through the Storyboard for the Indeterminate State you'll find;
<DoubleAnimation Storyboard.TargetName="DeterminateRoot"
Storyboard.TargetProperty="Opacity"
To="0"
Duration="0" />
...and you've got your ProgressBar set to IsIndeterminate="True" so you're setting the one place that accepts the Background property to a zero opacity explicitly.
So you could go pull that animation out of the Storyboard for that state, or put in your own new object to set your thing, or just do the workaround you mentioned by just throwing it in a Border or a Grid or something and doing it that way amongst other possibilities.
You might also try (once you've fixed your opacity setting issue from the storyboard) just flipping your SolidColorBrush with Opacity into just pure hex with the Alpha Channel set as 50% opacity equivalent. Making it just;
<ProgressBar Background="#80000000" IsIndeterminate="True" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"/>
Anyhow, hope this helps, Cheers!
I'm a bit a of XAML noob so this is probably something very simple I'm missing...
In a Windows Phone 8 app I have a map Pushpin I'm trying to animate with an effect very much like ripples in a pond. I have a large container eclipse and a child eclipse that will expand from 0 width/height to 30 width/height (the same size as the parent eclipse).
I'm doing this to visually indicate to the user that their location is actively being tracked, or just been picked up.
Unfortunately I've not managed to get my animation working.
<ControlTemplate x:Key="UserLocationPushpinControlTemplate" TargetType="m:Pushpin">
<Grid x:Name="ContentGrid" Width="34" Height="34">
<Grid.Resources>
<Storyboard x:Name="Storyboard1">
<DoubleAnimation
Storyboard.TargetName="Animated"
Storyboard.TargetProperty="Width"
From="0" To="30" Duration="0:0:1" AutoReverse="False"
RepeatBehavior="Forever" />
<DoubleAnimation
Storyboard.TargetName="Animated"
Storyboard.TargetProperty="Height"
From="0" To="30" Duration="0:0:1" AutoReverse="False"
RepeatBehavior="Forever" />
</Storyboard>
</Grid.Resources>
<Grid MinHeight="30" MinWidth="30">
<Ellipse x:Name="Parent" Margin="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Width="30"
Height="30"
Stroke="White"
StrokeThickness="3"
Fill="{StaticResource PrimaryColorBrush}"/>
<Ellipse x:Name="Animated" Margin="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Width="0"
Height="0"
Stroke="White"
StrokeThickness="2"/>
</Grid>
</Grid>
</ControlTemplate>
Just in case it's relevant - this ControlTemplate is within a UserControl housing my Windows Phone 7 / Bing map control (the Windows Phone 8 map control is lacking some functionality I require).
Any help would be greatly appreciated. Thank you.
Update
I can add an animation, but am not sure how to apply the Storyboard via code. Ideally I'd like to apply the Storyboard by code as I'd like to define a couple for different circumstances.
Here's the updated XAML:
<ControlTemplate x:Key="UserLocationPushpinControlTemplate" TargetType="m:Pushpin">
<Grid x:Name="ContentGrid" Width="34" Height="34">
<Grid.Resources>
<Storyboard x:Name="LocateStoryboard">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Width)" Storyboard.TargetName="AnimatedEllipse" RepeatBehavior="Forever">
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="30"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Height)" Storyboard.TargetName="AnimatedEllipse" RepeatBehavior="Forever">
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="30"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Grid.Resources>
<Grid MinHeight="30" MinWidth="30">
<Ellipse x:Name="ParentEllipse" Margin="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Width="30"
Height="30"
Stroke="White"
StrokeThickness="3"
Fill="{StaticResource PrimaryColorBrush}"/>
<Ellipse x:Name="AnimatedEllipse" Margin="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Width="0"
Height="0"
Stroke="White"
StrokeThickness="2"/>
</Grid>
</Grid>
</ControlTemplate>
As a side note: I had issues creating the animation before as I was unaware that Blend does not record changes if you're tweaking the XAML code directly. You need to seek out the various property controls and set everything there.
This is the code I was trying to use to start the animation:
Pushpin pushpin = new Pushpin();
pushpin.Tag = PIN_TAG;
pushpin.Location = ViewModel.Location;
pushpin.Template = (ControlTemplate)Resources["UserLocationPushpinControlTemplate"];
pushpin.PositionOrigin = PositionOrigin.Center;
MapBase.Children.Add(pushpin);
Storyboard animation = (Storyboard)pushpin.Resources["LocateStoryboard"];
animation.Begin();
The storyboard variable is null. It seems I need to delve in to the ControlTemplate structure to dig down to the "LocateStoryboard" resource. Any ideas how to do this?
Not sure if it can help, but take a look at the article by Igor Ralic here... He has a detailed example on how to add a fade in animation on pushpins.
I have a ListView and modified it's DataTemplate with 2 TextBlocks.
The first TextBlock contains a Heading, the second a Sub-Heading.
I style the 2 TextBlocks with different colours.
Here's an example of the ListViewItem in Normal view.
Here's an example of the ListViewItem in Selected view.
So my question is how do I change the Foreground colours of the TextBlocks in Selected views? Hoping to do this in the xaml. I've tried setting different brushes, which work for items that haven't explicitly been styled.
Not sure how to handle this scenario.
You can use visual states.
<ListView>
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock x:Name="txtOne" Grid.Row="0" Foreground="Green"/>
<TextBlock x:Name="txtTwo" Grid.Row="1" Foreground="Gray"/>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="SelectionStates">
<VisualState x:Name="Unselected"/>
<VisualState x:Name="Selected">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="txtOne" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="Red"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="txtTwo" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="Yellow"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
You don't need to play with the visual state.
In your ResourceDictionary, set a value for these brushes "ListBoxItemSelectedBackgroundThemeBrush", "ListBoxItemSelectedPointerOverBackgroundThemeBrush", "ListBoxFocusBackgroundThemeBrush". It will override the default brushes of your application.
Example:
<!-- Overrides default ListBox brushes -->
<SolidColorBrush x:Key="ListBoxItemSelectedBackgroundThemeBrush" Color="{StaticResource GreenColor}" />
<SolidColorBrush x:Key="ListBoxItemSelectedPointerOverBackgroundThemeBrush" Color="{StaticResource LightGreenColor}" />
<SolidColorBrush x:Key="ListBoxFocusBackgroundThemeBrush" Color="Transparent" />
Here is a usefull link when developping in WinRt, which references the brushes name, for the default controls of winRt.
WinRt default brushes names and values
Thanks to some researching and thinking out of the box, found a suitable solution that works:
Metro App ListView SelectedItem Selected VisualState
I can see this being handy for a couple of other scenarios as well.
Well, as the title suggests:
I have a storyboard and I want it's animation to start without the intervention of code.
The reason for this requirement is that I am targeting Silverlight Embedded and I am too lazy right now to recompile my application as well. And, coming to think of it, it will be easier to change the animation only in the future.
Does XAML have a property to make the storyboard run as soon as the xaml loads?
You can use the Loaded event to start your storyboard
See MSDN for an example:
Storyboard (Silverlight)
Picked the example from MSDN:
<Canvas
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Rectangle
x:Name="MyAnimatedRectangle"
Width="100"
Height="100"
Fill="Blue">
<Rectangle.Triggers>
<!-- Animates the rectangle's opacity.
This is the important part, the EventTrigger which will start our animation -->
<EventTrigger RoutedEvent="Rectangle.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="MyAnimatedRectangle"
Storyboard.TargetProperty="Opacity"
From="1.0" To="0.0" Duration="0:0:5" AutoReverse="True" RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
</Canvas>
The object Rectangle has properties. In the Triggers property we defined an EventTrigger which will fire when this event will occur. We choose the Rectangle.Loaded event, which means it will fire when loaded ;).
We add a BeginStoryboard property to begin our storyboard, and add a Storyboard. This animation will use a DoubleAnimation on the Opacity property, which means that in a duration of 5 seconds, the opacity will gradually fade to zero, and back (AutoReverse property) and it will do this Forever (the RepeatBehaviour property).
<UserControl x:Class="SOSMVVM.AniM11"
xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:d='http://schemas.microsoft.com/expression/blend/2008'
xmlns:mc='http://schemas.openxmlformats.org/markup-compatibility/2006'
mc:Ignorable='d'
d:DesignWidth='640'
d:DesignHeight='480'>
<StackPanel Margin="5">
<Rectangle Name="rect3" Fill="Blue" Margin="2" Width="20"
Height="20" HorizontalAlignment="Left" />
<Button Margin="2,20,0,0" HorizontalAlignment="Left"
Content="Start Animations" Width="100">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="rect3" Storyboard.TargetProperty="Width"
From="20" To="400" Duration="0:0:10" SpeedRatio="0.5" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Button.Triggers>
</Button>
</StackPanel>
</UserControl>