Forum Xamarin.Forms
We are excited to announce that the Xamarin Forums are moving to the new Microsoft Q&A experience. Q&A is the home for technical questions and answers at across all products at Microsoft now including Xamarin!

We encourage you to head over to Microsoft Q&A for .NET for posting new questions and get involved today.

Custom Entry Renderer: Changing Property does not effect UI with MVVM

AgredoAgredo USMember ✭✭
edited November 2019 in Xamarin.Forms

Hello guys,

I have a Custom Entry Renderer:
`
using System;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
namespace Retailer.CustomControl
{
public class TextBox : Entry
{

    public static readonly BindableProperty BorderVisibilityProperty = BindableProperty.Create("BorderVisibility", typeof(bool), typeof(TextBox), false, BindingMode.TwoWay);

    public bool BorderVisibility
    {
        get
        {
            return (bool)GetValue(BorderVisibilityProperty);
        }
        set
        {
            SetValue(BorderVisibilityProperty, value);
        }
    }

    public new event EventHandler Completed;

    public void SendComplete()
    {
        Completed?.Invoke(this, EventArgs.Empty);
    }
}

}
`

in a rg.Xamarin.Pupup Page.

`
<?xml version="1.0" encoding="UTF-8"?>

<Grid VerticalOptions="Center" HorizontalOptions="Center">
    <ScrollView>
        <StackLayout VerticalOptions="Center" HorizontalOptions="Center" BackgroundColor="WhiteSmoke" Padding="16" WidthRequest="320">
            <StackLayout>
                <Label Text="No store information found!" FontSize="Medium" HorizontalOptions="Center" VerticalOptions="Center"/>
                <Label Text="Please insert this information about your store" FontSize="Body" HorizontalOptions="Center" VerticalOptions="Center"/>
            </StackLayout>

            <StackLayout>
                <Label Text="{Binding CountryTextBlockText}"/>
                <UserControl:TextBox Text="{Binding CountryTextBoxText}" />
            </StackLayout>

            <StackLayout>
                <Label Text="{Binding StreetTextBlockText}"/>
                <StackLayout Orientation="Horizontal">
                    <UserControl:TextBox Text="{Binding StreetTextBoxText}" />
                    <UserControl:TextBox Text="{Binding HouseNumberTextBoxText}"/>
                </StackLayout>
            </StackLayout>

            <StackLayout>
                <Label Text="{Binding PostalCodeTextBlock}"/>
                <UserControl:TextBox Placeholder="{Binding PostalCodeTextBoxText}" />
            </StackLayout>

            <StackLayout Orientation="Horizontal">
                <Label Text="{Binding MapVisibilityLabel}"/>
                <Switch IsToggled="{Binding ShowMapToggleToggled}"/>
            </StackLayout>

            <StackLayout Orientation="Horizontal">
                <Label Text="{Binding UseCurrentPositionButton}"/>
                <Button Text="{Binding UseCurrentPositionButtonText}" Command="{Binding GetCurrentPositionButtonCommand}"/>
            </StackLayout>

            <map:Map HeightRequest="240" IsVisible="{Binding ShowMapToggleToggled}"/>

            <Button Text="Submit" Command="{Binding SubmitButtonCommand}"/>
        </StackLayout>
    </ScrollView>
</Grid>


`

For UWP I created a Custom Renderer:

`
using Retailer.UWP.Renderer;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Xamarin.Forms;
using Xamarin.Forms.Platform.UWP;

[assembly: ExportRenderer(typeof(Retailer.CustomControl.TextBox), typeof(TextBoxRenderer))]
namespace Retailer.UWP.Renderer
{
public class TextBoxRenderer : EntryRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs e)
{
base.OnElementChanged(e);

        if (e.OldElement == null)
        {
            Control.BorderBrush = new SolidColorBrush();
        }
    }

    protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
    {

        if (((Retailer.CustomControl.TextBox)sender).BorderVisibility == false)
        {
            Control.BorderBrush = new SolidColorBrush();
        }

        if(e.PropertyName == nameof(TextBox.TextProperty))
        {
            if(Control.Text != (sender as Retailer.CustomControl.TextBox).Text)
                Control.Text = (sender as Retailer.CustomControl.TextBox).Text;
        }

    }

    private void FixFormsBackgroundColor(Xamarin.Forms.Frame frame)

    {

        var color = new Windows.UI.Color

        {

            A = Convert.ToByte(frame.BackgroundColor.A * 255),

            R = Convert.ToByte(frame.BackgroundColor.R * 255),

            G = Convert.ToByte(frame.BackgroundColor.G * 255),

            B = Convert.ToByte(frame.BackgroundColor.B * 255)

        };



        if (Control.Parent is Panel parent)

        {

            parent.Background = null;

        }



        Control.Background = new SolidColorBrush(color);

    }
}

}
`

