Dynamically add UserControls to a Custom Xamarin ListView using a bindable property

Viewed 326

I have already created all of the controls, and have tested that the controls are added as needed without using the binding.

The issue is as soon as I add the binding which is supposed to determine which control to add, the controls stop working as needed.

The list view control will be populated from a collection of a class, which will have an indicator field to determine which control needs to be loaded. The list view contains a 2nd user control which basically acts as a placeholder for the correct control, it has a bindable property of type text which is set to determine the correct control to be loaded.

Here is the XAML Code for the list view control

    <ContentView.Content>
        <StackLayout>
            <Label Text="Binding Control Type"/>
            <Entry x:Name="cntName"/>
            <ListView  x:Name="GroupedView" GroupDisplayBinding="{Binding Title}" HasUnevenRows="True" GroupShortNameBinding="{Binding ShortName}" IsGroupingEnabled="True">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <ViewCell.ContextActions>
                                <MenuItem Text="Add Comment"/>
                                <MenuItem Text="Add Attachment"/>
                            </ViewCell.ContextActions>
                            <Grid>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="3*"/>
                                    <ColumnDefinition Width="7*"/>
                                </Grid.ColumnDefinitions>
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="3*"/>
                                    <RowDefinition Height="7*"/>
                                </Grid.RowDefinitions>
                                <Label Text="{Binding QUESTION_ID}" Grid.Row="0" Grid.Column="0" VerticalTextAlignment="Center" FontSize="Medium"/>
                                <Label Text="{Binding QUESTION_DETAILS}" Grid.Row="1" Grid.Column="0" VerticalTextAlignment="Center" FontSize="Medium"/>
                                <con:ucListViewControls ControlType="{Binding QUESTION_ANSWERCONTROL}" Grid.Row="1" Grid.Column="1"/>
                            </Grid>
                        </ViewCell>
                    </DataTemplate>
                </ListView.ItemTemplate>
                <ListView.GroupHeaderTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <ViewCell.ContextActions>
                                <MenuItem Text="Add Comment"/>
                                <MenuItem Text="Add Attachment"/>
                            </ViewCell.ContextActions>
                            <StackLayout Orientation="Horizontal" Padding="5,5,5,5" BackgroundColor="#E2F5F9">
                                <StackLayout.GestureRecognizers>
                                    <TapGestureRecognizer Command="{Binding Source={x:Reference this}, Path=Tapped}" CommandParameter="{Binding .}"/>
                                </StackLayout.GestureRecognizers>
                                <Button Image="{Binding StateIcon}" BackgroundColor="Transparent" BorderColor="Transparent" BorderWidth="0"/>
                                <Label Text="{Binding Title}" TextColor="#005569" FontSize="15" VerticalOptions="Center"/>
                            </StackLayout>
                        </ViewCell>
                    </DataTemplate>
                </ListView.GroupHeaderTemplate>
            </ListView>
        </StackLayout>
    </ContentView.Content>

And the code behind for the control

    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class ucExpandibleListView : ContentView
    {
        private ObservableCollection<dbQuestionGroup> _allGroups;
        private ObservableCollection<dbQuestionGroup> _expandedGroups;

        public ucExpandibleListView()
        {
            InitializeComponent();

            Tapped = new Command(x => HeaderTapped(x));

            _allGroups = new ObservableCollection<dbQuestionGroup>()
            {
                new dbQuestionGroup("Category 1", "C1", false)
                {
                    new dbQuestionModel() { QUESTION_ID = 1, QUESTION_DETAILS = "Testing Question 1", QUESTION_ANSWERCONTROL = "RBL" },
                    new dbQuestionModel() { QUESTION_ID = 2, QUESTION_DETAILS = "Testing Question 2", QUESTION_ANSWERCONTROL = "" }
                }
        };
            UpdateListContent();
        }

        private void UpdateListContent()
        {
            _expandedGroups = new ObservableCollection<dbQuestionGroup>();

            foreach (dbQuestionGroup group in _allGroups)
            {
                dbQuestionGroup newGroup = new dbQuestionGroup(group.Title, group.ShortName, group.Expanded);
                newGroup.QuestionCount = group.Count;

                if (group.Expanded)
                {
                    foreach (dbQuestionModel question in group)
                    {
                        newGroup.Add(question);
                    }
                }
                _expandedGroups.Add(newGroup);
            }
            GroupedView.ItemsSource = _expandedGroups;
        }

        public Command Tapped { get; set; }

        private void HeaderTapped(object group)
        {
            var groupCat = (dbQuestionGroup)group;
            int selectedIndex = _expandedGroups.IndexOf(groupCat);

            if (groupCat.Expanded)
            {
                _allGroups[selectedIndex].Expanded = false;
            }
            else
            {
                _allGroups.ToList().ForEach(x => x.Expanded = false);
                _allGroups[selectedIndex].Expanded = !_allGroups[selectedIndex].Expanded;
            }

            UpdateListContent();
        }
    }

