前回 で ItemsControl っぽいものを作りましたが、さらに ItemsPanel を切り替えるようにしてみます
修正対象は上記の通り
まずは ItemsControl
/// <summary> /// ItemsControl 風 View /// </summary> public class ItemsControl : ContentView { #region ItemsPanel /// <summary> /// ItemsPanel BindableProperty /// </summary> public static readonly BindableProperty ItemsPanelProperty = BindableProperty.Create<ItemsControl, Layout<View>>( p => p.ItemsPanel, new StackLayout(), BindingMode.OneWay, null, OnItemsPanelChanged); /// <summary> /// ItemsPanel CLR プロパティ /// </summary> public Layout<View> ItemsPanel { get { return (Layout<View>)this.GetValue(ItemsPanelProperty); } set { this.SetValue(ItemsPanelProperty, value); } } /// <summary> /// ItemsPanel 変更イベントハンドラ /// </summary> /// <param name="bindable">BindableObject</param> /// <param name="oldValue">古い値</param> /// <param name="newValue">新しい値</param> private static void OnItemsPanelChanged(BindableObject bindable, Layout<View> oldValue, Layout<View> newValue) { var control = bindable as ItemsControl; if (control == null) { return; } if (oldValue != null) { oldValue.Children.Clear(); } if (newValue == null) { return; } foreach (var item in control.ItemsSource) { var content = control.ItemTemplate.CreateContent(); View view; var cell = content as ViewCell; if (cell != null) { view = cell.View; } else { view = (View)content; } view.BindingContext = item; control.ItemsPanel.Children.Add(view); } control.Content = newValue; control.UpdateChildrenLayout(); control.InvalidateLayout(); } #endregion //ItemsPanel ~ 中略 ~ /// <summary> /// コンストラクタ /// </summary> public ItemsControl() { this.Content = this.ItemsPanel; } ~ 中略 ~ }
ItemsPanel の部分を変更しました
そして ViewModel
/// <summary> /// トップ画面の ViewModel /// </summary> public class TopPageViewModel : BindableBase { /// <summary> /// アイテム /// </summary> private ObservableCollection<ItemViewModel> items; /// <summary> /// アイテム /// </summary> public ObservableCollection<ItemViewModel> Items { get { return this.items; } set { this.SetProperty<ObservableCollection<ItemViewModel>>(ref this.items, value); } } /// <summary> /// コンストラクタ /// </summary> public TopPageViewModel() { this.items = new ObservableCollection<ItemViewModel>() { new ItemViewModel() { Content = "Top", XConstraint = Constraint.RelativeToParent((p) => { return p.Width / 2 - 30; }), YConstraint = Constraint.Constant(0), }, new ItemViewModel() { Content = "Left", XConstraint = Constraint.Constant(0), YConstraint = Constraint.RelativeToParent((p) => { return p.Height / 2 - 40; }), }, new ItemViewModel() { Content = "Right", XConstraint = Constraint.RelativeToParent((p) => { return p.Width - 100; }), YConstraint = Constraint.RelativeToParent((p) => { return p.Height / 2 - 40; }), }, new ItemViewModel() { Content = "Bottom", XConstraint = Constraint.RelativeToParent((p) => { return p.Width / 2 - 50; }), YConstraint = Constraint.RelativeToParent((p) => { return p.Height - 40; }), }, }; } }
ちょっとデータを変えてます
ItemViewModel はプロパティのみのただの ViewModel なので割愛
最後に 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:b="clr-namespace:XamarinControl.Behaviors;assembly=XamarinControl" xmlns:c="clr-namespace:XamarinControl.Controls;assembly=XamarinControl" xmlns:vm="clr-namespace:XamarinControl.ViewModels;assembly=XamarinControl" x:Class="XamarinControl.Views.TopPage"> <ContentPage.BindingContext> <vm:TopPageViewModel/> </ContentPage.BindingContext> <c:ItemsControl ItemsSource ="{Binding Items}"> <c:ItemsControl.ItemsPanel> <RelativeLayout/> </c:ItemsControl.ItemsPanel> <c:ItemsControl.ItemTemplate> <DataTemplate> <Grid Padding="20,10" RelativeLayout.XConstraint="{Binding XConstraint}" RelativeLayout.YConstraint="{Binding YConstraint}"> <Label Text="{Binding Content}"/> </Grid> </DataTemplate> </c:ItemsControl.ItemTemplate> </c:ItemsControl> </ContentPage>
デフォルトの StackLayout の代わりに RelativeLayout を設定しています
これで実行すると・・・
相対位置でレイアウトされるようになりました