For iOS:
`
using Retailer.CustomControl;
using Retailer.iOS.Renderer;
using System.ComponentModel;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

[assembly: ExportRenderer(typeof(TextBox), typeof(TextFieldRenderer))]
namespace Retailer.iOS.Renderer
{
public class TextFieldRenderer : EntryRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs e)
{
base.OnElementChanged(e);

        if (Control == null)
        {
            Control.BorderStyle = UITextBorderStyle.None;
            Control.BackgroundColor = UIColor.White;
        }


    }
    protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        base.OnElementPropertyChanged(sender, e);

        if (Control != null && ((Retailer.CustomControl.TextBox)sender).BorderVisibility == false)
        {
            Control.BorderStyle = UITextBorderStyle.None;
        }
        else
        {
            Control.BorderStyle = UITextBorderStyle.Line;
        }
    }
}

}
`

And Android:

`
using Android.Content;
using Retailer.Droid.Renderer;
using System.ComponentModel;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

[assembly: ExportRenderer(typeof(Retailer.CustomControl.TextBox), typeof(CustomEntryRenderer))]
namespace Retailer.Droid.Renderer
{
public class CustomEntryRenderer : EntryRenderer
{
public CustomEntryRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs e)
{
base.OnElementChanged(e);

        if (Control != null)
        {
            Control.SetBackground(new Android.Graphics.Drawables.ColorDrawable(Android.Graphics.Color.White));
        }
    }

    protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        base.OnElementPropertyChanged(sender, e);

        if (((Retailer.CustomControl.TextBox)sender).BorderVisibility == false)
        {

        }
    }
}

}
`

Changing the Properties in Constructure in ViewModel works to change the Value of myCustom Renderer TextBox. But I am trying to fill the TextBoxes with Lication Data from Geocoding.

` private string countryTextBoxText;

    public string CountryTextBoxText
    {
        get { return countryTextBoxText; }
        set { SetProperty(ref countryTextBoxText, value);
 }

`

`
private async Task setCurrentPlacemark()
{
var location = await Geolocation.GetLocationAsync();

        var placemarks = await Geocoding.GetPlacemarksAsync(location);

        currentPlacemark = placemarks?.FirstOrDefault();

        CountryTextBoxText = currentPlacemark.CountryName;

        if (placemarks != null)
        {
            PositionDataDownloaded();
        }

    }

`

But this does not work. Replacing TextBox with Entry works perfectly. I think my Custom renderer does sot support Binding the original Text Property. But Why?

Hopefully someone can help.

Best Answer

Answers

  • LandLuLandLu Member, Xamarin Team Xamurai

    Have you implemented the INotifyPropertyChanged for your CountryTextBoxText of the view model?
    And I think this code is unnecessary:

    if(e.PropertyName == nameof(TextBox.TextProperty))
    {
        if(Control.Text != (sender as Retailer.CustomControl.TextBox).Text)
            Control.Text = (sender as Retailer.CustomControl.TextBox).Text;
    }
    

    If you only want to display the text property any subclass inheriting from the Entry can make it. And we don't need to add extra code in the renderer for the displaying.

  • AgredoAgredo USMember ✭✭

    Thanks this was just a Text. I forgot to delete this.
    Yes I Implemented INotifyPropertyChanged. It works when I set the poroperty in constructure of my ViewModel ut later when I get Locattion and want to fill it with Grocoding (Country Name, StreetName...) this does not work anymoer.
    I am Using XF 4.3

  • LandLuLandLu Member, Xamarin Team Xamurai

    What platform did it occur on? If you comment out the custom renderer will it work?
    I created a blank project to test it, however, the text could be displayed correctly.
    We need your sample to help me reproduce this issue.

  • AgredoAgredo USMember ✭✭

    @LandLu said:
    What platform did it occur on? If you comment out the custom renderer will it work?
    I created a blank project to test it, however, the text could be displayed correctly.
    We need your sample to help me reproduce this issue.

    On All Platforms.

    It will Work if I use the official Entry. What do you mean with comment the renderer out? The Whole classes?
    I will create a project to test.

  • JoeHarvey_MSFTJoeHarvey_MSFT Member, Xamarin Team Xamurai

    @Agredo
    This is interesting, but its too hard to follow so much code posted on a Forum
    Please send a support ticket and one of the Xamarin Support guys will be more then happy to help you out

    Microsoft Support link. Free support for all Xamarin Developers
    https://support.microsoft.com/en-us/supportforbusiness/productselection?sapId=211dd84f-3474-c3c5-79bf-66db630c92a6!

  • AgredoAgredo USMember ✭✭
    edited November 2019

    @LandLu said:
    What platform did it occur on? If you comment out the custom renderer will it work?
    I created a blank project to test it, however, the text could be displayed correctly.
    We need your sample to help me reproduce this issue.

    @LandLu @JoeHarvey_MSFT I uploaded a sample App. Currently this only works (I tested) on UWP because Xamarin.Essentials need some Information for iOS and Android.

    The Button to show the popup can be found on the ItemsPage.xaml.

    The UWP App just need a Key, I deleted because I dont want to make it public here,

    Thank you :)

  • AgredoAgredo USMember ✭✭

    @JoeHarvey_MSFT @LandLu Thank you for helping me! I forgot base.OnElementPropertyChanged(sender, e);

Sign In or Register to comment.