Item in GroupHeaderTemplate not updating when ItemsSource is updated via a timed task

I have an app that displays the current status of a bunch of sensors (for the sake of this post, I will say that they can either be "opened" or "closed").

I have a Timer that runs once a second and makes a REST call to a service that gets me an ObservableCollection all the sensors and their current state.  So if a sensor is tripped, the state of the sensor in the SfListView is updated right away.  This works perfectly.

However, when I group the sensors by sensor type, there is also a visual indicator in the GroupHeaderTemplate showing the state for that entire group.  I am not able to get this value to update unless I reload the page.

Here is the stripped down version of my code:

<syncfusion:SfListView x:Name="gridResults" AutoFitMode="DynamicHeight" Loaded="GridResults_Loaded" Grid.Row="0">
    <syncfusion:SfListView.GroupHeaderTemplate>
        <DataTemplate>
            <ViewCell>
                <ViewCell.View>
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="30" />
                            <ColumnDefinition Width="*" />
                        </Grid.ColumnDefinitions>

                        <Label Text="&#x25B8;">
                            <Label.Triggers>
                                <DataTrigger TargetType="Label" Binding="{Binding IsExpand}" Value="True">
                                    <Setter Property="Text" Value="&#x25BE;" />
                                </DataTrigger>
                            </Label.Triggers>
                        </Label>

                        <Grid Grid.Column="1">
                            <Label Text="{Binding }" />
                            <Label Text="&#x25CF;" TextColor="Red" IsVisible="{Binding .,Converter={StaticResource imageSourceConverter}}" />
                        </Grid>
                    </Grid>
                </ViewCell.View>
            </ViewCell>
        </DataTemplate>
    </syncfusion:SfListView.GroupHeaderTemplate>
    <syncfusion:SfListView.ItemTemplate>
        <DataTemplate>
            <ViewCell>
                <ViewCell.View>

                    <Grid>
                        <Label Text="{Binding Title}" />
                        <Label Text="{Binding StatusDescription}" />
                    </Grid>
                </ViewCell.View>
            </ViewCell>
        </DataTemplate>
    </syncfusion:SfListView.ItemTemplate>
</syncfusion:SfListView>

So in the header template, there are two labels.  The first one ends up being the sensor type that we are grouping by.  The second label is just a red dot.  That dot's visibility is determined on if any of the items in the grid are "opened".

My image source converter just returns a true or a false if any sensors in the list are "opened":

