How can I create a dynamic settings option with a treeview

Okay, I am trying to set up a settings page for my app. On this page I want the user to select a series of options for what fields to display on another page.
I have a settings system overloaded using the using Xamarin.Essentials; extensions, so I can handle lookup, generation, and clearing of various settings. I will then later in the target page use these functions to alter various IsVisible options.

When a switch is toggled (or eve checkbox checked unchecked) I want the StateChanged event to fire and run my custom callback using the dynamic data from the event to modify the internal stored settings for the app.

I am thinking that it should go something like this...

This is a dataclass to store a group of settings to be passed to the treeview.

public class SettingsOption : EventParent, INotifyPropertyChanged
{

    /// <summary>
    /// private storage Displayname
    /// </summary>
    private string displayname;

    /// <summary>
    /// private storage ImageIcon
    /// </summary>
    private ImageSource imageIcon;

    /// <summary>
    /// private storage PropertiesGroup
    /// </summary>
    private ObservableCollection<SettingsOption> propertiesGroup;

    /// <summary>
    /// private storage PropertyName
    /// </summary>
    private string propertyName;

    /// <summary>
    /// Gets or sets the value for Displayname
    /// </summary>
    public string Displayname
    {
        get => displayname;

        set
        {
            SetProperty(ref displayname, value, nameof(Displayname));
            OnPropertyChanged(nameof(Displayname));
        }
    }

    /// <summary>
    /// Gets or sets the value for ImageIcon
    /// </summary>
    public ImageSource ImageIcon
    {
        get => imageIcon;

        set
        {
            SetProperty(ref imageIcon, value, nameof(ImageIcon));
            OnPropertyChanged(nameof(ImageIcon));
        }
    }

    /// <summary>
    /// Gets or sets the value for PropertiesGroup
    /// </summary>
    public ObservableCollection<SettingsOption> PropertiesGroup
    {
        get => propertiesGroup;

        set
        {
            SetProperty(ref propertiesGroup, value, nameof(PropertiesGroup));
            OnPropertyChanged(nameof(PropertiesGroup));
        }
    }

    /// <summary>
    /// Gets or sets the value for PropertyName
    /// </summary>
    public string PropertyName
    {
        get => propertyName;

        set
        {
            SetProperty(ref propertyName, value, nameof(PropertyName));
            OnPropertyChanged(nameof(PropertyName));
        }
    }

    /// <summary>
    /// Defines the PropertyChanged
    /// </summary>
    public event PropertyChangedEventHandler PropertyChanged;
    /// <summary>
    /// The RaisedOnPropertyChanged
    /// </summary>
    /// <param name="_PropertyName"> The _PropertyName <see cref="string" /> </param>
    public void RaisedOnPropertyChanged(string _PropertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(_PropertyName));
    }
}


