2

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);
        }
Hikari
  • 589
  • 7
  • 29
  • For your WoDett object, does the NotaTestata property contain an `OnPropertyChanged(nameof(NotaTestata))` event handler call in the setter? – sme Jan 15 '18 at 08:37
  • Yes, it does. I have edited the question. – Hikari Jan 15 '18 at 08:51
  • Have you set the `BindingContext`? I think whether you did something wrong with binding? – Ax1le Jan 15 '18 at 09:53
  • @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 Jan 15 '18 at 09:58

2 Answers2

1

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(...);
}
sme
  • 4,023
  • 3
  • 28
  • 42
  • I have edited both renderers but is still not working. I have also edited the question. – Hikari Jan 15 '18 at 09:04
  • @Hikari Can you put a break point in the `OnElementPropertyChanged` method, and see if the `TextProperty` ever changes? – sme Jan 15 '18 at 09:07
  • It's called, but not for the `TextProperty`. I have also added a test button that changes the `NotaTestata` value but the `OnElementPropertyChanged` method isn't called. – Hikari Jan 15 '18 at 09:12
  • @Hikari Is the label inside of a cell in the list view? What does the view model look like? – sme Jan 15 '18 at 09:19
  • The label is inside a TableView. I have made a zip with view, viewModel and the renderers: https://ufile.io/22bvt – Hikari Jan 15 '18 at 09:29
0

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!

Hikari
  • 589
  • 7
  • 29
  • In that case, it seems like you are updating the text in your view, by setting `myCustomLabel.Text = "...";`. This will update your view, but unless you have Two-way binding, the value of the binded object will not change. Instead, you should directly update the value of the `WoDett.NotaTestata` value in your View Model, `Wodett.NotaTestata = "...";` Two way binding is best for handling user input into textboxes, radio buttons, etc. – sme Jan 15 '18 at 10:49