miso_soup3 Blog

主に ASP.NET 関連について書いています。

日記 3 BindingContext を子へ伝播したい

日記。

Xamarin.Forms で、CustomRenderer として、このように SementedControl(親)と SegmentedControlOption(子)コントロールを作っていた。

f:id:miso_soup3:20170120170451p:plain

子のバインディング(SementedControl.Text)で失敗するケースがあって、
{Binding Hoge} だとバインディングできず、
{Binding Source={x:Reference aaa}, Path=BindingContext.Hoge}で明示するとバインディンが成功した。


で、デバッグをしてみると、子の SegmentedControlOption の Parent も BindingContext も null だった。
Xamarin.Forms では、一般的に親の BindingContext は子に伝播されると聞く。そして ListView では、ItemSource に指定したコレクションの要素以外を BindingContext に指定したい場合は、{x:Reference aaa} みたいに指定する。

この違いは、何だろう? 何の違いにより、親から子へ伝播する・しない が決まるのだろう?
これを解決したく、調べたり、人に聞いたりした。
私はてっきり、親も子も View を継承しているし、IViewContainer も宣言してるし、適当に?伝播するのだろうと予想していたが違っていた。

Layout を継承するとうまくいく。

SegmentedControl にて、View を継承するのではなく、Layout を継承すると、SegmentedControlOption(子)には、Parent も BindingContext もちゃんと設定されていた。
しかし、Layout を継承した場合は、LayoutChildren を override しなければならず、無駄な実装が増えてしまう(return; しておいても一見うまくいったけど)。

OnBindingContextChanged で設定する

Table セクションの実装を参照し、次のように、子の BindingContext に値を設定すれば、バインディングできた。

Xamarin.Forms/TableSection.cs at 74cb5c4a97dcb123eb471f6b1dffa1267d0305aa · xamarin/Xamarin.Forms · GitHub

protected override void OnBindingContextChanged()
{
	base.OnBindingContextChanged();
			
	foreach (SegmentedControlOption child in Children)
	{
		SetInheritedBindingContext(child, BindingContext);
	}
}

BindableObject.cs の SetInheritedBindingContext:https://github.com/xamarin/Xamarin.Forms/blob/20e2e12dce2f81b92e8682f128cd81e08469f485/Xamarin.Forms.Core/BindableObject.cs#L94

結局?

Xamarin.Forms のコントロールを見ていると、自分で子の BindingContext を設定しているところが多いように見えた。
okazuki さんの Xamarin 入門を読んでいても、Behavior の BindingContext は自分で設定しないといけない。
つまり、何かを用意しておけば子に伝播する、という仕組みは無く、自分のコントロールは自分でなんとかする(BindingContextをセットする)みたいだ。

でも、Layout.cs の実装の中に、子の BindingContext をなんとかする、という記述は見つけられず、 ObservableCollection な Children を持っているとだけしか読み取ることができなかった。

ありがたくも、欲しいコントロールは OSS によくある。が、”こういうプロパティを ViewModel のこんなプロパティとバインディングしたい” となったときは自分でカスタムしなければいけない、ということが多いように思う(まあそうだろうけど…)。

参照した SegmentedControl:
GitHub - alexrainman/SegmentedControl
GitHub - chrispellett/Xamarin-Forms-SegmentedControl: An example for how to implement a cross platform segmented control for iOS and Android using Xamarin Forms