This is a function I created in order to make a lookup and toggle of the setting possible

     private bool BounceSetting(string settingName)      {             if (!AppSettingsFunctions.CheckKey(settingName))             {                 AppSettingsFunctions.SaveSettingData(settingNametrue);             }             else             {                 //Save the new setting as a process of the inverse of getting the existing data                 AppSettingsFunctions.SaveSettingData(settingName, !AppSettingsFunctions.GetBoolSetting(settingName));             }      }          

I want to then be able to setup the treeview like this.


    private void BuildSettingsTreegroups() { tvSettingsOptions.ChildPropertyName = "PropertiesGroup";            tvSettingsOptions.ItemTemplateContextType = ItemTemplateContextType.Node;            tvSettingsOptions.IsAnimationEnabled = true;            tvSettingsOptions.Indentation = 20;            tvSettingsOptions.AutoExpandMode = Syncfusion.TreeView.Engine.AutoExpandMode.RootNodesExpanded;            tvSettingsOptions.ItemsSource = SettingsGroup;            tvSettingsOptions.ItemTemplate = new DataTemplate(() => {
               var displayName = new Label { VerticalTextAlignment = TextAlignment.Center };                var slEntry = new StackLayout { Orientation = StackOrientation.Horizontal };                var optionIcon = new Image { HeightRequest = 26, WidthRequest = 26 };                var swSetting = new SfSwitch();                optionIcon.SetBinding(Image.SourceProperty, new Binding("Content.ImageIcon"));                displayName.SetBinding(Label.TextProperty, new Binding("Content.Displayname"));                swSetting.SetBinding(SfSwitch.IsOnProperty, AppSettingsFunction.GetBoolSetting("Content.PropertyName"));                swSetting.StateChanged += (se) => BounceSetting("Content.PropertyName");                slEntry.Children.Add(optionIcon);                slEntry.Children.Add(displayName);                slEntry.Children.Add(swSetting);                return slEntry;             });
}

The problem is that the swSetting binding. To get the current setting the app needs to call the settings function I overloaded in my AppSettingsFunction class.

With the above code I could theoretically build my settings page like this then building an ObservableCollection<SettingsOptions> object that I populate my settings into like this ...

     var mainOptionsgroup = new SettingsOption();
 
     var inventoryOptions = new SettingsOption();
     inventoryOptions.Displayname = "Select Inventory options to hide";
     inventoryOptions.ImageIcon = "InventoryIcon";
     inventoryOptions.PropertiesGroup.Add(new SettingsOption { Displayname = "Size", PropertyName = "inv_ShowSize" });
     inventoryOptions.PropertiesGroup.Add(new SettingsOption { Displayname = "SKU", PropertyName = "inv_SKU" });
     inventoryOptions.PropertiesGroup.Add(new SettingsOption { Displayname = "Price", PropertyName = "inv_Price" });
 
     var supplierPageOptions = new SettingsOption();
     supplierPageOptions.Displayname = "Select Supplier options to hide";
     supplierPageOptions.ImageIcon = "Suppliers";
     supplierPageOptions.PropertiesGroup.Add(new SettingsOption { Displayname = "Account#", PropertyName = "sup_Account" });
     supplierPageOptions.PropertiesGroup.Add(new SettingsOption { Displayname = "Phone Number", PropertyName = "sup_Phone" });
     supplierPageOptions.PropertiesGroup.Add(new SettingsOption { Displayname = "Address", PropertyName = "sup_Address" });
    mainOptionsgroup.Displayname = "Settings";
    mainOptionsgroup.PropertiesGroup.Add(inventoryOptions);
    mainOptionsgroup.PropertiesGroup.Add(supplierPageOptions);



Any ideas would be most appreciated!

Thanks!


2 Replies

JK Jesse Knott April 27, 2020 03:27 AM UTC

Well, it seems I figured it out, or at least a workaround that is working for me

Here is what I had to do to make this work

For the ItemsTemplate I simply made the sfSwitch use it's AutomationIdProperty hold the value of the property, then look it up using the lambda for calling BounceSetting(((SfSwitch)s).AutomationId);..

I added a Property to my SettingsOption class for LastState so that I can lookup the existing value and send it in the data.

tvSettingsOptions.ItemTemplate = new DataTemplate(() =>
            {
                try
                {
                    var slEntry = new StackLayout { Orientation = StackOrientation.Horizontal };

                    var optionIcon = new Image { HeightRequest = 26, WidthRequest = 26 };
                    var displayName = new Label { VerticalTextAlignment = TextAlignment.Center };
                    var swSetting = new SfSwitch();

                    optionIcon.SetBinding(Image.SourceProperty, new Binding("Content.ImageIcon"));
                    displayName.SetBinding(Label.TextProperty, new Binding("Content.Displayname"));
                    swSetting.SetBinding(SfSwitch.AutomationIdProperty, new Binding("Content.PropertyName"));

                    swSetting.SetBinding(SfSwitch.IsOnProperty, new Binding("Content.LastState"));

                    swSetting.StateChanged += (s, e) => BounceSetting(((SfSwitch)s).AutomationId);

                    slEntry.Children.Add(optionIcon);
                    slEntry.Children.Add(displayName);
                    slEntry.Children.Add(swSetting);

                    var grid = new Grid();
                    grid.Children.Add(slEntry);
                    return grid;
                }
                catch (Exception ex)
                {
                    Debug.WriteLine($"SettingsPage.xaml.cs->BuildSettingsTreegroups()->{ex.StackTrace}->{ex.Message}.{Environment.NewLine}{ex.InnerException}.{Environment.NewLine}{ex.Data}");
                    return new Label { Text = "ERROR" };
                }
            });


Thanks anyhow!


CS Chandrasekar Sampathkumar Syncfusion Team April 27, 2020 12:42 PM UTC

Hi Jesse, 
Thank you for using Syncfusion products. 
We have checked the reported query “How can I create a dynamic settings option with a treeview” from our end. Could you please tell us the operations performed in BounceSetting event in detail, so that it would be helpful for us to check on it and provide the solution at the earliest. 
Regards, 
Chandrasekar Sampathkumar 


Loader.
Up arrow icon