Xamarin ImageButton states - xaml

I am trying to create an ImageButton that uses a different image for the normal (unpressed) state and the state where the user is actively pressing the button. I found an article that uses a VisualStateGroup to change the appearance of the button based on the state:
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/imagebutton#imagebutton-visual-states
I am able to successfully use this to change the image. However, if I set a different image for the 'Pressed' state, when I click on the button, it does change to the new image as expected, but when I release the button it's visual state continues to display the pressed image and does not return to the 'Normal' state until I click somewhere else in the window. The XAML I am using to set the states is as follows:
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<VisualState.Setters>
<Setter Property="Scale"
Value="1" />
<Setter Property="Source" Value="Assets/changeuser.png" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Pressed">
<VisualState.Setters>
<Setter Property="Scale"
Value="0.8" />
<Setter Property="Source" Value="Assets/changeuser_highlight.png" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
Does anyone know how to make this work? Is there a different state than 'Pressed' that I should be using. Is there some other way to do this?
Thanks.

You can use Released to return the initial image.
For me it worked.
<VisualState x:Name="Released">
<VisualState.Setters>
<Setter Property="Scale"
Value="1" />
<Setter Property="Source"
Value="Assets/changeuser.png" />
</VisualState.Setters>
</VisualState>

Related

How to change color or frame border when control inside it is focused?

I want frame which decorates a control inside it to change border color if the control and frame is pressed/tapped on all platforms: UWP, IOS, Android. If I subscribe Frame to "Focused" event it works only on UWP, not on Android and IOS. Gesture recognizers does not have "untapped" event as pointed here. Visual state groups did not work on mobile platforms too. So it looks like I am left with an option to create renderers (something similar to this article), that I would like to avoid that or use a third party library like "xamarin community toolkit" but I did not manage to make it work.
<Grid VerticalOptions="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Frame Focused="frame_Focused" Unfocused="frame_Unfocused">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<VisualState.Setters>
<Setter Property="BorderColor" Value="Black" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Focused">
<VisualState.Setters>
<Setter Property="BorderColor" Value="Green" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Selected">
<VisualState.Setters>
<Setter Property="BorderColor" Value="Green" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="BorderColor" Value="Black" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentPresenter>
</ContentPresenter>
</Frame>
<StackLayout>
<Label />
</StackLayout>
</Grid>

How to set the color for RatingControl stars in UWP?

I have added a RatingControl in my UWP application. How can I set the color for filled and empty stars? Here is the code that I have added:
<RatingControl x:Name="MyRating" Value="3.5" Width="300" Height="200" />
If you check the default style of the RatingControl, you can find the following VisualStateGroup:
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Target="ForegroundContentPresenter.Foreground"
Value="{ThemeResource RatingControlDisabledSelectedForeground}"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Placeholder">
<VisualState.Setters>
<Setter Target="ForegroundContentPresenter.Foreground"
Value="{ThemeResource RatingControlPlaceholderForeground}"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="PointerOverPlaceholder">
<VisualState.Setters>
<Setter Target="ForegroundContentPresenter.Foreground"
Value="{ThemeResource RatingControlPointerOverPlaceholderForeground}"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="PointerOverUnselected">
<VisualState.Setters>
<Setter Target="ForegroundContentPresenter.Foreground"
Value="{ThemeResource RatingControlPointerOverUnselectedForeground}"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Set">
<VisualState.Setters>
<Setter Target="ForegroundContentPresenter.Foreground"
Value="{ThemeResource RatingControlSelectedForeground}"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="PointerOverSet">
<VisualState.Setters>
<Setter Target="ForegroundContentPresenter.Foreground"
Value="{ThemeResource RatingControlSelectedForeground}"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
As you can see, the colors are based on resources like RatingControlSelectedForeground, RatingControlPointerOverUnselectedForeground etc.
You can either provide your custom overrides for these as separate resources, or you can edit the style of the control.
Note - empty stars color
Although it is not part of the template, you can customize empty stars color by modifying the RatingControlUnselectedForeground resource.
Resource overrides
You can override the resources on the level of a single rating control, any parent or on the application level.
Override on RatingControl level will apply only to this single RatingControl:
<RatingControl x:Name="MyRating" Value="3.5" Width="300" Height="200" >
<RatingControl.Resources>
<SolidColorBrush x:Key="RatingControlSelectedForeground" Color="Red" />
<SolidColorBrush Color="Blue" x:Key="RatingControlUnselectedForeground" />
</RatingControl.Resources>
</RatingControl>
You can do the override on any parent, like on the page:
<Page.Resources>
<SolidColorBrush x:Key="RatingControlSelectedForeground" Color="Red" />
<SolidColorBrush Color="Blue" x:Key="RatingControlUnselectedForeground" />
</Page.Resources>
Or finally you can put it on application level if you add it in <Application.Resources> in App.xaml. Then it will apply to any RatingControl in the app.
Alternative solution - Custom style
If you want more control and even better customization, you can directly edit the default RatingControl style and its template.
Right-click the RatingControl in Designer (or in Document Outline window) and select Edit style, then Edit a copy, set a custom name and location where the style should be placed and confirm with OK. This will create a copy of the default template of the control, where you can edit the VisualState Setter values to match your desired color scheme.
Also note, that you will still have to provide a custom resource for the empty stars (RatingControlUnselectedForeground).

