Search code examples
c#wpfmvvmdata-bindingsingleton

change property of singleton from another singleton?


I am trying to change the property "MyText" of the class "MainViewModel" from the class "ClassA" under MVVM. I'm not sure if I'm doing this correctly. Here's my code.

<Window.DataContext>
         <local:MainViewModel/>
     </Window.DataContext>
     <Grid>
         <StackPanel Orientation="Vertical">
             <TextBlock Text="{Binding MyText}"
                        Margin="50"/>
             <Button Content="Main Class"
                     Width="150"
                     Height="70"
                     Margin="50"
                     Command="{Binding Cmd}"
                     CommandParameter="MainClass"/>
    
             <Button Content="Class A"
                     Width="150"
                     Height="70"
                     Command="{Binding Cmd}"
                     CommandParameter="ClassA" />
         </StackPanel>
     </Grid>
    public class MainViewModel : BaseViewModel
    {
        private static MainViewModel instance = new MainViewModel();
        static MainViewModel()
        {
            instance = new MainViewModel();
        }
        public static MainViewModel Instance
        {
            get => instance;
        }
        private string myText = string.Empty;
        public string MyText
        {
            get
            {
                return myText;
            }
            set
            {
                if( myText != value)
                {
                    myText = value;
                    OnPropertyChanged();
                }
            }
        }
        public RelayCommand Cmd { get => new RelayCommand(CmdExec); }
        private void CmdExec(object parameter)
        {
            switch (parameter.ToString())
            {
                case "MainClass":
                    try
                    {
                        MyText = "I am from Class -> MainViewModel!";
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(ex.Message);
                    }
                    break;

                case "ClassA":
                    try
                    {
                       // MyText = "I'am from Class -> ClassA!";
                        ClassA.Instance.ChangeText();
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(ex.Message);
                    }
                    break;
                default:
                    break;
            }
        }
    }
    public class ClassA
    {
        private static readonly ClassA instance=new ClassA();

        static ClassA()
        {
           
        }
        private ClassA() { }
        public static ClassA Instance
        {
            get => instance;
        }
        public void ChangeText()
        {
            MainViewModel.Instance.MyText = "I'am from Class -> ClassA!";
        }
    }
    public class BaseViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged([CallerMemberName] string name = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }
    }
    public class RelayCommand : ICommand
    {
        private readonly Action<object> _execute;
        private readonly Predicate<object> _canExecute;

        public RelayCommand(Action<object> execute)
            : this(execute, null)
        {
        }
        public RelayCommand(Action<object> execute, Predicate<object> canExecute)
        {
            if (execute == null)
                throw new ArgumentNullException("execute");
            _execute = execute;
            _canExecute = canExecute;
        }
        public bool CanExecute(object parameter)
        {
            return _canExecute == null ? true : _canExecute(parameter);
        }
        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }
        public void Execute(object parameter)
        {
            _execute(parameter);
        }
    }

Unfortunately it does not work for me. It should be a simple call. Does anyone have any idea what might be doing wrong? What should I do?


Solution

  • Because DataContext is set to another instance of MainViewModel.

     <Window.DataContext>
         <local:MainViewModel/>
     </Window.DataContext>
    

    You still have public constructor of MainViewModel, so the code above creates a new instance of MainViewModel.

    You need to have private constructor of MainViewModel to make sure that the only one instance of a class can ever be created.

    public class MainViewModel : BaseViewModel
    {
        private static MainViewModel instance = new MainViewModel();
        static MainViewModel()
        {
            instance = new MainViewModel();
        }
        private MainViewModel(){}
        public static MainViewModel Instance
        {
            get => instance;
        }
        ...
    }
    

    Then you need to set DataContext of MainWindow in code behind

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = MainViewModel.Instance;
        }
        ...
    }
    

    Get rid of

    <Window.DataContext>
         <local:MainViewModel/>
     </Window.DataContext>