Fernando Ares
Fernando Ares

Reputation: 33

How to get listview children items and remove a piece of code dynamically from code behind

I'm struggling with Xamarin frames taking extra space when "showing/hiding" content into them. I've seen several related posts and implemented content templates which didn't solve it.

Basically I have a detailed view containing a Syncfusion ListView. Withing such ListView I'm rendering cards that should be dynamic, some of them contains a chart, some others not, cards contain different info based on category.

Here is my DetailCard implementation:

    public class DetailCard : BaseCard
    {
        public bool ChartDataVisible { get; set; }
        public string detailsList;

        public DetailCard()
        {
            ChartDataVisible = false;
        }
    }

    public class BaseCard : INotifyPropertyChanged
    {
        #region Field

        private IReadOnlyCollection<ChartModel> chartData;

        #endregion

        #region Events

        /// <summary>
        /// The declaration of the property changed event.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        #region Property

        /// <summary>
        /// Gets or sets the property that has been displays the Category.
        /// </summary>
        public string Category { get; set; }

        /// <summary>
        /// Gets or sets the property that has been displays the Category value.
        /// </summary>
        public string CategoryValue { get; set; }

        /// <summary>
        /// Gets or sets the property that has been displays the Category percentage.
        /// </summary>
        public string CategoryPercentage { get; set; }

        /// <summary>
        /// Gets or sets the property that has been bound with SfChart Control, which displays the health care data visualization.
        /// </summary>
        public IReadOnlyCollection<ChartModel> ChartData
        {
            get
            {
                return this.chartData;
            }

            set
            {
                if (this.chartData == value)
                {
                    return;
                }

                this.chartData = value;
                this.OnPropertyChanged("ChartData");
            }
        }

        /// <summary>
        /// Gets or sets the property that has been displays the background gradient start.
        /// </summary>
        public string BackgroundGradientStart { get; set; }

        /// <summary>
        /// Gets or sets the property that has been displays the background gradient end.
        /// </summary>
        public string BackgroundGradientEnd { get; set; }

        #endregion

        #region Methods

        /// <summary>
        /// The PropertyChanged event occurs when changing the value of property.
        /// </summary>
        /// <param name="property">Property name</param>
        protected void OnPropertyChanged(string property)
        {
            this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
        }

        #endregion
    }

Here's my ChartView control template (Notice this will be visible or not with a boolean Binding from my Model):

<StackLayout xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             xmlns:chart="clr-namespace:Syncfusion.SfChart.XForms;assembly=Syncfusion.SfChart.XForms" 
             x:Class="SmartFarm.Shared.Views.Templates.ChartView"
             IsVisible="{TemplateBinding BindingContext.ChartDataVisible}" >
    <ContentPresenter/>

    <chart:SfChart
                Margin="0,11,0,0"
                BackgroundColor="Transparent"
                HeightRequest="{OnIdiom Phone=44,
                                        Desktop=80,
                                        Tablet=80}"
                HorizontalOptions="FillAndExpand"
                VerticalOptions="FillAndExpand">
        <chart:SfChart.PrimaryAxis>
            <chart:DateTimeAxis
                    IsVisible="True"
                    RangePadding="None"
                    ShowMajorGridLines="False" />
        </chart:SfChart.PrimaryAxis>

        <chart:SfChart.SecondaryAxis>
            <chart:NumericalAxis IsVisible="True" ShowMajorGridLines="False" />
        </chart:SfChart.SecondaryAxis>

        <chart:SfChart.Series>
            <chart:SplineSeries
                    ItemsSource="{TemplateBinding BindingContext.ChartData , Mode=TwoWay}"
                    StrokeWidth="2"
                    XBindingPath="DateTimeXValue"
                    YBindingPath="YValue"
                    Color="{DynamicResource Deep-Purple}" />
        </chart:SfChart.Series>
    </chart:SfChart>

</StackLayout>

Here is my list view into a Details ContentPage:


       <listView:SfListView
            x:Name="listViewCards"
            AutoFitMode="Height"
            IsScrollingEnabled="False"
            ItemSize="74"
            ItemsSource="{Binding CardItems}"
            SelectionMode="None"
            TapCommand="{Binding ItemTappedCommand}">
            <listView:SfListView.ItemTemplate>
                <DataTemplate>
                    <svg:CustomShadowFrame
                        x:Name="shadowFrame"
                        Margin="16,8"
                        Padding="0"
                        BackgroundColor="{DynamicResource GrayShadowColor}"
                        BorderWidth="0"
                        CornerRadius="4"
                        HasShadow="True"
                        HorizontalOptions="CenterAndExpand"
                        Radius="4"
                        WidthRequest="{OnIdiom Phone=343,
                                               Default=736}">

                        <Grid
                            Padding="16"
                            RowDefinitions="Auto, *"
                            RowSpacing="5">

                            <!--  Category  -->
                            <Label
                                HeightRequest="24"
                                HorizontalOptions="Start"
                                Style="{StaticResource TitleLabelStyle}"
                                Text="{Binding CategoryValue}" />

                            <!--  Category Value  -->
                            <Label
                                Grid.Row="1"
                                FontSize="15"
                                HeightRequest="{Binding Category}"
                                HorizontalOptions="FillAndExpand"
                                HorizontalTextAlignment="Start"
                                Style="{StaticResource DescriptionLabelStyle}"
                                Text="{Binding Category}" />

                            <ContentView 
                                x:Name="chartView"
                                Grid.Row="2"
                                ControlTemplate="{StaticResource ChartView}" >
                            </ContentView>
                        </Grid>

                    </svg:CustomShadowFrame>
                </DataTemplate>
            </listView:SfListView.ItemTemplate>

