Create bindable properties for Treeview in Xamarin Forms

334 Views Asked by At

I needed to use a Treeview in my xamarin forms application, however the only existing TreeView on the net are not free (Syncfusion and Telerik).

So I found this very interesting project : https://github.com/AdaptSolutions/Xamarin.Forms-TreeView

the only problem that I found is that the ItemSource and SelectedItem properties are not bindable and therefor I can't use it on an MVVM Pattern. Which brings us to my question, How can I make them bindable.

I tried to follow this documentation : https://learn.microsoft.com/en-us/xamarin/xamarin-forms/xaml/bindable-properties

but still nothing. Can anyone help me with that please ? Thank you

UPDATE :

this is the TreeView class :

public class TreeView : ScrollView
    {
        #region Fields
        private readonly StackLayout _StackLayout = new StackLayout { Orientation = StackOrientation.Vertical };

        //TODO: This initialises the list, but there is nothing listening to INotifyCollectionChanged so no nodes will get rendered
        private IList<TreeViewNode> _RootNodes = new ObservableCollection<TreeViewNode>();
        private TreeViewNode _SelectedItem;
        #endregion

        #region Public Properties

        public TreeViewNode SelectedItem
        {
            get => _SelectedItem;

            set
            {
                if (_SelectedItem == value)
                {
                    return;
                }

                if (_SelectedItem != null)
                {
                    _SelectedItem.IsSelected = false;
                }

                _SelectedItem = value;

                SelectedItemChanged?.Invoke(this, new EventArgs());
            }
        }

      
        public IList<TreeViewNode> RootNodes
        {
            get => _RootNodes;
            set
            {
                _RootNodes = value;

                if (value is INotifyCollectionChanged notifyCollectionChanged)
                {
                    notifyCollectionChanged.CollectionChanged += (s, e) =>
                    {
                        RenderNodes(_RootNodes, _StackLayout, e, null);
                    };
                }

                RenderNodes(_RootNodes, _StackLayout, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset), null);
            }
        }

        #endregion


        #region Constructor
        public TreeView()
        {
            Content = _StackLayout;
        }
        #endregion

        

        #region Private Static Methods
        private static void AddItems(IEnumerable<TreeViewNode> childTreeViewItems, StackLayout parent, TreeViewNode parentTreeViewItem)
        {
            foreach (var childTreeNode in childTreeViewItems)
            {
                if (!parent.Children.Contains(childTreeNode))
                {
                    parent.Children.Add(childTreeNode);
                }

                childTreeNode.ParentTreeViewItem = parentTreeViewItem;
            }
        }
        #endregion

        

        #region Internal Static Methods
        internal static void RenderNodes(IEnumerable<TreeViewNode> childTreeViewItems, StackLayout parent, NotifyCollectionChangedEventArgs e, TreeViewNode parentTreeViewItem)
        {
            if (e.Action != NotifyCollectionChangedAction.Add)
            {

                AddItems(childTreeViewItems, parent, parentTreeViewItem);
            }
            else
            {
                AddItems(e.NewItems.Cast<TreeViewNode>(), parent, parentTreeViewItem);
            }
        }
        #endregion
    }

so what Im trying to do here is making RootNodes bindable as well as SelectedItem afterwards.

What I did is simply adding this, thinking it should work but obviously it does not :

public static readonly BindableProperty RootNodesProperty =
            BindableProperty.Create(nameof(RootNodes), typeof(IList<TreeViewNode>), typeof(TreeView));

        public IList<TreeViewNode> RootNodes
        {
            get => (IList<TreeViewNode>)GetValue(RootNodesProperty);
            set
            {
                SetValue(RootNodesProperty, value);
                _RootNodes = value;
                if (value is INotifyCollectionChanged notifyCollectionChanged)
                {
                    notifyCollectionChanged.CollectionChanged += (s, e) =>
                    {
                        RenderNodes(_RootNodes, _StackLayout, e, null);
                    };
                }

                RenderNodes(_RootNodes, _StackLayout, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset), null);
            }
        }

UPDATE 2 : Here is what it looks like enter image description here

Hope this helps

1

There are 1 best solutions below

2
On

It seems you will not need to create custom ItemSource and SelectedItem in ScrollView, because Xamarin Foms has Bindable Layouts that contains ItemsSource and ItemTemplateSelector .

Bindable layouts enable any layout class that derives from the Layout class to generate its content by binding to a collection of items, with the option to set the appearance of each item with a DataTemplate. Bindable layouts are provided by the BindableLayout class, which exposes the following attached properties:

  • ItemsSource – specifies the collection of IEnumerable items to be displayed by the layout.
  • ItemTemplate – specifies the DataTemplate to apply to each item in the collection of items displayed by the layout.
  • ItemTemplateSelector – specifies the DataTemplateSelector that will be used to choose a DataTemplate for an item at runtime.

If you need to use ScrollView, sample code as follows:

<ScrollView>
 <StackLayout BindableLayout.ItemsSource="{Binding User.TopFollowers}"
             Orientation="Horizontal"
             ...>
    <BindableLayout.ItemTemplate>
        <DataTemplate>
            <controls:CircleImage Source="{Binding}"
                                  Aspect="AspectFill"
                                  WidthRequest="44"
                                  HeightRequest="44"
                                  ... />
        </DataTemplate>
    </BindableLayout.ItemTemplate>
 </StackLayout>
</ScrollView>