xamarin设计时绑定无法解析数据上下文中的属性

时间:2015-10-29 17:36:12

标签: xamarin.forms design-time

我目前正在根据自己的需要修改ItemsView。我注意到我的实现存在缺陷:

与ListView不同,根据我当前的迭代元素,我不会获得智能感知。有谁知道如何实现这一目标?

以下是我的控制实施:

// http://adventuresinxamarinforms.com/2015/04/29/creating-a-xamarin-forms-accordion-control-without-custom-renders/
    public class ItemsView : Grid
    {
        protected ScrollView ScrollView;
        protected readonly StackLayout ItemsStackLayout;

        public ItemsView()
        {
            ScrollView = new ScrollView();
            ScrollView.SetBinding(ScrollOrientationProperty, new Binding(nameof(ScrollOrientation), mode: BindingMode.OneWay, source: this));

            ItemsStackLayout = new StackLayout
            {
                Padding = new Thickness(0),
                Spacing = 0,
                HorizontalOptions = LayoutOptions.FillAndExpand
            };
            ItemsStackLayout.SetBinding(StackOrientationProperty, new Binding(nameof(ItemsStackLayout), mode: BindingMode.OneWay, source: this));

            ScrollView.Content = ItemsStackLayout;
            Children.Add(ScrollView);

            SelectedCommand = new Command<object>(item =>
            {
                var selectable = item as ISelectable;
                if (selectable == null)
                    return;

                SetSelected(selectable);
                SelectedItem = selectable.IsSelected ? selectable : null;
            });
        }

        protected virtual void SetSelected(ISelectable selectable)
        {
            selectable.IsSelected = true;
        }

        public bool ScrollToStartOnSelected { get; set; }

        #region SelectedCommand

        public static BindableProperty SelectedCommandProperty = BindableProperty.Create<ItemsView, ICommand>(d => d.SelectedCommand, default(ICommand));

        public ICommand SelectedCommand
        {
            get { return (ICommand) GetValue(SelectedCommandProperty); }
            set { SetValue(SelectedCommandProperty, value); }
        }

        #endregion SelectedCommand

        #region ScrollOrientation

        public static BindableProperty ScrollOrientationProperty = BindableProperty.Create<ItemsView, ScrollOrientation>(d => d.ScrollOrientation, ScrollOrientation.Vertical);

        public ScrollOrientation ScrollOrientation
        {
            get { return (ScrollOrientation) GetValue(ScrollOrientationProperty); }
            set { SetValue(ScrollOrientationProperty, value); }
        }

        #endregion ScrollOrientation

        #region StackOrientation

        public static BindableProperty StackOrientationProperty = BindableProperty.Create<ItemsView, StackOrientation>(d => d.StackOrientation, StackOrientation.Vertical);

        public StackOrientation StackOrientation
        {
            get { return (StackOrientation) GetValue(StackOrientationProperty); }
            set { SetValue(StackOrientationProperty, value); }
        }

        #endregion StackOrientation

        public event EventHandler SelectedItemChanged;

        public static readonly BindableProperty ItemsSourceProperty =
            BindableProperty.Create<ItemsView, IEnumerable>(p => p.ItemsSource, default(IEnumerable<object>), BindingMode.OneWay, null, ItemsSourceChanged);

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

        public static readonly BindableProperty SelectedItemProperty =
            BindableProperty.Create<ItemsView, object>(p => p.SelectedItem, default(object), BindingMode.TwoWay, null, OnSelectedItemChanged);

        public object SelectedItem
        {
            get { return (object)GetValue(SelectedItemProperty); }
            set { SetValue(SelectedItemProperty, value); }
        }

        public static readonly BindableProperty ItemTemplateProperty =
            BindableProperty.Create<ItemsView, DataTemplate>(p => p.ItemTemplate, default(DataTemplate));

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

        private static void ItemsSourceChanged(BindableObject bindable, IEnumerable oldValue, IEnumerable newValue)
        {
            var itemsLayout = (ItemsView)bindable;
            itemsLayout.SetItems();

            var newObservableCasted = newValue as INotifyCollectionChanged;
            var oldObservableCasted = oldValue as INotifyCollectionChanged;
            if (newObservableCasted != null)
                newObservableCasted.CollectionChanged += itemsLayout.ItemsSourceCollectionChanged;
            if (oldObservableCasted != null)
                oldObservableCasted.CollectionChanged -= itemsLayout.ItemsSourceCollectionChanged;
        }

        private void ItemsSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
        {
            this.SetItems();
        }

        protected virtual void SetItems()
        {
            ItemsStackLayout.Children.Clear();

            if (ItemsSource == null)
                return;

            foreach (var item in ItemsSource)
            {
                var itemView = GetItemView(item);
                if (itemView == null)
                {
                    ItemsStackLayout.Children.Add(new Label()
                    {
                        Text = "ItemTemplate missing."
                    });
                    break;
                }
                ItemsStackLayout.Children.Add(itemView);
            }

            SelectedItem = ItemsSource.OfType<ISelectable>().FirstOrDefault(x => x.IsSelected);
        }

        protected virtual View GetItemView(object item)
        {
            if (ItemTemplate == null)
                return null;

            ItemTemplate.SetValue(BindingContextProperty, item);
            var content = ItemTemplate.CreateContent();
            var view = content as View;
            if (view == null)
                return null;

            var gesture = new TapGestureRecognizer
            {
                CommandParameter = item
            };
            gesture.SetBinding(TapGestureRecognizer.CommandProperty, (ItemsView v) => v.SelectedCommand, BindingMode.OneWay);

            AddGesture(view, gesture);

            return view;
        }

        protected void AddGesture(View view, TapGestureRecognizer gesture)
        {
            view.GestureRecognizers.Add(gesture);

            var layout = view as Layout<View>;

            if (layout == null)
                return;

            foreach (var child in layout.Children)
                AddGesture(child, gesture);
        }

        private static void OnSelectedItemChanged(BindableObject bindable, object oldValue, object newValue)
        {
            var itemsView = (ItemsView)bindable;
            if (newValue == oldValue)
                return;

            var selectable = newValue as ISelectable;
            itemsView.SetSelectedItem(selectable ?? oldValue as ISelectable);
        }

        protected virtual void SetSelectedItem(ISelectable selectedItem)
        {
            var items = ItemsSource;

            foreach (var item in items.OfType<ISelectable>())
                item.IsSelected = selectedItem != null && item == selectedItem && selectedItem.IsSelected;

            var handler = SelectedItemChanged;
            if (handler != null)
                handler(this, EventArgs.Empty);
        }
    }

