'xamarin design time binding cannot resolve property in data context

i'm currently in the process of modifying an ItemsView according to my needs. I noticed on flaw in my implementation however:

Unlike ListView i don't get intellisense according to my current iteration element. Does anyone know how to make that happen?

Here's my control implementation:

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

my viewmodels:

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

The view which uses the controls:

<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>

Screenshot of designtime error:

Picture with error

Oddly enough an ordinary xamarin.forms listview seems to have no trouble getting the design time correct here and mapping the child datacontext within the item template.

Is there some attribute i'm missing out on to make it work? Or am i doing something wrong in my implementation which makes this fail? The template itself renders just fine. So it's just the design time getting it wrong here.

Any ideas welcome. So far none of my binding context redirects worked successfully.



Solution 1:[1]

For me, it was a design-time DataType specification added on page level:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:viewModels="clr-namespace:**;assembly=**"
             xmlns:bindingConverters="clr-namespace:**;assembly=**"
             x:DataType="viewModels:WelcomeViewModel" <!-- HERE-->
             x:Class="**.WelcomePage"
             Title="{Binding Title}">
   <ContentPage.Content >
       <CollectionView ItemsSource="{Binding Items}">
                <CollectionView.ItemTemplate>
                    <DataTemplate>
                                <Label Grid.Row="0" Grid.Column="0" Text="{Binding Name}"/> <!-- this binding was looking for property 'Name' on root level, which is 'WelcomeViewModel' -->
                    </DataTemplate>
                </CollectionView.ItemTemplate>
         </CollectionView>
    </ContentPage.Content>
</ContentPage>

So, I've just removed x:DataType="viewModels:WelcomeViewModel" and it started working.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 LaoR