Xamarin.FormsでListViewの選択行ハイライト色を変える


ListView の選択行のハイライト色を設定するプロパティが用意されていないとかふざけてるんで、自前で作るしかないようです。

## 実現手段の概要

基本的にカスタムレンダラー対応。

ListView をタップして行（セル）が選択されると、その行の View たる ViewCell に PropertyChanged が送られるようなので、それを利用。

ViewCell クラスのプロパティにそんなのは存在しないが、ともかく送られてくる。OnPropertyChanged で見張っていると判る。

しかし送られて来るのはAndroidのみでiOSだと送られて来ない。
なのでiOSはカスタムレンダラー必須。面倒臭すぎ。


## コード

### PCL
```xml:XAML
<?xml version="1.0" encoding="utf-8"?>
<ContentPage
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:v="clr-namespace:MyApp.Views"
    xmlns:vm="clr-namespace:MyApp.ViewModels"
    Padding="30"
    x:Class="MyApp.Views.MainPage">

    <ContentPage.BindingContext>
        <vm:MainPageViewModel />
    </ContentPage.BindingContext>

    <StackLayout>
        <ContentView BackgroundColor="Gray" Padding="5">
            <ListView ItemsSource="{Binding Items}" BackgroundColor="White" HasUnevenRows="false" RowHeight="40">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <v:MyViewCell SelectedBackgroundColor="Red">
                            <ContentView>
                                <StackLayout Orientation="Horizontal">
                                    <Label Text="{Binding}" TextColor="Black" VerticalTextAlignment="Center" />
                                </StackLayout>
                            </ContentView>
                        </v:MyViewCell>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
        </ContentView>
    </StackLayout>

</ContentPage>
```

```csharp:View
using Xamarin.Forms;

namespace MyApp.Views
{
    public class MyViewCell : ViewCell
    {
        public static readonly BindableProperty SelectedBackgroundColorProperty = BindableProperty.Create(
            "SelectedBackgroundColor", typeof(Color), typeof(MyViewCell), Color.Default
        );

        public Color SelectedBackgroundColor {
            get { return (Color)GetValue(SelectedBackgroundColorProperty); }
            set { SetValue(SelectedBackgroundColorProperty, value);}
        }

        public MyViewCell()
        {
        }
    }
}
```

```csharp:ViewModel
using System.Collections.Generic;

namespace MyApp.ViewModels
{
    public class MainPageViewModel
    {
        public List<string> Items { protected set; get; } = new List<string>();

        public MainPageViewModel()
        {
            Items.Add("First Item");
            Items.Add("Second Item");
		}
    }
}
```

### iOS
```csharp:カスタムレンダラー
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

using UIKit;

using MyApp.Views;
using MyApp.iOS.Renderers;

[assembly: ExportRenderer(typeof(MyViewCell), typeof(MyViewCellRenderer))]

namespace MyApp.iOS.Renderers
{
    public class MyViewCellRenderer: ViewCellRenderer
    {
        public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv)
        {
            var cell = base.GetCell(item, reusableCell, tv);

            cell.SelectedBackgroundView = new UIView
            {
                BackgroundColor = ((MyViewCell)item).SelectedBackgroundColor.ToUIColor()
            };

            return cell;
        }
    }
}
```

### Android
```csharp:カスタムレンダラー
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

using MyApp.Views;
using MyApp.Droid.Renderers;

[assembly: ExportRenderer(typeof(MyViewCell), typeof(MyViewCellRenderer))]

namespace MyApp.Droid.Renderers
{
    public class MyViewCellRenderer: ViewCellRenderer
    {
        private Android.Views.View _cellCore;
        private bool _selected;
        private Android.Graphics.Drawables.Drawable _unselectedBackground;

        protected override Android.Views.View GetCellCore(Cell item, Android.Views.View convertView, Android.Views.ViewGroup parent, Android.Content.Context context)
        {
            _cellCore = base.GetCellCore(item, convertView, parent, context);

            this._selected = false;
            this._unselectedBackground = _cellCore.Background;

            return _cellCore;
        }

        protected override void OnCellPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            base.OnCellPropertyChanged(sender, e);

            if (e.PropertyName == "IsSelected") {
				_selected = !(_selected);
                if (_selected) {
                    _cellCore.SetBackgroundColor(((MyViewCell)sender).SelectedBackgroundColor.ToAndroid());
                } else {
                    _cellCore.SetBackground(this._unselectedBackground);
                }
			}
        }
    }
}
```

## 結果

![ios.png](https://qiita-image-store.s3.amazonaws.com/0/169798/cce8991a-3f49-ab8d-ccee-7750a207bedd.png "ios.png")

![android.png](https://qiita-image-store.s3.amazonaws.com/0/169798/adf1a87c-e88b-8ad1-a19a-ce5897501a21.png "android.png")



両OSともハイライト色が赤くなりました。
うまくいっているようです。


## AndroidをPCLだけでなんとかするケース

前述のとおり、iOSでは IsSelected を捕捉できないっぽいので、これが通用するのはAndroidだけです。

```csharp:View
ing Xamarin.Forms;

namespace MyApp.Views
{
    public class MyViewCell : ViewCell
    {
        public static readonly BindableProperty SelectedBackgroundColorProperty = BindableProperty.Create(
            "SelectedBackgroundColor", typeof(Color), typeof(MyViewCell), Color.Default
        );

        public Color SelectedBackgroundColor {
            get { return (Color)GetValue(SelectedBackgroundColorProperty); }
            set { SetValue(SelectedBackgroundColorProperty, value);}
        }

        protected Color _UnselectedBackgroundColor;
        protected bool _selected;

        public MyViewCell()
        {
            this._selected = false;
        }

        protected override void OnPropertyChanged(string propertyName = null)
        {
            base.OnPropertyChanged(propertyName);

            if (propertyName == "IsSelected") {
                this._selected = !(this._selected);
                if (this._selected) {
                    if (this._UnselectedBackgroundColor == null) {
                        this._UnselectedBackgroundColor = this.View.BackgroundColor;
                    }
					this.View.BackgroundColor = this.SelectedBackgroundColor;
                } else {
                    this.View.BackgroundColor = this._UnselectedBackgroundColor;
                }
            }
        }
    }
}
```

とりあえずは以上です。


## こちらを
参考にし
パクリました
http://blog.wislon.io/posts/2017/04/11/xamforms-listview-selected-colour