Xamarin.Forms - Changing selected item color in UWP

Xamarin XAML

6 years ago

Xamarin.Forms has a built-in ListView control which offers rich cross-platform functionality including item selection. In case of UWP the color of the selected item is by default determined by the current accent color of the OS. However, it is a common requirement to customize this to better match the branding of your application. In this article we will show how to do so.

Digging into the default style

When it comes to the styling of UWP control, the first place to visit are the default styles of the controls. You can either find a slightly outdated version [on-line in the documentation](https://msdn.microsoft.com/en-us/library/windows/apps/xaml/f5fba473-bc71-40ec-8df1-ea977c6938ed), or you can find them locally on your hard drive as part of the UWP SDKs. Go to file explorer and navigate to the following path:

C:\Program Files (x86)\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAP

In this folder you are going to find several subfolders for each of the installed versions of UWP. Select the one which your project is targeting, navigate to the Generic subfolder and finally open the file generic.xaml in your favorite text editor. In this rather huge file search for TargetType="ListViewItem" which should hopefully yield the default style for ListViewItem control that provides the behavior for each individual item in your ListView. Within this style you are going to find out that it uses a number of brush-based resources for different states of the item, including the SelectedBackground property. This is set as follows:

SelectedBackground="{ThemeResource ListViewItemBackgroundSelected}"

Where does this ListViewItemBackgroundSelected resource come from? That's where the second file in the Generic folder comes into play - themeresources.xaml. Open it and search for this key to find:

<StaticResource x:Key="ListViewItemBackgroundSelected" ResourceKey="SystemControlHighlightListAccentLowBrush" />

So yet another reference! Search again to finally find:

<SolidColorBrush x:Key="SystemControlHighlightListAccentLowBrush" Color="{ThemeResource SystemAccentColor}" Opacity="0.6" />

So basically, the selected item is indeed displayed with the system accent color with opacity 0.6. How can we now override this behavior in Xamarin.Forms?

Overriding default color

Application-wide resource

The easier but far less ideal option you have is to override the resource in an application-wide manner. If we provide a custom SystemControlHighlightListAccentLowBrush it will override or instances where this Brush is used (even including other controls). To do this, we will go to the UWP project's App.xaml file and will add a resource in the Application's resource dictionary:

<Application ...>
    <Application.Resources>
         <SolidColorBrush Color="DodgerBlue" x:Key="SystemControlHighlightListAccentLowBrush" />
    </Application.Resources>
</Application>

Because the brush has the same key as the system provided one and it is higher in the hierarchy, it will override the system default. All ListViews and other controls in your app should now use the vivid DodgerBlue color for selected item background.

Custom renderer

The less invasive option is to create a custom version of ListView with custom renderer on UWP. First we will create a new SelectionColorListView class in the shared or .NET Standard project:

public class SelectionColorListView : ListView
{
    public static readonly BindableProperty SelectionColorProperty =
        BindableProperty.Create(nameof(SelectionColor), typeof(Color), typeof(SelectionColorListView), Color.LawnGreen);

    public Color SelectionColor
    {
        get => (Color) GetValue(SelectionColorProperty);
        set => SetValue(SelectionColorProperty, value);
    }
}

Now we need a custom renderer for UWP which will check the custom resource when the control is first set:

[assembly: ExportRenderer(typeof(SelectionColorListView), typeof(SelectionColorListViewRenderer))]

namespace XFUwpListViewColors.UWP
{
    public class SelectionColorListViewRenderer : ListViewRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
        {
            base.OnElementChanged(e);
            if (e.NewElement != null)
            {
                UpdateSelectionColor();
            }
        }

        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);
            if (e.PropertyName == nameof(SelectionColorListView.SelectionColor))
            {
                UpdateSelectionColor();
            }
        }

        private void UpdateSelectionColor()
        {
            if (Control != null && Element is SelectionColorListView listView)
            {
                var nativeColor = XamarinColorToNative(listView.SelectionColor);
                Control.Resources["SystemControlHighlightListAccentLowBrush"] = new SolidColorBrush(nativeColor);
            }
        }

        private Windows.UI.Color XamarinColorToNative(Xamarin.Forms.Color color)
        {
            var alpha = (byte)(color.A * 255);
            var red = (byte)(color.R * 255);
            var green = (byte)(color.G * 255);
            var blue = (byte)(color.B * 255);
            return Windows.UI.Color.FromArgb(alpha, red, green, blue);
        }
    }
}

We are waiting for the element or SelectionColor property to be updated and then set the SystemControlHighlightListAccentLowBrush resource for the underlying control. Now, you can use your custom ListView in XAML like this:

<local:SelectionColorListView SelectionMode="Single" SelectionColor="LimeGreen" />

Where local: must be a declared XAML namespace in the ContentPage element:

 xmlns:local="clr-namespace:NamespaceOfYourCustomListView"

Note that updating the color at runtime is not possible, because once the selection is first done, the control will keep using the resource that was resolved for the first time.

Further improvements

To unify the style across platforms, you could now provide custom renderers for ListView controls on Android and iOS as well. This is not required though and Xamarin.Forms will otherwise happily use the default ListViewRenderer and ignore the SelectionColor property. In addition, you may want to customize other ListViewItem colors as well - the hover background color, pressed background color, etc.. To do this, you can follow the exact same process as outlined above.

Future

At Build 2018 Microsoft announced an upcoming tool that will auto-generate appropariate default style resources for your application based on your branding. This will make our lives significantly easier and unifying the style for the whole application will be as easy as 1,2,3.

Source code

Source code for this article is available on my GitHub.

Summary

Modifying default UWP control styles in Xamarin.Forms a bit more complicated than doing the same in classic UWP projects. If you don't want to change the behavior of all controls, you should create a custom renderer and perform the changes there only.