UWP Change Grid.HorizontalAlignment to stretch/left using VisualStateManager

I'm having trouble getting the visual state manager to correctly change the HorizontalAlignment/VerticleAlignment of the main grid of my application.
For screen sizes that would be used on phone, I want the HorizontalAlignment/ VerticleAlignment to stretch, with Height/Width set to Auto.
For Desktop it should be Left/Top with Height/Width set to a specific size. For whatever reason when I try to mess with the alignment it acts quirky and does not do what I want
Here is an example of what I have tried:
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="PhoneView">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0"/>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="Grid.HorizontalAlignment" Value="Stretch"/>
<Setter Target="Grid.VerticleAlignment" Value="Stretch"/>
<Setter Target="Grid.Height" Value="Auto"/>
<Setter Target="Grid.Width" Value="Auto"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="DesktopSmall">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="1920"/>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="Grid.HorizontalAlignment" Value="Left"/>
<Setter Target="Grid.VerticleAlignment" Value="Top"/>
<Setter Target="Grid.Height" Value="1280"/>
<Setter Target="Grid.Width" Value="1920"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
Another issue is when I try to set the max resolution to 1920x1280, if I manually set the height/width of the Grid, the Windows TaskBar (the OS taskbar, not the applications app bar/command bar) cuts off part of my application. I don't want to set the alignments to stretch, because I'm trying to limit the real time stretching of the view.
Thanks! Let me know if more details are needed.
It should write like this -
<Setter Target="Container.(Grid.HorizontalAlignment)" Value="Left" />
<Setter Target="Container.(Grid.VerticalAlignment)" Value="Top" />
I have some tips here for creating visual state setters without writing a single line of code.

How to set ScrollViewer Width bound to a page element ActualWidth in AdaptiveTrigger UWP

I have a ScrollViewer which Width property binds to another page element. I want to apply this only on certain AdaptiveTrigger MinWindowWidth
<ScrollViewer x:Name="scrollViewerEditor" Width="{Binding ElementName=stackPanelSearch, Path=ActualWidth}">
...
</ScrollViewer>
I want to place the Width binding in AdaptiveTrigger like the following. What's the proper way to do it?
<VisualState x:Name="VisualStateWide">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="{StaticResource WideMinWidth}" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="scrollViewerEditor.(FrameworkElement.Width)" Value="Binding ElementName=stackPanelSearch, Path=ActualWidth}" />
If you don't mind to do this work in the code behind, there is an easy way to solve this problem, using the VisualStateGroup.CurrentStateChanged event for example like this:
<VisualStateManager.VisualStateGroups>
<VisualStateGroup CurrentStateChanged="VisualStateGroup_CurrentStateChanged">
<VisualState x:Name="VisualStateNarrow">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="scrollViewerEditor.Style" Value="{StaticResource ScrollViewerStyle1}" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="VisualStateWide">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="720" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="scrollViewerEditor.Style" Value="{StaticResource ScrollViewerStyle2}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
In code behind:
private void VisualStateGroup_CurrentStateChanged(object sender, VisualStateChangedEventArgs e)
{
var state = e.NewState;
if (state.Name == "VisualStateNarrow")
{
scrollViewerEditor.Width = 300;
}
else
{
if (tb.ActualWidth != 0)
scrollViewerEditor.Width = tb.ActualWidth;
else
scrollViewerEditor.Width = 600;
}
}
It is not allowed to use data binding in Setter, usually the right way to solve this problem is to create a custom AttachedProperty for your ScrollViewer and bind this attached Property to the WidthProperty, but the support for custom attached property inside the VisualState is not quite ideal, I will keep researching on this and see if it works with custom AttachedProperty.

MouseOver ContentPresenter XAML Style

I need to change colour of mouseover/pointover of content presenter but my style does not work.
Someone help me?
Thanks
<Style x:Key="Test" TargetType="ContentPresenter">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="PointerOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentPresenter">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ColorTest}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentPresenter">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ColorTest}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Style>
I do not believe this is possible. Visual States are published by specific controls, and ContentPresenter represents something that could be any kind of control or arbitrarily complex tree of elements.
You can use the subsections in Control Styles and Templates to get an idea of which Visual States are valid for each control, but as you see these are specific for the control in question, and not every state is always supported.
Your style could be amended to use Triggers, such as <Trigger Property="IsMouseOver" Value="True">, but then you could only provide setters for properties of ContentPresenter, and Foreground is not one of those.
Update
However, as TextBlock.Foreground is an attached property, you can make the Trigger solution work in your specific example, and including complex control contents. Please note this won't work for all properties however.
<Grid>
<Grid.Resources>
<ControlTemplate TargetType="Button" x:Key="ButtonControlTemplate">
<ContentPresenter />
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="TextBlock.Foreground" Value="Red" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="TextBlock.Foreground" Value="Blue" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Grid.Resources>
<Button Template="{StaticResource ButtonControlTemplate}" Content="Test" />
</Grid>