David Shochet
David Shochet

Reputation: 5395

How can I set a text color for a selected CollectionView item?

Here is my CollectionView:

            <CollectionView 
                    Margin="5,5,5,15"
                    ItemSizingStrategy="MeasureAllItems"
                    ItemsSource="{Binding Languages}"
                    SelectedItem="{Binding SelectedLanguage, Mode=TwoWay}"
                    SelectionChangedCommand="{Binding LanguageChangedCommand}"
                    SelectionMode="Single">
                <CollectionView.ItemsLayout>
                    <GridItemsLayout Orientation="Vertical" />
                </CollectionView.ItemsLayout>
                <CollectionView.ItemTemplate>
                    <DataTemplate x:DataType="language:CultureInfo">
                        <ContentView Padding="5" >
                            <Grid ColumnDefinitions="*,3*">
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="40" />
                                </Grid.RowDefinitions>
                                <Image Source="{Binding TwoLetterISOLanguageName, StringFormat='flag_{0}.png'}" Grid.Column="0" HorizontalOptions="Start" />
                                <Label Text="{Binding DisplayName}" Grid.Column="1" HorizontalOptions="Start" VerticalOptions="Center" />
                            </Grid>
                        </ContentView>
                    </DataTemplate>
                </CollectionView.ItemTemplate>
            </CollectionView>

And here is my style:

<Style TargetType="ContentView">
    <Setter Property="VisualStateManager.VisualStateGroups">
        <VisualStateGroupList>
            <VisualStateGroup x:Name="CommonStates">
                <VisualState x:Name="Normal">
                    <VisualState.Setters>
                        <Setter Property="BackgroundColor" Value="Transparent" />
                    </VisualState.Setters>
                </VisualState>
                <VisualState x:Name="Selected">
                    <VisualState.Setters>
                        <Setter Property="BackgroundColor" Value="{StaticResource LaticreteColor}" />
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateGroupList>
    </Setter>
</Style>

Here I set background color of the selected item. But I also need to set text color of the selected item, and TextColor property does not exist:

"Cannot resolve property "TextColor" on type "ContentView (property missing or missing accessors)"."

How can this be achieved?

Upvotes: 0

Views: 356

Answers (2)

Alexandar May - MSFT
Alexandar May - MSFT

Reputation: 10148

To achieve the effect, you can use VisualStateManager.VisualStateGroups to make it work. Please try adding x:Name for the Label and then use to set the TextColor for the Label. See Set state on multiple elements.

Please refer to the sample code below:

<ContentPage.Resources>
       <Style TargetType="ContentView">
            <Setter Property="VisualStateManager.VisualStateGroups">
                <VisualStateGroupList>
                    <VisualStateGroup Name="CommonStates">

                        <VisualState x:Name="Normal">
                            <VisualState.Setters>
                                <Setter Property="BackgroundColor" Value="Transparent" />
                            </VisualState.Setters>
                        </VisualState>

                        <VisualState Name="Selected">
                            <VisualState.Setters>
                                <Setter Property="BackgroundColor" Value="Yellow" />
                                <Setter TargetName="label" Property="Label.TextColor" Value="Red" />
                            </VisualState.Setters>
                        </VisualState>
                    </VisualStateGroup>
                </VisualStateGroupList>
               
            </Setter>          
        </Style>
</ContentPage.Resources>

<CollectionView ItemsSource="{Binding Monkeys}"
                        SelectionMode="Single"
                        SelectionChanged="OnCollectionViewSelectionChanged">
            <CollectionView.ItemTemplate>
                <DataTemplate>
                    <ContentView Padding="5">
                        <Grid Padding="10">  
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto" />
                            <RowDefinition Height="Auto" />
                        </Grid.RowDefinitions>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto" />
                            <ColumnDefinition Width="Auto" />
                        </Grid.ColumnDefinitions>
                        <Image Grid.RowSpan="2"
                               Source="{Binding ImageUrl}"
                               Aspect="AspectFill"
                               HeightRequest="60"
                               WidthRequest="60" />
                        <Label Grid.Column="1"
                               Text="{Binding Name}"
                               FontAttributes="Bold" />
                        <Label Grid.Row="1"
                               Grid.Column="1"
                               
                               x:Name="label"
                               Text="{Binding Location}"
                               FontAttributes="Italic"
                               VerticalOptions="End" />
                    </Grid>
                    </ContentView>
                </DataTemplate>
            </CollectionView.ItemTemplate>
</CollectionView>

Output:

enter image description here

Upvotes: 1

Stephen Quan
Stephen Quan

Reputation: 26149

The solution would be to put a VisualStateGroup directly on your DataTemplate in the areas you need it, so, you can put one directly on the ContentView for the BackgroundColor but, you can also put one directly on the Label for TextColor.

However, I found a quirk/bug with CollectionView not showing the VisualGroup correctly on startup. Only after you've interacted with the CollectionView do you see the outcome, hence, any initial value in the SelectedLanguages will not be shown to the user.

There are two workarounds needed to resolve that issue:

  1. Add a delay, approx. 500ms, on app startup to delay set the SelectedLanguages
  2. Replace VisualStateGroup with a Binding (or MultiBinding).

Here, I've used a simplistic CompareToObjectConverter. You'll see that this has been hardcoded to only recognize strings, but, you can generalize for other types:

public class CompareToObjectConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values.Length != 4)
            throw new ArgumentException("Must have four values");
        return values[0]?.ToString() == values[1]?.ToString() ? values[2] : values[3];
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Then, in your XAML, you can refer to this converter in your Bindings:

<!-- MainPage.xaml ... -->
<ContentPage>
    <!-- ... -->
    <ContentPage.Resources>
        <ResourceDictionary>
            <local:CompareToObjectConverter x:Key="CompareToObjectConverter"/>
        </ResourceDictionary>
    </ContentPage.Resources>
    <!-- ... -->
    <CollectionView ItemsSource="{Binding Languages}"
                    SelectedItem="{Binding SelectedLanguage, Mode=TwoWay}"
                    SelectionMode="Single">
        <CollectionView.ItemTemplate>
            <DataTemplate>
                <ContentView>
                    <ContentView.BackgroundColor>
                        <MultiBinding Converter="{x:StaticResource CompareToObjectConverter}">
                            <Binding Path="SelectedLanguage.Name" Source="{x:RelativeSource AncestorType={x:Type local:MainPage}}"/>
                            <Binding Path="Name"/>
                            <Binding Path="." Source="{x:Static Colors.LightSteelBlue}"/>
                            <Binding Path="." Source="{x:Static Colors.Transparent}"/>
                        </MultiBinding>
                    </ContentView.BackgroundColor>
                    <Grid ColumnDefinitions="*,*">
                        <Image Source="{Binding TwoLetterISOLanguageName, StringFormat='flag_{0}.png'}"/>
                        <Label Grid.Column="1" Text="{Binding DisplayName}">
                            <Label.TextColor>
                                <MultiBinding Converter="{x:StaticResource CompareToObjectConverter}">
                                    <Binding Path="SelectedLanguage.Name" Source="{x:RelativeSource AncestorType={x:Type local:MainPage}}"/>
                                    <Binding Path="Name"/>
                                    <Binding Path="." Source="{x:Static Colors.White}"/>
                                    <Binding Path="." Source="{x:Static Colors.Black}"/>
                                </MultiBinding>
                            </Label.TextColor>
                        </Label>
                    </Grid>
                </ContentView>
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>
</ContentPage>

Upvotes: 1

Related Questions