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?
CodePudding user response:
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>
CodePudding user response:
I will supplement the answer of @user2250152. The method he proposed for obtaining a Singleton instance in Code Behind is often very inconvenient in Design Mode, since Code Behind is not executed in this Mode. You can get the value in XAML:
<Window ---------------
---------------
DataContext="{x:Static local:MainViewModel.Instance}">
P.S. Also, in your implementation, the instance field does not make sense.
You must use either an autoproperty or initialization on first call.
public class SomeClass
{
private SomeClass() { }
public static SomeClass Instance {get;} = new SomeClass();
public class SomeClass
{
private static SomeClass? _instance;
private SomeClass() { }
public static SomeClass Instance => _instance ??= new SomeClass();