Custom Control parameters binding order

DottorPagliacciusDottorPagliaccius ✭✭ITMember ✭✭

Hi to everyone, this is my first post here.

Some time ago I was looking for a Repeater-like control in XF, and I finally get this http://www.qimata.com/?p=7671, very simple indeed. I then started the usual "why don't add this, why don't add that" and so I added other properties and templates. Now, the control works very well for now, but I have a problem (apart from this, I don't think that is the best way to handle this scenario, if you have advice please share your thoughts).

All the logic is in the ItemsChanged event, that fires when ItemSource property is bound. Now, if I don't write the property for last, when event fires the other are yet to be evaluated. For example, this

<local:RepeaterView ShowSeparator="false" ItemsSource="{Binding itemsource}">

is not the same of

<local:RepeaterView ItemsSource="{Binding itemsource}" ShowSeparator="false">

Only in the first case property ShowSeparator has the expected value, because ItemsChanged fires before parameter initialization. Now, caring about parameters' order is not acceptable, then how I can handle this in a more decently manner?

 public class RepeaterView : StackLayout
    {
        public static readonly BindableProperty ItemTemplateProperty = BindableProperty.Create(nameof(ItemTemplate), typeof(DataTemplate), typeof(RepeaterView), default(DataTemplate));
        public static readonly BindableProperty HeaderTemplateProperty = BindableProperty.Create(nameof(HeaderTemplate), typeof(DataTemplate), typeof(RepeaterView), default(DataTemplate));
        public static readonly BindableProperty EmptyTextTemplateProperty = BindableProperty.Create(nameof(EmptyTextTemplate), typeof(DataTemplate), typeof(RepeaterView), default(DataTemplate));
        public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create(nameof(ItemsSource), typeof(ICollection), typeof(RepeaterView), new List<object>(), BindingMode.OneWay, null, propertyChanged: (bindable, oldValue, newValue) => { ItemsChanged(bindable, (ICollection)oldValue, (ICollection)newValue); });

        public static readonly BindableProperty EmptyTextProperty = BindableProperty.Create(nameof(EmptyText), typeof(string), typeof(RepeaterView), string.Empty);

        public static readonly BindableProperty SelectedItemCommandProperty = BindableProperty.Create(nameof(SelectedItemCommand), typeof(ICommand), typeof(RepeaterView), default(ICommand));

        public ICollection ItemsSource
        {
            get { return (ICollection)GetValue(ItemsSourceProperty); }
            set { SetValue(ItemsSourceProperty, value); }
        }

        public DataTemplate ItemTemplate
        {
            get { return (DataTemplate)GetValue(ItemTemplateProperty); }
            set { SetValue(ItemTemplateProperty, value); }
        }

        public DataTemplate HeaderTemplate
        {
            get { return (DataTemplate)GetValue(HeaderTemplateProperty); }
            set { SetValue(HeaderTemplateProperty, value); }
        }

        public DataTemplate EmptyTextTemplate
        {
            get { return (DataTemplate)GetValue(EmptyTextTemplateProperty); }
            set { SetValue(EmptyTextTemplateProperty, value); }
        }

        public string EmptyText
        {
            get { return (string)GetValue(EmptyTextProperty); }
            set { SetValue(EmptyTextProperty, value); }
        }

        public ICommand SelectedItemCommand
        {
            get { return (ICommand)GetValue(SelectedItemCommandProperty); }
            set { SetValue(SelectedItemCommandProperty, value); }
        }

        public bool ShowSeparator { get; set; } = true;

        private static void ItemsChanged(BindableObject bindable, ICollection oldValue, ICollection newValue)
        {
            var repeater = (RepeaterView)bindable;

            repeater.Children.Clear();

            var headerTemplate = repeater.HeaderTemplate;

            if (headerTemplate != null)
            {
                var content = headerTemplate.CreateContent();
                if (!(content is View) && !(content is ViewCell))
                {
                    //throws exception
                }

                var view = (content is View) ? content as View : ((ViewCell)content).View;

                repeater.Children.Add(view);
                repeater.Children.Add(new Divider());
            }

            if (newValue.Count == 0 && (repeater.EmptyTextTemplate != null || !string.IsNullOrEmpty(repeater.EmptyText)))
            {
                if (repeater.EmptyTextTemplate == null)
                    repeater.Children.Add(new Label { Text = repeater.EmptyText });
                else
                {
                    var content = repeater.EmptyTextTemplate.CreateContent();
                    if (!(content is View) && !(content is ViewCell))
                    {
                        //throws exception
                    }

                    var view = (content is View) ? content as View : ((ViewCell)content).View;

                    repeater.Children.Add(view);
                }

                return;
            }

            var dataTemplate = repeater.ItemTemplate;

            foreach (object item in newValue)
            {
                var content = dataTemplate.CreateContent();
                if (!(content is View) && !(content is ViewCell))
                {
                    //throws exception
                }

                var view = (content is View) ? content as View : ((ViewCell)content).View;

                if (repeater.SelectedItemCommand != null)
                {
                    var tapGestureRecognizer = new TapGestureRecognizer();

                    tapGestureRecognizer.Tapped += (sender, e) => { repeater.SelectedItemCommand?.Execute(item); };

                    view.GestureRecognizers.Add(tapGestureRecognizer);
                }

                view.BindingContext = item;

                repeater.Children.Add(view);

                if (repeater.ShowSeparator)
                    repeater.Children.Add(new Divider { Margin = new Thickness(5, 0) });
            }
        }
    }
}

Answers

Sign In or Register to comment.