2
votes

I have create a custom renderer for label that show the text in html format.

This is the iOS's code:

   [assembly: ExportRenderer(typeof(LabelHtmlView), typeof(LabelHtmlCustomRenderer))]
namespace SgatMobileV2.iOS {
    public class LabelHtmlCustomRenderer : LabelRenderer {              

        protected override void OnElementChanged(ElementChangedEventArgs<Label> e) {
            base.OnElementChanged(e);

            var View = (LabelHtmlView)Element;
            if (View == null) return;

            var Attribute = new NSAttributedStringDocumentAttributes();
            var NsError = new NSError();

            Attribute.DocumentType = NSDocumentType.HTML;
            View.Text = string.IsNullOrEmpty(View.Text) ? string.Empty : View.Text;

            Control.AttributedText = new NSAttributedString(View.Text, Attribute, ref NsError);

        }    
    }
}

Android code:

[assembly: ExportRenderer(typeof(LabelHtmlView), typeof(LabelHtmlCustomRenderer))]
namespace SgatMobileV2.Droid.CustomRenderer {
    public class LabelHtmlCustomRenderer : LabelRenderer {

        public LabelHtmlCustomRenderer(Context context)
            : base(context) {
        }
        protected override void OnElementChanged(ElementChangedEventArgs<Label> e) {
            base.OnElementChanged(e);

            var View = (LabelHtmlView)Element;
            if (View == null) return;

            Control.SetText(Html.FromHtml(View.Text.ToString(), FromHtmlOptions.ModeLegacy), TextView.BufferType.Spannable);
        }

    }
}

I use, for example, the control in XAML in this way:

<view:LabelHtmlView Text="{Binding WoDett.NotaTestata}" Grid.Row="0" Grid.Column="1" HeightRequest="200"/>

where WoDett is a complex object and NotaTestata is a string that contains HTML markup.

I have debugged the code and the OnElementChanged is called before I set the WoDett object, so the NotaTestata is null, so the label in XAML appears empty.

This behavior is the same on iOS and Android.

I have others custom renderer and they work properly with similar logic, but this label don't.

How can I solve this?

EDIT:

This is the NotaTestata property in the object class:

private string _NotaTestata = string.Empty;
public string NotaTestata {
    get { return _NotaTestata; }
    set {
          _NotaTestata = value;
          OnPropertyChanged(nameof(NotaTestata));
        }
    }

EDIT 2:

The same custom label inside a ListView, works fine.

<ListView x:Name="ListaRigheWo" CachingStrategy="RetainElement" HasUnevenRows="True" SeparatorVisibility="Default" ItemsSource="{Binding ListaWORighe}">

where ListaWORighe is defined in this way:

public ObservableCollection<WorkOrderDettaglioListaRighe> ListaWORighe {
            get {
                ObservableCollection<WorkOrderDettaglioListaRighe> Lista = null;

                if (WoDett.ListaWORighe != null) {
                    Lista = OrdinamentoRigheWo(WoDett.ListaWORighe);
                }

                return Lista;
            }
            set {
                WoDett.ListaWORighe = new List<WorkOrderDettaglioListaRighe>(value);
                RaisePropertyChanged(() => ListaWORighe);
            }
        }

and the custom label is used like this:

<view:LabelHtmlView Text="{Binding DescrizioneIntervento}" Grid.Row="1" Grid.Column="2" Grid.ColumnSpan="4" Style="{StaticResource LblValore}" />

EDIT 3:

Following the suggest of user @sme, I have modify the renderers like this, adding the override of OnElementPropertyChanged method:

protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) {
            base.OnElementPropertyChanged(sender, e);
            if (e.PropertyName == Label.TextProperty.PropertyName) {
                UpdateText();
            }
        }

        protected override void OnElementChanged(ElementChangedEventArgs<Label> e) {
            base.OnElementChanged(e);
            UpdateText();
        }

        private void UpdateText() {
            var View = (LabelHtmlView)Element;
            if (View == null) return;

            var Attribute = new NSAttributedStringDocumentAttributes();
            var NsError = new NSError();

            Attribute.DocumentType = NSDocumentType.HTML;
            View.Text = string.IsNullOrEmpty(View.Text) ? string.Empty : View.Text;

            Control.AttributedText = new NSAttributedString(View.Text, Attribute, ref NsError);
        }
2
For your WoDett object, does the NotaTestata property contain an OnPropertyChanged(nameof(NotaTestata)) event handler call in the setter? - sme
Yes, it does. I have edited the question. - Hikari
Have you set the BindingContext? I think whether you did something wrong with binding? - Ax1le
@Land Yes, I use a class that have an AutoWireViewModel property to bind the viewmodel to view, and all others binded properties on the view works fine. If I use a normal Label, it shows the HTML markup. - Hikari

2 Answers

1
votes

Both of your renderers should also override the OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) method. OnElementChanged is called when a new object is created, and OnElementPropertyChanged is called when a property of the object is changed. I recommend doing it like so (inside of your OnElementPropertyChanged() method:

if (e.PropertyName == Label.TextProperty.PropertyName)
{
    // the text was updated
    Control.SetText(...);
}
0
votes

I have solve by adding Mode=TwoWay on XAML in this way:

<view:LabelHtmlView Text="{Binding WoDett.NotaTestata, Mode=TwoWay}" Grid.Row="0" Grid.Column="1" />

I don't know why for a custom label is this necessary, while for "normal" label it isn't, but now it seem to works correctly.

Thanks at all!