How can I change visual of flyoutitem in xamarin shell on select? - xaml

I want to highlight current page by changing background color of a flyoutitem, but I also need to change text color inside a frame. My Template
<Grid ...>
<Frame CornerRadius="10"
Padding="0"
BackgroundColor="White"
Grid.Column="1">
<Label Text="{Binding Title}"
Margin="50,0,0,0"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
VerticalTextAlignment="Center"
TextColor="Black"
Grid.Column="1"/>
</Frame>
</Grid>
I need to change Frame color to darker green and label text color to white whenever I navigate to a certain page. I've tried visual state manager, but it doesn't work even just with background-only. I found a solution to attach trigger on a Frame, but nothing happens
protected override void Invoke(Frame sender)
{
sender.BackgroundColor = Color.FromHex("#229904");
(sender.Content as Label).TextColor = Color.White;
}

Firstly, you need to add a custom style in your resources:
<Style x:Key="FloutItemStyle" TargetType="Grid">
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Selected">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="xxx"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
Then consume it in your Shell:
<Shell.ItemTemplate>
<DataTemplate >
<Grid Style="{StaticResource FloutItemStyle}">
//...
</Grid>
</DataTemplate>
</Shell.ItemTemplate>

Related

VisualStateManager not working inside Button.ContentTemplate UWP

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.

CollectionView GridItemsLayout Change BackgroundColor of nested Item (Button)

I have a problem concerning a CollectionView with a GridItemsLayout. In my application, I have a StackLayout with a horizontal CollectionView(GridItemsLayout), which contains a Button bound to a certain category.
Whenever a button is clicked/tapped, it filters the ListView (of type Surgery) below based on the category. All of that is working fine, however, I would like to highlight the Category/Button by changing it BackgroundColor to see which category is currently used for filtering.
<CollectionView HeightRequest="70"
x:Name="categoryCollection"
ItemsSource="{Binding Categories}"
Margin="20,0,20,0"
SelectionMode="Single">
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Horizontal" Span="1" HorizontalItemSpacing="5"/>
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid Padding="0,5,0,5">
<Button x:Name="categoryButton" Text="{Binding Name}"
Command="{Binding Path=BindingContext.FilterCommand, Source={x:Reference Name=SurgeryListView}}"
CommandParameter="{Binding .}"
CornerRadius="15"
FontFamily="OpenSans" Margin="0,5,10,5"
Opacity="0.6" FontSize="16" TextTransform="None">
</Button>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<ListView x:Name="listView"
ItemsSource="{Binding Surgeries}"
RefreshCommand="{Binding LoadSurgeriesCommand}"
IsRefreshing="{Binding IsRefreshing}"
IsPullToRefreshEnabled="True"
RowHeight="70"
BackgroundColor="{StaticResource darkThemeBackground}"
ItemTemplate="{StaticResource surgeryDataTemplateSelector}">
<ListView.Behaviors>
<behaviors:EventToCommandBehavior
EventName="ItemTapped"
Command="{Binding SurgerySelectedCommand}"
EventArgsConverter="{StaticResource ItemTappedConverter}">
</behaviors:EventToCommandBehavior>
</ListView.Behaviors>
</ListView>
I tried to use VisualStates but that was not working, because tapping the button does not actually change the SelectedItem (one would need to click the surrounding/parent grid element for that). Moreover, the altered VisualState was only applied to the Grid's BackgroundColor, not to that of the actual button.
Question: How can I highlight the current Category/Button by changing its Background Color?
Since the selection is working good, remains only the ui/xaml part, if you look at the documentation about VisualStateManager, you can add the following style:
<CollectionView.Resources>
<Style TargetType="Grid">
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Selected">
<VisualState.Setters>
<Setter Property="BackgroundColor"
Value="LightSkyBlue" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
</CollectionView.Resources>

Segmented control Xamarin Forms customisation

I would like to create the segmented control in the image below.
What i currently have or attempted using is the library in the following link : https://github.com/1iveowl/Plugin.SegmentedControl
How ever as you can see the final result ends up being a horizontal segmented UI, which is what I do not want.
I have checked the documentation of the plugin to see if there is a way of changing the orientation and it seems that is the current limitation of the plugin
<control:SegmentedControl
x:Name="SegmentedGenderControl"
TintColor="#F2EBF9"
SelectedTextColor="#6F1AC1"
TextColor="Black"
DisabledColor="White"
BorderColor="#6F1AC1"
BorderWidth="1.0"
FontSize="Medium"
Margin="8,8,8,8">
<control:SegmentedControl.Children >
<control:SegmentedControlOption Text="Male"/>
<control:SegmentedControlOption Text="Female"/>
<control:SegmentedControlOption Text="Female"/>
</control:SegmentedControl.Children>
</control:SegmentedControl>
The second alternative that I have thought about is using a grid with 3 rows :
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
</Grid>
And then manually handle the selection based on the selection. Is there a simpler or plugin that is available to the public that is not the one above that I can use ?
So I finally managed to solve this problem as suggested by an external party through the use of a collection view
See the code that follows :
<CollectionView
HeightRequest="250"
x:Name="OptionsCollectionView"
ItemsSource="{Binding SelectionOptions}"
VerticalOptions="Start"
SelectionMode="Single">
<CollectionView.ItemTemplate>
<DataTemplate>
<yummy:PancakeView
x:Name="optionPancake"
Padding="20">
<yummy:PancakeView.Border>
<yummy:Border
Color="{StaticResource CollectionViewBorderColor}"
Thickness="2" />
</yummy:PancakeView.Border>
<StackLayout
Orientation="Horizontal">
<Label
x:Name="optionLabel"
Text="{Binding Option}"
FontSize="15"
FontFamily="EuclidCircularASemibold"
TextColor="{StaticResource SubHeadingColor}"
FontAttributes="Bold" />
</StackLayout>
</yummy:PancakeView>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
The code that follows is for styling the visual state group, when an item is selected:
<Style TargetType="yummy:PancakeView">
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup>
<VisualState Name="Normal"/>
<VisualState Name="Selected">
<VisualState.Setters>
<Setter
Property="BackgroundColor"
Value="{StaticResource FrameSelectedColor}"/>
<Setter
Property="yummy:PancakeView.Border"
Value="{yummy:BorderMarkup Color={StaticResource SelectedLabelColor}, Thickness='2'}"/>
<Setter TargetName="optionLabel"
Property="Label.TextColor"
Value="{StaticResource SelectedLabelColor}"/>
<Setter Property="CornerRadius"
Value="5"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>

