Dominik Roszkowski
Dominik Roszkowski

Reputation: 2543

How to bind Preference controls (SwitchPreference) in MvvmCross?

Problem

I'd like to bind value of SwitchPreferenceCompat control on PreferenceScreen to the view model property with MvvmCross.

Binding for some controls like EditTextPreference works perfectly, but for others (CheckBoxPreference or SwitchPreferenceCompat) unfortunately not. I can see in the debug log that the property value is not changed for these controls.

Environment

I use MvvmCross 6.0 and Xamarin.Android with Visual Studio for Mac 7.4.3 (build 10). Additionally, I use Xam.Plugins.Settings to store settings.

I run the app on Android Emulator running Android 8.0.

Implementation details/MWW

Settings fragment fragment_settings.axml:

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:local="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <PreferenceCategory
        android:title="Basic settings">
        <SwitchPreferenceCompat
            android:key="notifications_switch"
            android:title="Switch Preference"
            android:summary="Switch Summary"
            android:defaultValue="true" />
        <CheckBoxPreference
            android:key="notifications_checkbox"
            android:title="Enable notifications"
            android:summary="Do you want to receive push notifications?"
            android:defaultValue="true" />
        <EditTextPreference
            android:key="username_edittext"
            android:title="UserName"
            android:summary="Summary"
            android:dialogMessage="Give your username if you want"
            android:defaultValue="" />
    </PreferenceCategory>
</PreferenceScreen>

SettingsView.cs

using Android.Runtime;
using App.Core.ViewModels.Main;
using App.Core.ViewModels.Settings;
using MvvmCross.Platforms.Android.Presenters.Attributes;
using MvvmCross.Droid.Support.V7.Preference;
using MvvmCross.Binding.BindingContext;
using Android.OS;
using Android.Views;
using Android.Support.V7.Preferences;

namespace App.Droid.Views.Settings
{
    [MvxFragmentPresentation(typeof(MainContainerViewModel),
                            Resource.Id.content_frame,
                            true,
                            Resource.Animation.abc_fade_in,
                            Resource.Animation.abc_fade_out,
                            Resource.Animation.abc_fade_in,
                            Resource.Animation.abc_fade_out)]
    [Register(nameof(SettingsView))]
    public class SettingsView : MvxPreferenceFragmentCompat<SettingsViewModel>// BaseFragment<SettingsViewModel>
    {
        public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
        {
            var view = base.OnCreateView(inflater, container, savedInstanceState);

            var notifPreference = (CheckBoxPreference)this.FindPreference("notifications_checkbox");
            var switchPreference = (SwitchPreferenceCompat)this.FindPreference("notifications_switch");
            var usernamePreference = (EditTextPreference)this.FindPreference("username_edittext");

            var bindingSet = this.CreateBindingSet<SettingsView, SettingsViewModel>();
            bindingSet.Bind(switchPreference)
                .For(v => v.Checked)
                .To(vm => vm.NotificationsEnabled);
            bindingSet.Bind(notifPreference)
                .For(v => v.Checked)
                .To(vm => vm.NotificationsEnabled);
            bindingSet.Bind(usernamePreference)
                .For(v => v.Text)
                .To(vm => vm.UserName);
            bindingSet.Apply();

            return view;
        }

        public override void OnCreatePreferences(Bundle savedInstanceState, string rootKey)
        {
            this.AddPreferencesFromResource(Resource.Layout.fragment_settings);
        }
    }
}

And finally the view model itself. SettingsViewModel.cs:

using System.Diagnostics;
using MvvmCross.Commands;
using MvvmCross.Navigation;
using Plugin.Settings;
using Plugin.Settings.Abstractions;

namespace App.Core.ViewModels.Settings
{
    public class SettingsViewModel : MvxViewModel
    {
        private readonly IMvxNavigationService _navigationService;
        private static ISettings AppSettings => CrossSettings.Current;
        public IMvxAsyncCommand CloseCommand { get; private set; }

        public SettingsViewModel(IMvxNavigationService navigationService)
        {
            Debug.WriteLine("View Settings showing");
            _navigationService = navigationService;
            CloseCommand = new MvxAsyncCommand(async () => await _navigationService.Close(this));
        }

        public string UserName
        {
            get => AppSettings.GetValueOrDefault(nameof(UserName), string.Empty);
            set
            {
                Debug.WriteLine($"Setting property Username to {value}");
                AppSettings.AddOrUpdateValue(nameof(UserName), value);
            }
        }

        public bool NotificationsEnabled
        {
            get => AppSettings.GetValueOrDefault(nameof(NotificationsEnabled), false);
            set
            {
                Debug.WriteLine($"Setting property NotificationsEnabled to {value}");
                AppSettings.AddOrUpdateValue(nameof(NotificationsEnabled), value);
            }
        }
    }
}

Already tried

I tried adding switch in LinkerPleaseInclude.cs as suggested in mvvmcross binding on switch fails on release

    public void Include(Switch @switch)
    {
        @switch.CheckedChange += (sender, args) => @switch.Checked = [email protected];
    }

    public void Include(SwitchPreferenceCompat switchPreference)
    {
        switchPreference.PreferenceChange += (sender, args) => switchPreference.Checked = !switchPreference.Checked;
    }

Upvotes: 0

Views: 898

Answers (1)

asterixorobelix
asterixorobelix

Reputation: 138

I believe that the problem does not lie with your bindings. The issue appears to be with how you apply the value of your view model property to the AppSettings. I have been able to successfully take your preference screen xml and apply a view and view model binding. The value of the viewmodel property updates when the switch button is clicked.

You can inspect the code which I used to do the successful binding here Github Repo

Upvotes: 1

Related Questions