Xamarin.Forms の TextCell の Detail に複数行を表示したい

## 動機

Xamarin.Forms に **TextCell** という UI があります。大きなフォントで1行の **Text** ラベルと、小さなフォントで１行の **Detail** ラベルとからなるセルです。[**ListView**](https://docs.microsoft.com/ja-jp/xamarin/xamarin-forms/user-interface/listview/) や [**TableView**](https://docs.microsoft.com/ja-jp/xamarin/xamarin-forms/user-interface/tableview) の要素になります。

**Detail** に複数行の文字列を表示したいことがありました。試しに **Detail** に複数行の文字列を入れると、１行目だけが表示されました。これではダメですね。(あとで分かったのですが、1行目だけが表示されるのは iOS の場合で、Android では改行されずに続けて表示されます。)

こういうとき、まずは **ViewCell** を使うのだと思います。しかし、**ViewCell** の中に **Label** を並べただけだと、マージンやフォントサイズなどのスタイルが周囲の **TextCell** と合いません。合わせるにしても、iOS と Android とではスタイルが微妙に違うこともあって、スタイルを作りこむのは少々面倒です。

そこで、**Detail** に複数行の文字列を表示できる **TextCell** を[カスタム レンダラー](https://docs.microsoft.com/ja-jp/xamarin/xamarin-forms/app-fundamentals/custom-renderer/)で作ろうと思い立ちました。そちらの方が面倒ではないかという意見もあるやもしれませんが、あるいは、既にどこかに似たようなものがあるかもしれませんが、ご容赦ください。

## 準備

[Xamarin.Forms のソース](https://github.com/xamarin/Xamarin.Forms) の **TextCellRenderer** の実装などを見て作戦を練ります。

- iOS
  - **TextCellRenderer** の **DetailTextLabel** に該当するプロパティがありそう。
  - [iOS: UITableView cells with multiple lines?](https://stackoverflow.com/questions/5964448/ios-uitableview-cells-with-multiple-lines/30959936) - stackoverflow
- Android
  - **BaseCellView** に実装の中に `_DetailText.SetSingleLine(true)` とあるので、この値を _false_ に変更すれば良さそう。
  - [TextView SetSingleLine](https://developer.android.com/reference/android/widget/TextView.html#attr_android:singleLine) - Android Developers

Android の方ですが、 **_DetailText** が private であるため外から触ることができません。ググったところ、`GetChildAt()` を使ってアクセスできることが分かりました。次の記事を参考にしました。

- [Xamarin.Forms CustomRenderer for a TextCell](https://stackoverflow.com/questions/25810660/xamarin-forms-customrenderer-for-a-textcell) - stackoverflow

## 成果物

共通部分は空っぽです。

```/App1/App1/MultilineTextCell.cs
using Xamarin.Forms;
namespace App1
{
    public class MultilineTextCell : TextCell
    {
    }
}
```

iOS のカスタムレンダラー:

```/App1/App1.iOS/MultilineTextCellRenderer.cs
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(App1.MultilineTextCell), typeof(App1.iOS.MultilineTextCellRenderer))]
namespace App1.iOS
{
    public class MultilineTextCellRenderer : TextCellRenderer
    {
        public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv)
        {
            var cell = base.GetCell(item, reusableCell, tv);
            cell.DetailTextLabel.Lines = 0;
            cell.DetailTextLabel.LineBreakMode = UILineBreakMode.WordWrap;
            return cell;
        }
    }
}
```

Android のカスタムレンダラー:

```/App1/App1.Android/MultilineTextCellRenderer.cs
using Android.Content;
using Android.Views;
using Android.Widget;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(App1.MultilineTextCell), typeof(App1.Droid.MultilineTextCellRenderer))]
namespace App1.Droid
{
    public class MultilineTextCellRenderer : TextCellRenderer
    {
        protected override Android.Views.View GetCellCore(Cell item, Android.Views.View convertView, ViewGroup parent, Context context)
        {
            var cell = (LinearLayout)base.GetCellCore(item, convertView, parent, context);
            var detailText = (TextView)(cell.GetChildAt(1) as LinearLayout).GetChildAt(1);
            detailText.SetSingleLine(false);
            return cell;
        }
    }
}
```

XAML で利用します。

```/App1/App1/Views/Page1.xaml
xmlns:local="clr-namespace:App1"
...
<TextCell Text="通常のTextCell" Detail="{Binding MultilineText}"/>
<local:MultilineTextCell Text="複数行テキスト" Detail="{Binding MultilineText}" />
```
iOS のスクリーンショット:
![iOS.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25692/5d385b32-d8f2-b488-2262-21b11ad5bec2.png)

Android のスクリーンショット:
![Android.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25692/bff0fe32-468e-12ed-0d86-76434497e1e0.png)

以上、[ツイートしながら](https://twitter.com/NakamuraYoichi/status/1199972518376824832) コーディングしていました。