Forum Xamarin Xamarin.Forms

How to add several views in xaml to custom view?

igorkr_10igorkr_10 Member ✭✭✭✭
edited March 5 in Xamarin.Forms

Hi all.
I am creating custom control with Items property. Where Items must be a collection of views (like Children in StackLayout).
Obsolete info:

I do not plan to observe this collection so straight one time initialization of collection is enough for me.
How to do this properly?

[ContentProperty(nameof(Items))]
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class FloatingActionButton : ContentView
    {
 public static readonly BindableProperty ItemsProperty = BindableProperty.Create(nameof(Items), typeof(IList<FabItem>),
            typeof(FloatingActionButton), default(IList<FabItem>), BindingMode.TwoWay, propertyChanged: OnItemsChanged);

        public IList<FabItem> Items
        {
            get { return (IList<FabItem>)GetValue(ItemsProperty); }
            set { SetValue(ItemsProperty, value); }
        }
 
 private static void OnItemsChanged(BindableObject bindable, object oldValue, object newValue)
        {
            if (!(bindable is FloatingActionButton fab))
            {
                return;
            }
            IList<FabItem> fabItems = (IList<FabItem>)newValue;
            foreach (FabItem item in fabItems)
            {
                fab.StackLayout.Children.Add(item);
            }
        }
   }

<Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <StackLayout Grid.Row="0" x:Name="StackLayout" IsVisible="False" Opacity="0">
            </StackLayout>
            <Button Grid.Row="1" x:Name="Button" VerticalOptions="End" Opacity="0.8" HeightRequest="60" WidthRequest="60" CornerRadius="30" Clicked="OnButtonClicked">
                <Button.ImageSource>
                    <FileImageSource File="add.png"/>
                </Button.ImageSource>
            </Button>
        </Grid>

In my page i want to use this at following method:

<buttons:FloatingActionButton Margin="10"
                 AbsoluteLayout.LayoutFlags="PositionProportional"
         AbsoluteLayout.LayoutBounds="1, 1, AutoSize, AutoSize">
                <buttons:FabItem Text="1" ImageSource="add.png"/>
                <buttons:FabItem Text="2" ImageSource="add.png"/>
            </buttons:FloatingActionButton>

But I have a NullReferenceException at ending of InitializeComponent() method of Page which is telling me nothing.
I have initialized a collection in constructor and exception has gone. But I still don't know how to set those items to stackLayout's children.
I already checked XF sources but there is a couple of thing that I can't understand.

So, after all I have found solution (not so beautiful but working now).

I changed a IList to ObservableCollection:

public static readonly BindableProperty ItemsProperty = BindableProperty.Create(nameof(Items), typeof(ObservableCollection<FabItem>),
            typeof(FloatingActionButton), default(ObservableCollection<FabItem>), BindingMode.OneWay, propertyChanged: OnItemsChanged);

        public ObservableCollection<FabItem> Items
        {
            get { return (ObservableCollection<FabItem>)GetValue(ItemsProperty); }
            set { SetValue(ItemsProperty, value); }
        }

        public FloatingActionButton()
        {
            InitializeComponent();
            Items = new ObservableCollection<FabItem>();
            Items.CollectionChanged += this.OnItemsChanged;
        }

        private void OnItemsChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
            {
                StackLayout.Children.Add((View)e.NewItems[0]);
            }
        }

This works. If anyone knows how to do this in right way, please describe your solution.

Best Answer

Answers

Sign In or Register to comment.