我的观看模式:

public class InfoFieldsViewModel : HeaderedViewModel
{
    public override Task NavigatedToAsync(object state)
    {
        base.NavigatedToAsync(state);

        var casted = state as SiteSelectionEntry;
        if (casted != null)
        {
            HeaderText = $" < {casted.Name}";
        }

        Groups.IsEventNotificationEnabled = false;

        var group = new InfoFieldGroupViewModel("Gruppe 1");
        group.Items.Add(new InfoFieldDetailViewModel("Label1"));
        group.Items.Add(new InfoFieldDetailViewModel("Label2"));
        group.Items.Add(new InfoFieldDetailViewModel("Label3"));
        Groups.Add(group);

        group = new InfoFieldGroupViewModel("Gruppe 2");
        group.Items.Add(new InfoFieldDetailViewModel("Label4"));
        group.Items.Add(new InfoFieldDetailViewModel("Label5"));
        group.Items.Add(new InfoFieldDetailViewModel("Label6"));
        Groups.Add(group);

        Groups.IsEventNotificationEnabled = true;
        Groups.RaiseUpdate();

        return Done;
    }

    private ExtendedObservableCollection<InfoFieldGroupViewModel> _groups = new ExtendedObservableCollection<InfoFieldGroupViewModel>();

    public ExtendedObservableCollection<InfoFieldGroupViewModel> Groups
    {
        get { return GetValue(ref _groups); }
        set { SetValue(ref _groups, value); }
    }
}

public class EditableViewModel : ViewModelBase
{
    private bool _isInEditMode = new bool();

    public bool IsInEditMode
    {
        get { return GetValue(ref _isInEditMode); }
        set { SetValue(ref _isInEditMode, value); }
    }
}

public class InfoFieldGroupViewModel : ViewModelBase
{
    public InfoFieldGroupViewModel()
    {
        IsExpanded = true;
    }

    public InfoFieldGroupViewModel(string groupName)
    {
        _groupName = groupName;
    }

    private bool _isExpanded = new bool();

    public bool IsExpanded
    {
        get { return GetValue(ref _isExpanded); }
        set { SetValue(ref _isExpanded, value); }
    }

    private string _groupName;

    public string GroupName
    {
        get { return _groupName; }
        set { SetValue(ref _groupName, value); }
    }

    private ExtendedObservableCollection<InfoFieldDetailViewModel> _items = new ExtendedObservableCollection<InfoFieldDetailViewModel>();

    public ExtendedObservableCollection<InfoFieldDetailViewModel> Items
    {
        get { return GetValue(ref _items); }
        set { SetValue(ref _items, value); }
    }
}

public class InfoFieldDetailViewModel : EditableViewModel
{
    public InfoFieldDetailViewModel()
    {
    }

    public InfoFieldDetailViewModel(string label)
    {
        _label = label;
    }

    private string _label;

    public string Label
    {
        get { return _label; }
        set { SetValue(ref _label, value); }
    }
}

使用控件的视图:

<Grid BackgroundColor="{x:Static resources:Colors.DefaultBackground}">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <custom:ApplicationHeader
            HeaderText="{Binding HeaderText}"
            HeaderTapCommand="{Binding NavigatorBackCommand}"
            HomeButtonCommand="{Binding NavigatorBackCommand}"/>
        <Grid Row="1" custom:GridExtensions.IsBusy="{Binding IsBusy}">
            <custom:ItemsView ItemsSource="{Binding Groups}">
                <custom:ItemsView.ItemTemplate>
                    <DataTemplate>
                        <Label MinimumHeightRequest="30" Text="{Binding GroupName}"></Label>
                    </DataTemplate>
                </custom:ItemsView.ItemTemplate>
            </custom:ItemsView>
        </Grid>
    </Grid>

设计时错误的屏幕截图:

Picture with error

奇怪的是,普通的xamarin.forms listview似乎可以毫不费力地在这里设计时间正确并将子datacontext映射到项模板中。

是否有一些属性让我错过了让它发挥作用?或者我在实施中做错了什么导致这种失败?模板本身渲染得很好。所以这只是设计时间弄错了。

欢迎任何想法。到目前为止,我的绑定上下文重定向都没有成功。

0 个答案:

没有答案