Search code examples
c#xamlxamarin.formsxamarin.androidtoggle

How to bind two Switch controls in separate classes to the same bool property?


I am making a preference center that needs to sync updates for two separate Switch toggles with the same boolean/ toggle value. I am using Xamarin Forms with C#. I have a ViewModel.cs file such as

namespace XamarinDemoApp
public class MyViewModel : INotifyPropertyChanged
{
    private bool swithOne;
    public bool SwithOne
    {
        set
        {
            if (swithOne != value)
            {
                swithOne = value;
                OnPropertyChanged("SwithOne");
            }
        }
        get
        {
            return swithOne;
        }
    }

public MyViewModel()
    {
        SwithOne = true; // assign a value for `SwithOne `
    }

    bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
    {
        if (Object.Equals(storage, value))
            return false;

        storage = value;
        OnPropertyChanged(propertyName);
        return true;
    }

    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    public event PropertyChangedEventHandler PropertyChanged;

    }
}

Then my AllowSaleToggleTab.xaml.cs looks like

namespace XamarinDemoApp
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class AllowSaleToggleTab : ContentPage
{
    MyViewModel myViewModel;
    public AllowSaleToggleTab()
    {
        InitializeComponent();
        myViewModel = new MyViewModel();
        BindingContext = myViewModel;
    }
}
}

The other toggle tab is PC.xaml.cs

namespace XamarinDemoApp
{
[XamlCompilation(XamlCompilationOptions.Compile)]

public partial class PC : ContentPage
{
    MyViewModel myViewModel;

    public PC()
    {
        InitializeComponent();
        Console.WriteLine("PC Initialized");
        myViewModel = new MyViewModel();
        BindingContext = myViewModel;
    }
}
}

Finally, both my accompanying PC.xaml and AllowSaleToggleTab.xaml files have the elements

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:xamarindemoapp="clr-namespace:XamarinDemoApp" x:DataType="xamarindemoapp:MyViewModel"
             x:Class="XamarinDemoApp.AllowSaleToggleTab">
    <Switch x:Name="ToggleSwitch1"  IsToggled="{Binding SwithOne}"/>

Yet they still don't sync. Can anyone point out what I'm doing wrong? Thank you


Solution

  • I don't know how your code is used,but there are several methods to achieve this.

    For example,you can pass the data between two different Pages when navigating or use Xamarin.Forms MessagingCenter to send the data message between the two pages.

    You can refer to the following code(I combined the two methods above):

    TestPage1.xaml

    <ContentPage.Content>
        <StackLayout>
            <Switch x:Name="toggleSwitch1" IsToggled="{Binding SwithOne}"   HorizontalOptions="Center">
            </Switch>
            <Button  Text="navigate to Page2"  Clicked="Button_Clicked"/>
        </StackLayout>
    </ContentPage.Content>
    

    TestPage1.xaml.cs

    public partial class TestPage1 : ContentPage
    {
         MyViewModel myViewModel;
        public TestPage1()
        {
            InitializeComponent();
    
            myViewModel = new MyViewModel();
            BindingContext = myViewModel;
        }
        private async void Button_Clicked(object sender, EventArgs e)
        {
            await Navigation.PushAsync( new TestPage2(myViewModel.SwithOne));//myViewModel.SwithOne
        }
    }
    

    MyViewModel.cs

    public  class MyViewModel: INotifyPropertyChanged
    {       
        private  bool _swithOne { get; set; }
        public  bool SwithOne
        {
            set
            {
                if (_swithOne != value)
                {
                    _swithOne = value;
                    OnPropertyChanged("SwithOne");
    
                }
            }
            get
            {
                return _swithOne;
            }
        }
    
        public MyViewModel()
        {
            SwithOne = true;
    
            MessagingCenter.Subscribe<object, object>(this, "PassDataToOne", (sender, args) =>
            {
                bool value = (bool)args;
    
                if (value!= SwithOne) {
    
                    SwithOne = value;
                }
              
            });
        }
    
        bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
        {
            if (Object.Equals(storage, value))
                return false;
    
            storage = value;
            OnPropertyChanged(propertyName);
            return true;
        }
    
        protected   void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
    
    }
    

    TestPage2.xaml

    <ContentPage.Content>
        <StackLayout>
            <Switch x:Name="toggleSwitch2" IsToggled="{Binding SwithTwo}"   HorizontalOptions="Center" />
    
            <Button  Text="Navigate To  Page 1" Clicked="Button_Clicked"/>
        </StackLayout>
    </ContentPage.Content>
    

    TestPage2.xaml.cs

    public partial class TestPage2 : ContentPage
    {
        MyViewModel2 myViewModel;
    
        public TestPage2(bool isToggled)
        {
            InitializeComponent();
    
             myViewModel = new MyViewModel2(isToggled);
    
            BindingContext = myViewModel;
        }
    
        private async void Button_Clicked(object sender, EventArgs e)
        {
            await Navigation.PopAsync();
        }
    }
    

    MyViewModel2.cs

    public class MyViewModel2: INotifyPropertyChanged
    {
        private bool _swithTwo;
        public bool SwithTwo
        {
            set
            {
                if (_swithTwo != value)
                {
                    _swithTwo = value;
                    OnPropertyChanged("SwithTwo");
    
                    MessagingCenter.Send<object, object>(this, "PassDataToOne", _swithTwo);
    
                }
            }
            get
            {
                return _swithTwo;
            }
        }
    
        public MyViewModel2( bool isToggled)
        {
            SwithTwo = isToggled;
        }
    
    
        protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
    }
    

    Note:

    1.I Used two different ViewModels for the two pages(TestPage1 and TestPage2), TestPage1's ViewModel is MyViewModel, and TestPage1's ViewModel is MyViewModel2.

    2.After changing the status of Switch in TestPage1,we can pass the value SwithOne in MyViewModel.cs to TestPage2 by the Constructor:

        private async void Button_Clicked(object sender, EventArgs e)
        {
            await Navigation.PushAsync( new TestPage2(myViewModel.SwithOne));//myViewModel.SwithOne
        }
    

    3.If we change the value of SwithTwo in TestPage2,we can use MessagingCenter to send the value to TestPage1:

       public class MyViewModel2: INotifyPropertyChanged
    {
        private bool _swithTwo;
        public bool SwithTwo
        {
            set
            {
                if (_swithTwo != value)
                {
                    _swithTwo = value;
                    OnPropertyChanged("SwithTwo");
    
                    MessagingCenter.Send<object, object>(this, "PassDataToOne", _swithTwo);
    
                }
            }
            get
            {
                return _swithTwo;
            }
        }
    // other code
    }