Xamarin.Forms obsahuje ovladací prvek
ListView
, který nabízí bohaté funkce včetně výběru položky seznamu. V případě UWP je barva výběru udávána aktuálním nastavením barveného motivu operačního systému. Obvyklým požadavkem však je lépe přizpůsobit aplikaci vlastnímu brandingu. V tomto článku se podíváme, jak na to.
Nahlédnutí do výchozího stylu
Pokud chceme zjistit jak je implementován výchozí vzhled zabudovaných ovladacích prvků, nejlepší místo pro to jsou výchozí styly v samotném SDK. Můžeme buď najít trochu zastaralou verzi v on-line dokumentaci, nebo aktuální přímo na našem hard disku. V prohlížeči souborů přejděte na cestu:C:\Program Files (x86)\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAPV této složce najdeme podsložku pro každou nainstalovanou verzi UWP SDK. Vybereme tu, na kterou náš projekt cílí a přejděte do složky
Generic
kde otevřeme generic.xaml
ve našem oblíbeném textovém editoru. V tomto poměrně velkém souboru vyhledáme TargetType="ListViewItem"
, díky čemuž bychom měli najít výchozí styl prvku ListViewItem
, který poskytuje obálku pro každou položku v našem ListView
.
V tomto stylu zjistíme, že mnoho různých resource založených na třídě Brush
, mimojiné také vlastnost SelectedBackground
. Ta je nastavena takto:
SelectedBackground="{ThemeResource ListViewItemBackgroundSelected}"Odkud vlastně
ListViewItemBackgroundSelected
pochází? Na to tu máme druhý soubor ve složce Generic
– themeresources.xaml
. Otevřeme jej:
<StaticResource x:Key="ListViewItemBackgroundSelected" ResourceKey="SystemControlHighlightListAccentLowBrush" />Takže pouze další odkaz! Pokračujeme v hledání:
<SolidColorBrush x:Key="SystemControlHighlightListAccentLowBrush" Color="{ThemeResource SystemAccentColor}" Opacity="0.6" />Zjistili jsme tedy, že vybraná položka je skutečně zobrazena barvou motivu systému s viditelností 0.6. Jak můžeme toto výchozí chování v Xamarin.Forms změnit?
Změna výchozí barvy
Resource pro celou aplikaci
Jednodušší, ale výrazně méně ideální cestou je přepsání resource na úrovni aplikace. Poskytneme-li vlastníSystemControlHighlightListAccentLowBrush
, nahradí výchzozí náš brush všechny výskyty kde se resource používá – tedy včetně jiných ovladacích prvků.
Pokud je to to co chceme, můžeme v UWP projektu otevřít soubor App.xaml
a přidat resource do resource dictionary Application
:
<Application ...> <Application.Resources> <SolidColorBrush Color="DodgerBlue" x:Key="SystemControlHighlightListAccentLowBrush" /> </Application.Resources> </Application>Protože má brush stejný klíč jako ten nastavený systémem ale je výše v hierarchii, přepíše automaticky výchozí hodnotu v naší aplikaci. Všechny
ListView
a další prvky v naší aplikaci budou nyní namísto barvy motivu používat výraznou barvu DodgerBlue
.
Custom renderer
Méně invazivní cestou je použití Xamarin custom rendereru na vytvoření nové verzeListView
s custom rendererem na UWP. Nejprve vytvoříme novout třídu SelectionColorListView
ve sdíleném nebo .NET Standard projektu:
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); } }Nyní napíšeme vlastní custom renderer který při prvním načtení zjistí barvu pro výběr a nahradí resource právě pro tento control.
[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); } } }Nyní můžeme použít náš vlastní
ListView
v XAMLu:
<local:SelectionColorListView SelectionMode="Single" SelectionColor="LimeGreen" />Kde
local:
musí být deklarovaný XAML jmený prostor v elementu ContentPage
:
xmlns:local="clr-namespace:NamespaceOfYourCustomListView"Poznamenejme že aktualizace již vybrané barvy za běhu bohužel není možné, protože
ListView
si po prvním použití brush zapamatuje a bude nadále používat resource který nalezl poprvé.
Další vylepšení
Pro sjednocení přes různé platformy bychom mohli implementovat custom rederer na Androidu a iOS. Není to však povinné, protože Xamarin.Forms jinak bude prostě používat výchozíListViewRenderer
a ignorovat vlastnost SelectionColor
.
Navíc bychom také mohli přizpůsobit i ostatní barvy ListViewItem
– barvu při přejetí myši nad položkou, barvu při stisknutí, atd. Toho dosáhneme přesně stejným postupem jako výše.
Budoucnost
Na konferenci Build 2018 Microsoft představil připravovaný nástroj, který automaticky vygeneruje pro vaši aplikaci barevné styly odpovídající zadanému brandingu. To naše životy výrazně zjednodušší a přestylování celé aplikace už půjde levou zadní.Zdrojový kód
Zdrojový kód tohoto článku je dostupný na mém GitHubu.