Xamarin.Formsでコントロールのサイズを縦横比で指定する
コントロールのサイズを縦,横それぞれにピクセルの値を指定するのではなく,縦を決めれば自動的に横が縦の2分の1のサイズになるようにしたい,と思いました.例えばです.
名刺と同じ縦横比のカード風コントロールを作成するとします.
日本の名刺の一般的なサイズは横×縦=91mm × 55mm
ですので,この比率に合わせてFrame
コントロールのWidthRequest
とHeightRequest
という2つのプロパティにサイズを指定すれば,名刺らしいコントロールが作れることになります.
しかし,少し調整するたびにこの比率を計算するのは手間です.縦横比は固定なのだから,横サイズを決めたら縦サイズが決まってほしいものです.
考えて思いついたのが,以下の2つです.もっと色々あるはずですが,できるだけ簡単に済ませたいのです.
x:Referenceを使う
データバインディングのバインド先としてコントロールのプロパティを参照するものです.
コントロールに名前(x:Name属性)をつけておき,その名前を使ってプロパティを参照します.
以下の例ではButtonAと名前がつけられたコントロールの横幅(WidthRequest)を,ButtonBのWidthRequestプロパティで参照しています.
<Button x:Name="ButtonA" HorizontalOptions="Center" Text="ButtonA" VerticalOptions="Center" WidthRequest="100" /> <Button x:Name="ButtonB" HorizontalOptions="Center" Text="ButtonB" VerticalOptions="Center" WidthRequest="{Binding Source={x:Reference ButtonA}, Path=WidthRequest}" /><!-- これ -->
このように幅が同じボタンができます.
もちろん自分のプロパティを参照することもできます.
以下のように高さ(HeightRequest)に自分のWidthRequestを参照するように指定することができます.
<Button x:Name="ButtonC" HeightRequest="{Binding Source={x:Reference ButtonC}, Path=Width}" HorizontalOptions="Center" Text="ButtonC" VerticalOptions="Center" WidthRequest="100" />
このように正方形のボタンになります.
他のプロパティの値をバインドできることがわかりましたが,本来の目的である比率で指定することはXAMLだけではできないようです.
やるとしたらConverterを使って以下のようにする必要があります.
上記記事を参考にしてコンバータを作ります.とてもわかり易い内容でした.ありがとうございます. こちらの記事はXAMLから倍率をパラメータとしてConverterに渡すことについても書かれていて,IValueConverterについて詳しく学べると思います.
バインドされた値を2倍にする単純なコンバーターです.
class AspectRatioConverter : IValueConverter,IMarkupExtension { //バインドされた値に処理を加えて返す. public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { //値を2倍にして返す return (double)value*2; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } public object ProvideValue(IServiceProvider serviceProvider) { return this; } }
このコンバーターを使ってXAML側でこんなボタンを配置します.
<Button x:Name="ButtonD" HeightRequest="{Binding Source={x:Reference ButtonD}, Path=WidthRequest, Converter={converter:AspectRatioConverter}}" HorizontalOptions="Center" Text="ButtonD" VerticalOptions="Center" WidthRequest="100" />
ButtonDは高さが幅の2倍ある縦長のボタンになります.
ということで,コンバーター内の倍率を名刺の比率に変更すれば,名刺比率のコントロールになります.
今回のサンプルでは横(指定)に対して縦(比率で自動計算)というものなので,わかりにくくて申し訳ありませんが横縦比=0.6043を使います.
RelativeLayoutを使う
次に思いついたのがRelativeLayout
を使う方法です.
RelativeLayoutコントロールの中では,コントロールの位置,サイズに対して相対的な指定ができます.
例えば,「コントロールAの横幅をコントロールBの横幅の2倍にする」や「コントロールAの位置をコントロールBの位置から右へ30,下へ50移動した位置にする」といった相対的なレイアウトができます.
しかしながら,これは自身のプロパティを参照することはできないようです.
<RelativeLayout Padding="0" BackgroundColor="Pink"> <Button x:Name="ButtonA" HorizontalOptions="Center" Text="ButtonA" VerticalOptions="Center" WidthRequest="100" /> <Button x:Name="ButtonE" RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToView, ElementName=ButtonE, Property=Width, Factor=0.6043}" Text="ButtonE" WidthRequest="250" /> </RelativeLayout>
参照先のコントロールとしてElementNameに自分自身を指定すると,以下のメッセージとともに例外が発生します.
Constraints as specified contain an unsolvable loop
残念.
結論
Converterを使うのが良いということかなぁ.