自定义控件参数绑定顺序

时间:2016-07-25 10:17:42

标签: data-binding xamarin xamarin.forms custom-controls

原谅在xamarin论坛中的交叉发布,但没有人在那里回答我。

前段时间我在XF中寻找类似Repeater的控件,我终于得到了这个http://www.qimata.com/?p=7671,非常简单。然后我开始平常"为什么不添加这个,为什么不添加"所以我添加了其他属性和模板。现在,控件现在运行得很好,但我有一个问题(除此之外,我不认为这是处理这种情况的最佳方式,如果你有建议请分享你的想法)。

所有逻辑都在ItemsChanged事件中,该事件在绑定ItemSource属性时触发。现在,如果我不写最后一个属性,当事件触发时,另一个尚未被评估。例如,这个

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

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

仅在第一种情况下,属性ShowSeparator具有期望值,因为ItemsChanged在参数初始化之前触发。现在,关心参数&#39;订单是不可接受的,那么我怎样才能以更体面的方式处理这个问题?

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) });
            }
        }
    }
}

1 个答案:

答案 0 :(得分:0)

这里最好的策略是确保只有在真正要求时才会首先计算这些项目(例如在LayoutChildren中)。

所以在OnItemSourceChanged中,你只设置了ItemSource,但除非已经进行了初始化,否则不要做任何事情。

应该看起来像这样(pseude-code):

private static void ItemsChanged(...)
{
    var repeater = (Repeaterview)bindable;

    repeater.ItemsSource = value;

    if(repeater.IsInitialized) UpdateItems();
}


private override void LayoutChildren()
{
    IsInitialized = true;

    UpdateItems();
}

这是基本策略。一旦找到时间,我将更新到正确的方法/覆盖。如果你碰巧在我面前找到答案,请随时更新这个答案。