Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Xamarin Forms - force a control to refresh value from binding

Given the following ViewModel...

public class NameEntryViewModel 
{
    public NameEntryViewModel()
    {
        Branding = new Dictionary<string, string>();

        Branding.Add("HeaderLabelText", "Welcome to the app");
    }

    public Dictionary<string, string> Branding { get; set; }
}

Bound to the page...

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Monaco.Forms.Views.NameEntryPage">

        <Label Text="{Binding Branding[HeaderLabelText]}" />

</ContentPage>

When the page comes up the Label will get the text 'Welcome to the app'. This works great and fits into our plan for being able to customize and globalize our app. Then Branding dictionary is statically set in this example, but in the real world it is initialized by data from a service call.

However, should the user want to switch the language to Spanish, we would need each bound label to update to the new value. It's easy enough to reset the Branding dictionary and fill it with Spanish translations, but how can we force the controls to refresh from their bound sources?

I am trying to avoid two-way data binding here b/c we don't want the code overhead of creating a backing property for each Text property of the controls. Hence we are binding to a dictionary of values.

ANSWER

I accepted the answer below, but I didn't use a traditional property setter. Instead when a user wants to toggle a different language, we now have a centralized handler that repopulates our Dictionary and then notifies of the change to the Dictionary. We are using MVVMCross, but you can translate to standard forms...

    public MvxCommand CultureCommand
    {
        get
        {
            return new MvxCommand(async () =>
            {
                _brandingService.ToggleCurrentCulture();
                await ApplyBranding(); // <-- this call repopulates the Branding property
                RaisePropertyChanged(() => Branding);
            });
        }
    }
like image 663
John Livermore Avatar asked Feb 08 '17 22:02

John Livermore


1 Answers

As @BillReiss mentioned you need to use the OnPropertyChanged event inherit the NameEntryViewModel from this class:

public class BaseViewModel : INotifyPropertyChanged
{

    protected BaseViewModel ()
    {

    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged ([CallerMemberName] string propertyName = null)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
            handler (this, new PropertyChangedEventArgs (propertyName));
    }
}

And create a private property that you can assign to you public property something like:

Dictionary<string, string> _branding = new Dictionary<string, string>();
public Dictionary<string, string> Branding
{
    get
    {
        return _branding;
    }
    set
    {
        _branding = value;
        OnPropertyChanged(nameof(Branding));
    }
}

And with this every time you set the Branding property it will let the View know that something changed! Sometimes if you are doing this on a back thread you have to use Device.BeginInvokeOnMainThread()

like image 149
Mario Galván Avatar answered Sep 18 '22 17:09

Mario Galván