Here the results

Details View result

I'm still not dealing on how to resolve this, but now I'm thinking in removing the content view from each frame if my chart flag is false, how can I achieve this from code behind? I can't get a Frame list from my list view like this:

        protected override void OnAppearing()
        {
            base.OnAppearing();
            var listView = listViewCards as Syncfusion.ListView.XForms.SfListView;
            var itemSource = listView.ItemsSource as ObservableCollection<DetailCard>;
            var itemTemplate = listView.ItemTemplate;
    
            //I need a mechanism here to loop into all Frames from list view and remove the content view code

        }

Upvotes: 0

Views: 303

Answers (4)

SaiGanesh Sakthivel
SaiGanesh Sakthivel

Reputation: 49

Empty spacing below the items.

We have checked the reported query from our end. We would like to inform you that the empty space in the view is a contentview. We suggest to you to bind the IsVisible property to the ContentView instead of StackLayout to overcome the reported scenario.

Code snippet:

<ContentView  
            x:Name="chartView" 
            IsVisible = "{Binding ChartDataVisible}" 
            Grid.Row="2" 
            ControlTemplate="{StaticResource ChartView}" > 
</ContentView> 
 

Remove the code at a runtime.

We have checked the reported query from our side. You cannot get the elements from the ItemTemplate directly. However you can handle the ItemTemplate with different layout like you’re your chart flag is false you can use another template with the help of DataTemplateSelector in a SfListView.

You can also refer to the following UG Documentation link for your reference. UG Link:https://help.syncfusion.com/xamarin/listview/viewappearance#data-template-selector

Or you can stick with the above solution with IsVisible property. Based on chart flag you can architect your UI.

Upvotes: 0

Himanshu Dwivedi
Himanshu Dwivedi

Reputation: 8174

As per your requirement mentioned first of all you need to update the Grid definition like this:

<Grid
    Padding="16"
    RowDefinitions="Auto, *, Auto"
    RowSpacing="5">

Make the third row as Auto which contains the ChartView, after this control the Visibility of <ContenView> using a bool property based on availability of data to be displayed in Chart like this:

<ContentView 
     x:Name="chartView"
     Grid.Row="2"
     IsVisible="{Binding IsChartDataAvailable}"
     ControlTemplate="{StaticResource ChartView}"/>

You need to have this IsChartDataAvailable property of type bool in your model class of ItemSource => CardItems

Upvotes: 1

Fernando Ares
Fernando Ares

Reputation: 33

I ended up adding row definition to each Frame row and binding the row size, this is probably not the best way to do it but worked for me:

    public class DetailCard : BaseCard
    {
        public bool ChartDataVisible { get; set; }
        public GridLength RowSize { get; set; }
        public string DetailsList { get; set; }

        public DetailCard()
        {
            ChartDataVisible = false;
            RowSize = new GridLength(0);
        }
    }
    <Grid
        Padding="16"
        RowSpacing="5">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition  />
            <RowDefinition Height="{Binding RowSize}"/>
        </Grid.RowDefinitions>

        <!--  Category  
        .
        .
        .
        -->
        

        <!--  Category Value
        .
        .
        .
        -->


        <ContentView 
            x:Name="chartView"
            IsVisible="{Binding ChartDataVisible}"
            Grid.Row="2"
            ControlTemplate="{StaticResource ChartView}" >
        </ContentView>
    </Grid>

Upvotes: 0

hvaughan3
hvaughan3

Reputation: 11105

Have you tried setting SfListView.AutoFitMode to DynamicHeight? DynamicHeight will change the cell's height dynamically if a control in that cell changes size or is hidden from view. It may be sizing your cell with the chart's initial height and then once the binding kicks in, the height changes but the cell does not update.

You may also want to set the RowHeight on the Grid's Column that contains the Graph to Auto so that row will automatically collapse.

Syncfusion Docs:

https://help.syncfusion.com/xamarin/listview/item-size-customization?&_ga=2.163442492.1646268537.1619378899-161007305.1608580947#autofitmode-as-dynamicheight

Upvotes: 0

Related Questions