Forum Xamarin.Forms

Announcement:

The Xamarin Forums have officially moved to the new Microsoft Q&A experience. Microsoft Q&A is the home for technical questions and answers at across all products at Microsoft now including Xamarin!

To create new threads and ask questions head over to Microsoft Q&A for .NET and get involved today.

Customly rendered label on ListView not updating properly - INotifyPropertyChanged used

Hi,

I have a form with ListView with customly rendered label on it (testzxing:CustomLabel). The listview's source is bound to ObservableCollection Items. "Process" contains 2 properties that determine what is supposed to be label's Text (Binding TimingVsPlan) and BgColor (Binding TimingBgColor). For some reason Text is displayed properly for all items, but the background color seems to be for all items the same as the first row. I've checked and both properties GET event is fired properly.. I think it might be related to late listview binding as bgColor is updated properly in some other form..

Here's some code:

XAML
<ListView x:Name="lstProcesses" ItemsSource="{Binding Items}" ItemTapped="LstProcesses_ItemTapped" VerticalOptions="FillAndExpand" HasUnevenRows="True" SeparatorVisibility="Default" CachingStrategy="RecycleElement"> <ListView.ItemTemplate> <DataTemplate> <ViewCell> <StackLayout Padding="10"> <Label Text="{Binding PlaceName}" LineBreakMode="WordWrap" Style="{DynamicResource ListItemTextStyle}" FontSize="Medium" FontAttributes="Bold"/> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="6*"/> <ColumnDefinition Width="4*"/> </Grid.ColumnDefinitions> <Label Text="{Binding SetName}" FontSize="Small" LineBreakMode="WordWrap" Style="{DynamicResource ListItemTextStyle}" Grid.Row="0" Grid.Column="0"/> <testzxing:CustomLabel Text="{Binding TimingVsPlan}" BgColor="{Binding TimingBgColor}" FontSize="Small" Grid.Row="0" Grid.Column="1"/> </Grid> </StackLayout> </ViewCell> </DataTemplate> </ListView.ItemTemplate> </ListView>

CODE BEHIND:
public AssignedProcesses(AssignedProcessesViewModel vm) { InitializeComponent(); BindingContext = vm; }

VIEWMODEL:
public class AssignedProcessesViewModel : INotifyPropertyChanged
{

        public AssignedProcessesViewModel()
        {

        }

        public async void Initialize()
        {
            ProcessKeeper keeper = new ProcessKeeper();
            await keeper.GetUsersOpenProcesses("IsActive=false and IsFrozen=false");
            if (keeper.Items.Any())
            {
                //there are planned processes assigned to the user
                Items = new ObservableCollection<Process>(keeper.Items.OrderBy(i => i.PlannedStart));
                WelcomeText = $"Poniżej znajduje się lista planowanych konserwacji w bieżącym okresie, które zostały przypisane min. do Ciebie. Liczba pozycji: {Items.Count}";
                Display();
            }
        }

        private ObservableCollection<Process> _Items { get; set; }
        public ObservableCollection<Process> Items
        {
            get
            {
                return _Items;
            }
            set
            {
                if(value != _Items)
                {
                    _Items = value;
                    OnPropertyChanged();
                }
            }
        }


        private string _WelcomeText { get; set; }

        public string WelcomeText
        {
            get
            {
                return _WelcomeText;
            }
            set
            {
                if (value != _WelcomeText)
                {
                    _WelcomeText = value;
                    OnPropertyChanged();
                }
            }
        }

        private bool _isWorking { get; set; }

        public bool IsWorking
        {
            get
            {
                return _isWorking;
            }
            set
            {
                if (_isWorking != value)
                {
                    if (value == false)
                    {
                        if (PopupNavigation.Instance.PopupStack.Any()) { PopupNavigation.Instance.PopAllAsync(true); }  // Hide loading screen
                    }
                    else
                    {
                        PopupNavigation.Instance.PushAsync(new LoadingScreen(), true); // Show loading screen
                    }
                    _isWorking = value;
                    OnPropertyChanged();
                }
            }
        }

        public void Display()
        {
            PopupNavigation.Instance.PushAsync(new AssignedProcesses(this), true);
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged([CallerMemberName] string name = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }
    }

PROCESS MODEL:
`public class Process : INotifyPropertyChanged
{
private Nullable _PlannedStart { get; set; }
public Nullable PlannedStart
{
get
{
return _PlannedStart;
}
set
{
if (value != _PlannedStart)
{
_PlannedStart = value;
OnPropertyChanged();
}
}
}
public Nullable PlannedFinish { get; set; }

    public string TimingVsPlan
    {
        get
        {
            if (PlannedStart == null)
            {
                return "Nie dotyczy";
            }
            else
            {
                if (PlannedStart < DateTime.Now.AddDays(-7))
                {
                    return "Zaległe";
                }
                else
                {
                    return "Bieżące";
                }
            }
        }
    }
    public Color TimingBgColor
    {
        get
        {
            if (PlannedStart == null)
            {
                return Color.Default;
            }
            else
            {
                if (PlannedStart < DateTime.Now.AddDays(-7))
                {
                    return Color.Red;
                }
                else
                {
                    return Color.Green;
                }
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged([CallerMemberName] string name = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }}`
Tagged:

Answers

  • ColeXColeX Member, Xamarin Team Xamurai
    edited December 2019

    Your code looks no problem .

    Does CustomLabel inherit from Label ?

    Why don't you use BackgroundColor instead of BgColor ?

  • robs23robs23 Member ✭✭

    In fact I just changed BgColor to BackgroundColor and it works as it should.. I used custom renderer to create BgProperty to have background with rounded edges. So probably, the problem is in my custom renderer..

    `[assembly: ExportRenderer(typeof(CustomLabel), typeof(CustomLabelRendererAndroid))]
    namespace TestZXing.Droid
    {
    public class CustomLabelRendererAndroid : LabelRenderer
    {
    public CustomLabelRendererAndroid(Context context) : base(context)
    {

        }
    
        protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
        {
            base.OnElementChanged(e);
    
            if (Control != null)
            {
    
                Control.Gravity = GravityFlags.Center;
                var gd = new GradientDrawable();
                gd.SetCornerRadius(60f);
                gd.SetStroke(6, Android.Graphics.Color.Transparent);
                if (e.NewElement != null)
                {
                    var element = e.NewElement as CustomLabel;
                    if(element.BgColor != Color.Transparent)
                    {
                        gd.SetColor(element.BgColor.ToAndroid());
                    }
                }
                Control.SetBackground(gd);
            }
        }
    }
    

    }`

  • ColeXColeX Member, Xamarin Team Xamurai

    Try to use Effects to implement rounded edges , it does not break the original background.

    Refer https://stackoverflow.com/a/52369634/8187800.

Sign In or Register to comment.