1
votes

I've created a ListView in Xamarin form and bind to Observable collection in view model, adding item dynamically to ListView is working fine by calling OnPropertyChanged event.

But after getting status update from service I'm updating corresponding ListView item status and calling OnPropertyChanged event as well as re-assigining the ListView items to it but didn't get updated GUI properly sometimes working and some times not.

Below is the sample code that I've done.

<ListView Grid.Row="3" HasUnevenRows="True" ItemsSource="{Binding ServiceList}" IsPullToRefreshEnabled="True" SeparatorColor="Black">
    <ListView.ItemTemplate>
        <DataTemplate>
            <ViewCell>
                <StackLayout Orientation="Vertical" Spacing="4" Padding="5" BackgroundColor="LightGray">
                    <Label Text="{Binding OperationStatus, Converter={x:Static local:StatusMessageConverter.Default}}" FontSize="13" FontAttributes="Bold" TextColor="White" BackgroundColor="DarkCyan" />
                    <Label Text="{Binding Operation}" FontSize="10" Margin="10,0,0,0" />
                    <Label Text="{Binding OperationType}" FontSize="10" Margin="10,0,0,0" />
                    <Label Text="{Binding OperationStatus}" LineBreakMode="WordWrap" IsVisible="{Binding CanStatusVisible}" FontSize="10" Margin="10,0,0,0" />                          
                </StackLayout>
            </ViewCell>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

public class ServiceViewModel : INotifyPropertyChanged
{
    public ObservableCollection<ServiceItem> ServiceList 
    {
        get
        {
            return _serviceList;
        }
        set
        {
            _serviceList = value;
            OnPropertyChanged("ServiceList");
        }
    }

    var tempList = new ObservableCollection<ServiceItem>();
    tempList = ServiceList;
    var targetItem = from item in tempList
        where item.UniqueId == uniqueId
        select item;
    if (targetItem.Any())
    {
        var resultItem = targetItem.FirstOrDefault();
        resultItem.CanStatusVisible = true;
        resultItem.OperationStatus = string.Format("{0}: {1}", "Status Message", resultMessage);
    }

    ServiceList = null;
    ServiceList = tempList;
    OnPropertyChanged("ServiceList");
} 

public class ServiceItem
{
    public string UniqueId { get; set; }
    public string Operation { get; set; }
    public string OperationType { get; set; }
    public string OperationStatus { get; set; }
    public string StatusMessage { get; set; }
    public bool CanStatusVisible { get; set; }
}
2
Does your ServiceItem class implement the INotify PropertyChanged?Bruno Caceiro
I've added it to ViewModelnag
as @BrunoCaceiro says, your ServiceItem class needs to implement INPC in order for the UI to be notified of changes to its propertiesJason
Yep! I'm checking that.. :)nag

2 Answers

1
votes

See to it that your model class inherits from INotifyPropertyChangedinterface(as mentioned in the above comments).

public class ServiceItem :INotifyPropertyChanged
{
 private string uniqueId,operation,operationType,operationStatus,statusMessage;
 private bool statusVisible;


 public string UniqueId { get { return uniqueId; } set { uniqueId= value; RaisePropertyChanged(nameof(UniqueId)); } }

 public string Operation { get { return operation; } set { operation= value; RaisePropertyChanged(nameof(Operation)); } }

 public string OperationType { get { return operationType; } set { operationType= value; RaisePropertyChanged(nameof(OperationType)); } }

 public string OperationStatus { get { return operationStatus; } set { operationStatus= value; RaisePropertyChanged(nameof(OperationStatus)); } }

 public string StatusMessage { get { return statusMessage; } set { statusMessage= value; RaisePropertyChanged(nameof(StatusMessage)); } }

 public bool CanStatusVisible { get { return statusVisible; } set { statusVisible= value; RaisePropertyChanged(nameof(CanStatusVisible )); } }
}

Then your ViewModel code should look something like this:

var tempList = new ObservableCollection<ServiceItem>();
tempList = ServiceList;
var targetItem = from item in tempList
    where item.UniqueId == uniqueId
    select item;
if (targetItem.Any())
{
    var resultItem = targetItem.FirstOrDefault();
    resultItem.CanStatusVisible = true;
    resultItem.OperationStatus = string.Format("{0}: {1}", "Status Message", resultMessage);
}

ServiceList = null;
ServiceList = tempList;

Once you do these changes your code should work

0
votes

--- To clarify my comment on FreakyAli's good answer ---

The essential part of FreakyAli's answer is the first code snippet:

public class ServiceItem :INotifyPropertyChanged
...

Once that is done, the other code in question can be greatly simplified. I think (though I have not tested) that you can replace all the code Ali shows under "Then your ViewModel code should look something like this:" with:

ServiceItem resultItem = ServiceList.Where(item => item.UniqueId == uniqueId).FirstOrDefault();
if (resultItem != null)
{
    resultItem.CanStatusVisible = true;
    resultItem.OperationStatus = string.Format("{0}: {1}", "Status Message", resultMessage);
}

That is, it is not necessary to create a temp list, nor to manipulate ServiceList. When you change the property of a ServiceItem, that property's RaisePropertyChanged will trigger the needed display refresh.