Adaptive trigger doesn't work in a user control

I'm working on a UWP app. I'm already using adaptive triggers to adapt my xaml depending on the width of the window, and it works.. only in a Page.
Now I want to do the same for the xaml of a User Control, and it's not working .. Yet I put the VisualStateManager to root grid of the user control.
Is there a difference ?
Here's the code of my user control :
<UserControl
x:Class=.....>
<UserControl.Resources>
<ResourceDictionary>
<vm:ViewModelLocator x:Key="Locator"/>
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</ResourceDictionary>
</UserControl.Resources>
<UserControl.DataContext>
<Binding Path="MyUserControlVM" Source="{StaticResource Locator}"/>
</UserControl.DataContext>
<Grid x:Name="LayoutRoot" Background="white">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="Narrow">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="LastName.Foreground" Value="Red" />
<Setter Target="LastName.Fontsize" Value="10" />
<Setter Target="FirstName.Foreground" Value="Red"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Normal">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="600" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="LastName.Foreground" Value="Red" />
<Setter Target="LastName.Fontsize" Value="25" />
<Setter Target="FirstName.Foreground" Value="Red"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Wide">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="1000"/>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="LastName.Foreground" Value="Red" />
<Setter Target="FirstName.Foreground" Value="Red"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Listbox>
...
</ListBox>
<TextBlock x:Name="lblNoData" Grid.ColumnSpan="2" Text="No Data" Visibility="{Binding NoDataVisible, Converter={StaticResource BooleanToVisibilityConverter}}"/>
<ProgressRing x:Name="prLoading" Width="60" Height="60" Foreground="Blue" IsActive="{Binding InitializationNotifier.IsNotCompleted}" />
</Grid>
("LastName" and "FirstName" are textblocks in the DataTemplate of my listbox. I'm just trying to put my text in red in order to see when the triggers work)
And I call the user control in a simple page like this :
<Grid x:Name="LayoutRoot" Background="{StaticResource white}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Margin="0,0,0,10">
...
</StackPanel>
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="10,0">
<UC:MyUserControl/>
</Grid>
</Grid>
I don't understand why this code won't work. Thank you in advance for your help !
("LastName" and "FirstName" are textblocks in the DataTemplate of my listbox. I'm just trying to put my text in red in order to see when the triggers work)
You can refer to my another answer here, as I said in that answer, when the controls are placed in the DataTemplate, they becomes the visual structure of your data objects. I think there is no clean way to do this work only in the xaml code, Data Binding here is your friend.
From your code I can see that you want to change the Foreground (but all to Red?) and the FontSize of the TextBlocks inside of the DataTemplate depending on the window's size. So you can bind these two proprieties to the window's size, or you can use ItemsControl.ItemTemplateSelector to select different template when the window's size is changed.

How fadeout element in dataTemplate when mouse clicked its containter? My idea throw always exception

This code throw always exception. Is a way to get TextBox (x:Name = "MyBox") to hide (fadeout) when we click in its container (here ListViewItem)? What's wrong with my code?
<ListView x:Name="ListViewMy" IsItemClickEnabled="True" ItemsSource="{Binding list, Mode=TwoWay}" HorizontalAlignment="Left" Height="223" Margin="240,350,0,0" VerticalAlignment="Top" Width="582" >
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<Grid x:Name="GridMy">
<ContentPresenter x:Name="MyContentPresenter">
<ContentPresenter.ContentTemplate>
<DataTemplate x:Key="DataTemplate">
<Grid Background="#FF25C1D1" Margin="-30,0,0,0" x:Name="GridBlue">
<TextBox x:Name="MyBox" HorizontalAlignment="Left" Margin="135,0,0,0" TextWrapping="Wrap" Text="{Binding}" VerticalAlignment="Top" Width="352" Height="81" Background="{x:Null}" BorderBrush="#FF25C1D1" FontFamily="Segoe WP Semibold" FontSize="32" FontWeight="Bold" Foreground="White"/>
</Grid>
</DataTemplate>
</ContentPresenter.ContentTemplate>
</ContentPresenter>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver"/>
<VisualState x:Name="Pressed" >
<Storyboard>
<FadeOutThemeAnimation TargetName="MyBox" Duration="0:0:5"></FadeOutThemeAnimation>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
</ListView>
The problem with your code is that your animation, in the ControlTemplate for ListViewItem, should only target other elements within the ControlTemplate for ListViewItem. The DataTemplate won't work here because x:Name="MyBox" won't resolve. DataTemplates are reusable pieces of UI so Name expansion is skipped on them.
A good way to achieve your scenario is to target the animation to the ContentPresenter that is in fact part of your ControlTemplate, i.e.:
<FadeOutThemeAnimation TargetName="MyContentPresenter" Duration="0:0:5" />