public class SensorImageStatusConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value != null)
        {
            var items = (value as GroupResult).Items.ToList<SensorBase>().ToList();
            bool anyOpenOrOn = items.Any(sensor => (sensor).IsOpenOrOn);

            if (anyOpenOrOn)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        return false;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Everything in my view model reports the property status changed, so that isn't the issue.

And the way that the ObservableCollection is modified is via a Timer that runs at 1 second intervals.


private ObservableCollection<Sensor> Sensors;

private void StartTimer()
{
    Xamarin.Forms.Device.StartTimer(TimeSpan.FromSeconds(1), () =>
    {
        Task.Factory.StartNew(async () =>
        {
            RestResponse response = await SensorCommands.GetSensorList();
            if (this.Sensors == null)
            {
                this.Sensors = ParseSensorsFromRestResponse(response);
            }
            else 
            {
                foreach (Sensor s in this.Sensors)
                {
                    s.IsOpenOrOn = GetSensorStateFromRestResponse(response);
                }
            }

            Xamarin.Forms.Device.BeginInvokeOnMainThread(() =>
            {
                if (InitialLoad)
                {
                    gridResults.AllowGroupExpandCollapse = true;
                    GroupDescriptor descriptor = new GroupDescriptor();
                    descriptor.PropertyName = "SensorTypeDescription";
                    gridResults.DataSource.GroupDescriptors.Add(descriptor);

                    gridResults.ItemsSource = Sensors;
                    gridResults.CollapseAll();

                    await Task.Delay(100);
                    gridResults.RefreshListViewItem();
                }
                StartTimer();
            });
        });
        return false;
    });
}

Like I said, the item inside the grid updates as expected.  But the red dot in the header template does not update unless I refresh the page and rebind it.  What I would expect to happen is to see the red dot visible if any of the Sensors in the ObservableCollection are showing IsOpenOrOn, just as it does for the individual items in the listview.

Any help on this would be greatly appreciated.

Thanks!



5 Replies

SS SaiGanesh Sakthivel Syncfusion Team April 28, 2020 12:55 PM UTC

Hi Mike, 
 
Thank you for contacting Syncfusion support. 
 
We have checked the reported query from Item in GroupHeaderTemplate not updating our end. We suggest you to use DataSource’s LiveDataUpdateMode property. DataSource listens manipulation operations such as add, delete, and data update (property change) at runtime and responds based on the LiveDataUpdateMode property. Set LiveDataUpdateMode to AllowDataShaping, it refreshes an item in view and also updates the collection when the underlying property is changed. 
 
Code Snippet 
<syncfusion:SfListView.DataSource>  
    <data:DataSource LiveDataUpdateMode="AllowDataShaping">  
        <data:DataSource.GroupDescriptors>  
            <data:GroupDescriptor PropertyName="Year" /> 
        </data:DataSource.GroupDescriptors>  
    </data:DataSource>  
</syncfusion:SfListView.DataSource>  
 
And also we could like to inform you that inside the GroupHeaderTemplate, label should be binding with “Key”. Please refer to the following code snippet and also the UG link. 
 
Code Snippet 
<sync:SfListView.GroupHeaderTemplate> 
    <DataTemplate> 
        <ViewCell> 
            <ViewCell.View> 
                <Grid> 
                    <Grid.ColumnDefinitions> 
                        <ColumnDefinition Width="30" /> 
                        <ColumnDefinition Width="*" /> 
                    </Grid.ColumnDefinitions> 
                    <Label Text="&#x25B8;"> 
                        <Label.Triggers> 
                            <DataTrigger TargetType="Label" Binding="{Binding IsExpand}" Value="True"> 
                                <Setter Property="Text" Value="&#x25BE;" /> 
                            </DataTrigger> 
                        </Label.Triggers> 
                    </Label> 
                    <Grid Grid.Column="1"> 
                        <Label Text="{Binding Key}" /> 
                        <Label Text="&#x25CF;" TextColor="Red" IsVisible="{Binding .,Converter={StaticResource imageSourceConverter}}" /> 
                    </Grid> 
                </Grid> 
            </ViewCell.View> 
        </ViewCell> 
    </DataTemplate> 
</sync:SfListView.GroupHeaderTemplate> 
  
Please refer to the following UG  Documentation in the link. 
 
We hope this helps.  please if you are still facing the issue kindly modify our sample to replicate the issue with following details, 
  
·       Modify xaml page and view Model page. 
·       Model class details about property used in it. 
 
Regards, 
SaiGanesh Sakthivel


ML Mike Luken April 28, 2020 04:23 PM UTC

Thank you for the quick reply!

Unfortunately that did not fix the problem though.

I have created a sample app that recreates the problem and am attaching the project.

If you open this project and run it, you will see that the listview is grouped into three headings.

The first heading is called "Doors", and I have added 4 sensors to that group.  I have set one of the four sensors to "Open", so you can see that there is a red dot that shows up in the header next to the description.  This is working as I would expect.

Now, jump down to the third grouping called "Windows".  This group has 3 sensors added to it.  By default, all of these are set to "closed".  However, I have a timer running that updates "dining room" to "open" and then back to "closed" every three seconds.  If you expand this grouping, you can see that this is what it is doing.  However, what I would expect to see at this point is every three seconds the red dot should appear, as the converter should be setting the dot's visibility on/off.  This is not happening.  The dot never appears.

Please let me know what else I can provide to help you research this.

Thanks!

Attachment: SfListview_14380a4.zip


CS Chandrasekar Sampathkumar Syncfusion Team April 29, 2020 04:54 PM UTC

Hi Mike, 
Thank you for the update. 
We have checked the provided sample from our end. We like to let you know that the GroupHeader converter will be triggered when the GroupResult is changed. In this case, when updating the property which bound to the GroupHeader will not trigger the Converter to update the changes. So it is necessary to refresh the GroupHeader value in sample level. We suggest you to call RefreshListViewItem with the index of the GroupHeader to be updated like in the following code snippet, 
C#: Refresh the GroupHeader  
private void StartThread() 
{ 
    Device.StartTimer(TimeSpan.FromSeconds(3), () => 
    { 
    Task.Factory.StartNew(() => 
    { 
        Sensors.Where(x => x.Description == "Dining Room").FirstOrDefault().IsOpenOrOn = 
            !Sensors.Where(x => x.Description == "Dining Room").FirstOrDefault().IsOpenOrOn; 
    }); 
    Device.BeginInvokeOnMainThread(() => 
    { 
        var items = gridResults.DataSource.DisplayItems; 
        foreach (var item in items) 
        { 
            if(item is GroupResult) 
            { 
                var group = item as GroupResult; 
                if((string)group.Key == "Window") 
                { 
                    var index = gridResults.DataSource.DisplayItems.IndexOf(group); 
                    gridResults.RefreshListViewItem(index, index, true); 
                } 
            } 
        } 
    }); 
        return true; 
    }); 
} 
We have attached the modified sample for your reference and you can download the same using the following link, 
Sample Link: SfListViewSample 
Video: Video 
Please let us know if you need further assistance. 
Regards, 
Chandrasekar Sampathkumar 



ML Mike Luken April 29, 2020 05:59 PM UTC

Thank you so much.  That fixed the issue and it is now working as expected.  Thanks!!!!!!!


CS Chandrasekar Sampathkumar Syncfusion Team April 29, 2020 08:28 PM UTC

Hi Mike, 
Thank you for the update. 
We are glad that our solution meets your requirement. Please let us know if you need any further assistance. As always we are happy to help you out. 
Regards, 
Chandrasekar Sampathkumar 


Loader.
Up arrow icon