Here is the XAML Code for the placeholder control

  <ContentView.Content>
      <StackLayout x:Name="stkPlaceholder">
      </StackLayout>
  </ContentView.Content>

And the code behind for the placeholder control

    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class ucListViewControls : ContentView, INotifyPropertyChanged
    {
        public ucListViewControls()
        {
            InitializeComponent();
        }

        #region Control Attributes
        public event PropertyChangedEventHandler PropertyChanged;

        protected void NotifyPropertyChanged(string info)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(info));
        }
        #endregion

        #region Bindable Properties
        public static readonly BindableProperty ControlTypeProperty = BindableProperty.Create(nameof(ControlType), typeof(string), typeof(ucListViewControls));

        public string ControlType
        {
            get
            {
                return (string)GetValue(ControlTypeProperty);
            }
            set
            {
                SetValue(ControlTypeProperty, value);
                AddControl();
                NotifyPropertyChanged("ControlType");
            }
        }
        #endregion

        public void AddControl()
        {
            switch (ControlType)
            {
                case "RBL":
                    ucRadiobuttons radiobuttons = new ucRadiobuttons();
                    radiobuttons.lblTitle1 = "Yes";
                    radiobuttons.lblTitle2 = "No";
                    radiobuttons.lblTitle3 = "N/A";

                    radiobuttons.OnColor1 = Color.Green;
                    radiobuttons.OnColor2 = Color.Red;
                    radiobuttons.OnColor3 = Color.Transparent;
                    stkPlaceholder.Children.Add(radiobuttons);
                    break;
                default:
                    Entry placeholder = new Entry();
                    stkPlaceholder.Children.Add(placeholder);
                    break;
            }
        }
    }

I have tested that the controls are added without the binding, which works perfectly.

I have tried to rewrite the bindable property multiple times in case I missed something, I also could not find any post relating to something similar which wouldve helped me.

Any clues?

The first screenshot shows the expected output, and the seconds screenshots shows what happens when the binding is applied.

enter image description hereenter image description here

dbQuestionModel:

using System.Collections.Generic;

namespace PivotMobile_BusinessLayer.Models
{
    public class dbQuestionModel
    {
        public int QUESTION_PK { get; set; }

        public int QUESTION_ID { get; set; }

        public string QUESTION_CATEGORY { get; set; }

        public string QUESTION_DETAILS { get; set; }

        public string QUESTION_TYPE { get; set; }

        public string QUESTION_ANSWERCONTROL { get; set; }

        public string QUESTION_COMMENT { get; set; }

        public List<string> QUESTION_ATTACHMENTS { get; set; }
    }
}

dbQuestionGroup:

using System.Collections.ObjectModel;
using System.ComponentModel;

namespace PivotMobile_BusinessLayer.Models
{
    public class dbQuestionGroup : ObservableCollection<dbQuestionModel>, INotifyPropertyChanged
    {
        public static ObservableCollection<dbQuestionGroup> All { private set; get; }

        private bool _expanded;

        public string Title { get; set; }

        public string ShortName { get; set; }

        public bool Expanded
        {
            get
            {
                return _expanded;
            }
            set
            {
                if (_expanded != value)
                {
                    _expanded = value;
                    OnPropertyChanged("Expanded");
                    OnPropertyChanged("StateIcon");
                }
            }
        }

        public string StateIcon
        {
            get
            {
                return Expanded ? "expanded_blue.png" : "collapsed_blue.png";
            }
        }

        public int QuestionCount { get; set; }

        public dbQuestionGroup(string title, string shortName, bool expanded = true)
        {
            Title = title;
            ShortName = shortName;
            Expanded = expanded;
        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

ListView Page XAML:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="PivotMobile.Views.ObservationsView"
             xmlns:con="clr-namespace:PivotMobile.Controls">
    <ContentPage.Content>
        <StackLayout Margin="5">
            <con:ucExpandibleListView/>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

ListView Page Code Behind:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace PivotMobile.Views
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class ObservationsView : ContentPage
    {
        public ObservationsView ()
        {
            InitializeComponent ();
        }
    }
}
0 Answers
Related