I am trying to customize the selection color of a cell in a CollectionView but no matter how I try it, it's always an ugly grey.
I want my item template to have rounded corners, but when I select an item the I see ugly square grey corners behind it, as in this image:
Here's my current XAML:
<?xml version="1.0" encoding="UTF-8"?>
<ContentView
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Tests.CollectionViewTest">
<ContentView.Content>
<CollectionView
x:Name="collectionView"
Margin="15,0"
ItemSizingStrategy="MeasureFirstItem"
Grid.Row="1"
Grid.RowSpan="2"
VerticalScrollBarVisibility="Never"
BackgroundColor="Transparent"
SelectionMode="Multiple"
HorizontalOptions="Center"
VerticalOptions="Center"
>
<CollectionView.ItemsLayout>
<GridItemsLayout
Orientation="Vertical"
HorizontalItemSpacing="1"
VerticalItemSpacing="1"
Span="3" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Frame
x:Name="selectionFrame"
CornerRadius="18"
BackgroundColor="Transparent"
Padding="0"
HasShadow="False"
IsClippedToBounds="True"
BorderColor="Transparent">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup
Name="CommonStates">
<VisualState
Name="Normal" />
<VisualState
Name="Focused">
<VisualState.Setters>
<Setter
Property="BackgroundColor"
Value="Transparent" />
</VisualState.Setters>
</VisualState>
<VisualState
Name="Selected">
<VisualState.Setters>
<Setter
Property="BackgroundColor"
Value="#e25fc4" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<StackLayout
BackgroundColor="#f7f0f6"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
Orientation="Vertical"
Padding="8,0,8,10"
Margin="10"
Spacing="0"
HeightRequest="100">
<Label
Padding="10"
x:Name="ServiceName"
BackgroundColor="Transparent"
Text="Some Text"
HorizontalTextAlignment="Center"
TextColor="HotPink"
FontSize="Micro"
FontAttributes="Bold"
HorizontalOptions="Center"
VerticalOptions="End" />
<Label
BackgroundColor="Transparent"
Text="Some More Text"
HorizontalTextAlignment="Center"
TextColor="HotPink"
FontSize="Micro"
HorizontalOptions="Center"
VerticalOptions="Start" />
</StackLayout>
</Frame>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</ContentView.Content>
</ContentView>
And my code-behind:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Xamarin.Forms;
namespace Tests
{
public partial class CollectionViewTest : ContentView
{
public CollectionViewTest()
{
InitializeComponent();
collectionView.ItemsSource = new ObservableCollection<string>()
{
"", "", "", "", "", "", "", "", "", "", "", "", "", ""
};
}
}
}
I've tried other ways of doing this too, but nothing worked.
Is there a way to do it, or is this just a bug with CollectionView?
I found a kludgey solution, and in the absence of one that works the right way, it will have to do.
Set selection behavior in the CollectionView to none.
Put a tapGestureRecognizer into the itemTemplate
To simulate selection states, in the event handler for the tapGestureRecognizer, cast the sender to a Frame (or whatever element you attached the gesture recognizer to) and turn the frame border on or off (or do whatever you need to for your own custom selected-state appearance).
Manually handle whatever would normally be triggered by the CollectionView in response to selections. In other words, if you can select multiple items, you might be tracking the selected items in a separate list, and you will now have to do that from inside the tapGestureRecognizer.
It’s wrong but it works and sometimes that’s how you gotta do.
I start again to try to make it work, now i have a Frame in a StackLayout. And not a the other way around. But no luck is see , now there are no corners around the selected item.Sorry but i cannot make it to work.
Related
I created a customer entry which contain a label for error message
<Grid xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ExampleApp.Contols.CustomerEntry"
Title="Customer"
x:Name="AppEntry">
<Entry x:Name="ControlEntry"
BackgroundColor="{Binding BackgroundColor, Source={x:Reference AppEntry}}"/>
<Label x:Name="ConrolErrorMessage"
Text="{Binding ErrorMessage, Source={x:Reference AppEntry}}"/>
</Grid>
Now using the control, in any page.
<CustomerEntry
x:Name="Password"
BackgroundColor="Blue">
<controls:EntryControl.Behaviors>
<toolkit:TextValidationBehavior
InvalidStyle="{StaticResource InvalidEntry}"
MinimumLength="10"
MaximumLength="100" />
</controls:EntryControl.Behaviors>
</CustomerEntry>
The InvalidEntry style is simple:
<Style
x:Key="InvalidEntry"
TargetType="controls:CustomerEntry">
<Setter Property="BackgroundColor" Value="Red" />
</Style>
Now the behavior is not working. Is it possible to bind pass the behavior to the Entry of the customer control?
I try to make the entry of the customer control as follow:
<Entry x:Name="ControlEntry"
BackgroundColor="{Binding BackgroundColor, Source={x:Reference AppEntry}}"
Behaviors="{Binding Behaviors, Source={x:Reference AppEntry}}"/>
The above code didn't work.
I work on the front end of our app, configuring the UI. Our app originally had a ListView using Xamarin Community Toolkit's EventToCommandBehavior with custom ViewCells to facilitate the search. We set up custom renderers to change the ViewCell's background color. However, when I moved over to the Mac, we discovered that none of that worked with iOS; the cell background would not change.
At this point, I switched to a CollectionView with a TapGestureRecognizer on the grid using VisualStateManager to change the background color. Since then, I can either get the grid selection to highlight without the command enabling our buttons or the command to fire which enables the buttons, but the grid selection will not highlight. Simply put, I either get the command or the event but have been unable to get them working together. This is also a problem on our store page; the IAP's never highlighted before the purchase confirmation popped up. We have been experiencing this issue from the start and never noticed.
Here is the relevant XAML:
<!--Faction Search Results-->
<Frame Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="6" BackgroundColor="Black" BorderColor="White" CornerRadius="5">
<CollectionView ItemsSource="{Binding FactionItems}"
BackgroundColor="Transparent">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid x:DataType="Data:Faction" BackgroundColor="Black">
<Grid.GestureRecognizers>
<TapGestureRecognizer
NumberOfTapsRequired="1"
Command="{Binding Source={RelativeSource AncestorType={x:Type local:FactionTopPageViewModel}}, Path=ItemTapped}"
CommandParameter="{Binding .}">
</TapGestureRecognizer>
</Grid.GestureRecognizers>
<Grid.RowDefinitions>
<RowDefinition Height="{markups:OnScreenSize DefaultSize='12', iPod='20', iPhoneSE='20', iPhoneXR='20', iPhoneX='20', iPhone13='25', iPhone7p='25', iPhone11pm='25', iPhone13pm='16',
iPadMini='35', iPad9p7='35', iPad='35', iPadAir='35', iPad11='35', iPad12p9='40', Nexus1='20', NexusR='25', Nexus7R='30', Pixel2R='25', Pixel3R='25', GalaxyS8='25', Nexus6P='25', Pixel3XL='25', PixelC='40'}" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Padding="10,5" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="4" Text="{Binding FactionName}" HorizontalOptions="Start" VerticalTextAlignment="End" FontAttributes="Bold"
FontSize="{markups:OnScreenSize DefaultSize='12', iPod='15', iPhoneSE='15', iPhoneXR='18', iPhoneX='19', iPhone13='19', iPhone7p='19', iPhone11pm='19', iPhone13pm='16',
iPadMini='30', iPad9p7='30', iPad='32', iPadAir='32', iPad11='34', iPad12p9='34', Nexus1='14', NexusR='14', Nexus7R='22', Pixel2R='20', Pixel3R='20', GalaxyS8='20', Nexus6P='18', Pixel3XL='18', PixelC='34'}" />
<Label Padding="0,5" Grid.Row="0" Grid.Column="4" Grid.ColumnSpan="2" Text="{Binding NumberMembersString}" HorizontalOptions="Center" VerticalTextAlignment="End" FontAttributes="Bold"
FontSize="{markups:OnScreenSize DefaultSize='12', iPod='15', iPhoneSE='15', iPhoneXR='18', iPhoneX='19', iPhone13='19', iPhone7p='19', iPhone11pm='19', iPhone13pm='16',
iPadMini='30', iPad9p7='30', iPad='32', iPadAir='32', iPad11='34', iPad12p9='34', Nexus1='14', NexusR='14', Nexus7R='22', Pixel2R='20', Pixel3R='20', GalaxyS8='20', Nexus6P='18', Pixel3XL='18', PixelC='34'}"/>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="CommonStates">
<VisualState Name="Normal"></VisualState>
<VisualState Name="Selected">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="#ae00ff"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
Relevant code from the ViewModel:
public Command<Faction> ItemTapped { get; }
public FactionTopPageViewModel()
{
ItemTapped = new Command<Faction>(OnItemSelected);
}
private void OnItemSelected(Faction faction)
{
if (faction == null)
{
SelectedItem = null;
DisableFactionButtons();
return;
}
EnableFactionButtons();
SelectedItem = faction;
}
This results in our buttons enabled at the bottom of the page, but the faction (essentially a team) is not being highlighted in the CollectionView, so the user does not receive confirmation of selecting the proper faction.
If I remove the tapgesture and work through the CollectionView commands setting the SelectionMode="Single", SelectionChangedCommand={Binding IsTapped}, and SelectionChangedCommandParameter={Binding .} then the selected Faction highlights without enabling any buttons. These issues are not platform-specific, like the ListView ViewCell background.
I have been working on this longer than I care to mention, as it seems like I have not wrapped my head around some relatively simple concept. Any help would be greatly appreciated, this issue is holding up the next release of our app.
I'm creating a custom control for an entry that can be validated.
I did this by creating a ContentView that has a Grid as it's child that contains the entry, error label, etc.
I'd like this to be flexible when it comes to validation, so ideally it would be nice to expose the Entry's MultiValidationBehavior's Children property, or set that property as my control's content property.
As it stands now, I haven't figured out a way to add behaviors to my custom control.
Is this possible?
<ContentView x:Class="MPK.UI.Views.Components.FormEntry"
x:Name="FormEntryControl"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
xmlns:yummy="clr-namespace:Xamarin.Forms.PancakeView;assembly=Xamarin.Forms.PancakeView"
xmlns:converters="clr-namespace:MPK.UI.Converters;assembly=MPK.UI">
<ContentView.Resources>
<ResourceDictionary>
<converters:IsValidToEntryBorderConverter x:Key="IsValidToEntryBorderConverter"/>
<converters:ErrorsToLabelTextConverter x:Key="ErrorsToLabelTextConverter"/>
<xct:InvertedBoolConverter x:Key="InvertedBoolConverter" />
</ResourceDictionary>
</ContentView.Resources>
<Grid>
<yummy:PancakeView CornerRadius="10"
HeightRequest="50"
HorizontalOptions="FillAndExpand"
BackgroundColor="{StaticResource EntryBackgroundColor}"
Padding="16,0,16,0">
<yummy:PancakeView.Behaviors>
<xct:AnimationBehavior AnimateCommand="{Binding Source={x:Reference FormEntryControl}, Path=ShakeCommand}">
<xct:AnimationBehavior.AnimationType>
<xct:ShakeAnimation />
</xct:AnimationBehavior.AnimationType>
</xct:AnimationBehavior>
</yummy:PancakeView.Behaviors>
<yummy:PancakeView.Border>
<yummy:Border
Color="{Binding IsValid, Source={x:Reference MultiValidationBehavior}, Converter={StaticResource IsValidToEntryBorderConverter}}"
Thickness="1" />
</yummy:PancakeView.Border>
<Entry x:Name="Entry"
Text="{Binding Text, Source={x:Reference FormEntryControl}}"
Placeholder="{Binding Placeholder, Source={x:Reference FormEntryControl}}"
ReturnType="{Binding ReturnType, Source={x:Reference FormEntryControl}}"
ReturnCommand="{Binding ReturnCommand, Source={x:Reference FormEntryControl}}"
PlaceholderColor="{StaticResource EntryPlaceholderTextColor}"
BackgroundColor="Transparent"
IsPassword="{Binding IsPassword, Source={x:Reference FormEntryControl}}"
ClearButtonVisibility="{Binding ClearButtonVisibility, Source={x:Reference FormEntryControl}}">
<Entry.Effects>
<xct:RemoveBorderEffect />
</Entry.Effects>
<Entry.Behaviors>
<xct:MultiValidationBehavior x:Name="MultiValidationBehavior"
IsValid="{Binding IsValid, Source={x:Reference FormEntryControl}, Mode=TwoWay}"
Children="{Binding ValidationBehaviors, Source={x:Reference FormEntryControl}}"/>
<!-- Binding children doesn't work here -->
</Entry.Behaviors>
</Entry>
</yummy:PancakeView>
<xct:Expander Margin="8,4,0,0"
AnimationLength="100"
IsExpanded="{Binding IsValid, Source={x:Reference FormEntryControl}, Mode=OneWay, Converter={StaticResource InvertedBoolConverter}}">
<Label Text="{Binding Errors, Source={x:Reference MultiValidationBehavior}, Converter={StaticResource ErrorsToLabelTextConverter}}"
TextColor="{StaticResource ErrorColor}" />
</xct:Expander>
</Grid>
</ContentView>
The solution was much simpler than I expected.
In my control's code behind I needed to add a property that points to the multivalidationbehavior's children property.
public IList<ValidationBehavior> ValidationBehaviors => TheMultiValidationBehavior.Children;
Using my custom control looks something like this:
<components:FormEntry Placeholder="Name">
<components:FormEntry.ValidationBehaviors>
<xct:TextValidationBehavior MinimumLength="1" xct:MultiValidationBehavior.Error="Min: 1"/>
</components:FormEntry.ValidationBehaviors>
</components:FormEntry>
Good afternoon.
I am using a CollectionView in Xamarin forms 5.0
I already succed to "transfert" 2 or more items from one CollectionView to an other one in the same window using SelectionMode="Multiple".
For my test, i just use and customize a good example founded on the web: https://github.com/CrossGeeks/DragAndDropXFSample
What i'm trying to do concern the appearance of the dragged items.
Because i add a DragGestureRecognizer inside my CollectionView.ItemTemplate.DataTemplate
on my first collection view and a DropGestureRecognizer inside my CollectionView.ItemTemplate.DataTemplate second collection view, the drag & drop functionnality work well.
But, the animation draw only the item i touched, not all the selected ones.
Does anybody has an idea to manage that ?
Is it even possible?
Anyway, thanks for reading and maybe your help ;-)
The xaml page:
<?xml version="1.0" encoding="UTF-8" ?>
<ContentPage
x:Class="DragAndDropXFSample.EventsPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<ContentPage.Content>
<StackLayout Padding="20,40" BackgroundColor="Black">
<Label
FontAttributes="Bold"
FontSize="Large"
Text="Hi John" />
<Label Text="Your schedule Today" />
<Frame Margin="0,20,0,0" BackgroundColor="White">
<StackLayout>
<CollectionView
x:Name="EventsCollection"
ItemsSource="{Binding Events}"
SelectionMode="Multiple">
<CollectionView.EmptyView>
<Label Text="You have no events" VerticalOptions="CenterAndExpand" />
</CollectionView.EmptyView>
<CollectionView.ItemsLayout>
<GridItemsLayout
HorizontalItemSpacing="10"
Orientation="Vertical"
Span="2"
VerticalItemSpacing="10" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Frame
Padding="0"
BackgroundColor="{Binding Color}"
HasShadow="False">
<Frame.Style>
<Style TargetType="Frame">
<!-- To visualy show the selection to the user -->
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Selected">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="CadetBlue" />
<Setter TargetName="uiTime" Property="Label.FontAttributes" Value="Italic" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
</Frame.Style>
<StackLayout Padding="10,10,10,0" Spacing="10">
<Label
x:Name="uiTime"
FontAttributes="Bold"
FontSize="Large"
Text="{Binding Time, StringFormat='{0:HH:mm}'}" />
<Label FontSize="15" Text="{Binding Title}" />
<Label
FontSize="Caption"
Text="{Binding Location, StringFormat='At {0}'}"
TextColor="White" />
<StackLayout
Margin="-10,0"
Padding="5"
BackgroundColor="#66000000"
Orientation="Horizontal">
<Image
HeightRequest="20"
HorizontalOptions="EndAndExpand"
Source="ic_edit" />
<Label
FontSize="Caption"
Text="Edit"
TextColor="White"
VerticalOptions="Center" />
</StackLayout>
</StackLayout>
<Frame.GestureRecognizers>
<DragGestureRecognizer DragStartingCommand="{Binding Path=BindingContext.DragStartingCommand, Source={x:Reference EventsCollection}}" DragStartingCommandParameter="{Binding SelectedItems, Source={x:Reference EventsCollection}}" />
</Frame.GestureRecognizers>
</Frame>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<CollectionView x:Name="EventsCollection2" ItemsSource="{Binding Events2}">
<CollectionView.EmptyView>
<Label Text="You have no events" VerticalOptions="CenterAndExpand" />
</CollectionView.EmptyView>
<CollectionView.ItemsLayout>
<GridItemsLayout
HorizontalItemSpacing="10"
Orientation="Vertical"
Span="2"
VerticalItemSpacing="10" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Frame
Padding="0"
BackgroundColor="{Binding Color}"
HasShadow="False">
<StackLayout Padding="10,10,10,0" Spacing="10">
<Label
x:Name="uiTime2"
FontAttributes="Bold"
FontSize="Large"
Text="{Binding Time, StringFormat='{0:HH:mm}'}" />
<Label FontSize="15" Text="{Binding Title}" />
<Label
FontSize="Caption"
Text="{Binding Location, StringFormat='At {0}'}"
TextColor="White" />
<StackLayout
Margin="-10,0"
Padding="5"
BackgroundColor="#66000000"
Orientation="Horizontal">
<Image
HeightRequest="20"
HorizontalOptions="EndAndExpand"
Source="ic_edit" />
<Label
FontSize="Caption"
Text="Edit"
TextColor="White"
VerticalOptions="Center" />
</StackLayout>
</StackLayout>
<Frame.GestureRecognizers>
<DropGestureRecognizer DropCommand="{Binding BindingContext.DropOverList2Command, Source={x:Reference EventsCollection2}}" />
</Frame.GestureRecognizers>
</Frame>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
</Frame>
</StackLayout>
</ContentPage.Content>
</ContentPage>
The view model class :
public class EventsPageViewModel
{
private IList<Event> _dragEvent;
/// <summary>
/// It's a Command<IList<object>> beacause of the Selectionmode.Multiple
/// </summary>
public ICommand DragStartingCommand => new Command<IList<object>>((param) =>
{
_dragEvent = new List<Event>(param.Cast<Event>());
});
public ICommand DropOverList2Command => new Command(() =>
{
foreach (var e in _dragEvent)
{
if (Events.Contains(e))
{
Events.Remove(e);
Events2.Add(e);
}
}
});
public ObservableCollection<Event> Events { get; }
public ObservableCollection<Event> Events2 { get; }
public EventsPageViewModel()
{
Events = new ObservableCollection<Event>()
{
{new Event("Go for a walk", "Home", DateTime.Now.AddHours(3), Color.OrangeRed) },
{new Event("Finish PR", "Work", DateTime.Now.AddHours(5), Color.ForestGreen) },
{new Event("Watch a movie", "Home", DateTime.Now.AddMinutes(40), Color.LightSkyBlue) },
};
Events2 = new ObservableCollection<Event>()
{
{new Event("Go for a walk2", "Home", DateTime.Now.AddHours(3), Color.GreenYellow) }
};
}
}
Edit :
I cannot make video but i 've made some screenshots.
Initial state:
Nothing selected, the top collection view is the source, and the bottom collection view is the dest
I select the first item in the top collection view
I select a second item in the top CollectionView
I start dragging by touching and moving the first selected item
the expected result would be something like :
EDIT:
I find something interresting called Windows.UI.Xaml.DragEventArgs.DragUIOverride
see here but apparently it is for uwp, does anybody know if there is an equivalent for Android/iOS ? Thanks for your time
I've got following view:
<ContentPage.Content>
<AbsoluteLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<StackLayout AbsoluteLayout.LayoutFlags="All" AbsoluteLayout.LayoutBounds="0,0,1,1">
<StackLayout VerticalOptions="CenterAndExpand" Margin="20, 0, 20, 0">
<StackLayout>
<Label Text="Login" HorizontalOptions="CenterAndExpand" />
<Entry Text="{Binding Login, Mode=OneWayToSource}"></Entry>
</StackLayout>
<StackLayout>
<Label Text="Hasło" HorizontalOptions="CenterAndExpand" />
<Entry IsPassword="True" Text="{Binding Password, Mode=TwoWay}"></Entry>
</StackLayout>
<Button Text="Zaloguj się" Command="{Binding SignInCommand}"></Button>
</StackLayout>
</StackLayout>
<BoxView AbsoluteLayout.LayoutFlags="All" AbsoluteLayout.LayoutBounds="0,0,1,1" BackgroundColor="Gray" Opacity="0.5" InputTransparent="false" IsVisible="{Binding IsBusy}" />
<ActivityIndicator IsRunning="{Binding IsBusy}" AbsoluteLayout.LayoutFlags="All" AbsoluteLayout.LayoutBounds="0,0,1,1" VerticalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand" />
</AbsoluteLayout>
</ContentPage.Content>
I want to move BoxView and ActivityIndicator to external control to make reusable component. The problem is that to achieve this I need to "group" these controls to obtain one child element.
The question is if is there any element I can use to group my controls, but which will not affect the way how controls are displayed? Alternatevlly which element I can use and how to still have effect of overlay over whole page and loading indicator?
I was trying to use AbsoluteLayout, StackLayout etc. but I couldn't position it to persist initial effect (overlay on whole page and loading indicator in the center with opacity = 1).
You can use DependencyService and reuse it in any page.
Check this tutorial.
There is also a working sample.