Your explanations are not enough for a precise answer.
To do this, we need to know why you need a global reference to the ViewModel instance.
Therefore, I will describe several options.
1) If:
- in general, in principle, under no circumstances is it assumed that a ViewModel can have several instances at the assembly level in which it is created;
- if this does not create any security problems, since the static instance can be accessed by everyone;
- if static values are sufficient to create a single instance. In most cases, this means that the ViewModel has only one non-parameterized constructor.
Then in this case it is worth using Singleton.
Example:
public class MainWindowViewModel : ViewModelBase
{
// The only instance available outside of this class.
public static MainWindowViewModel Instanse { get; }
= new MainWindowViewModel();
// All constructors must be MANDATORY HIDDEN.
private MainWindowViewModel()
{
// Some code
}
// Some code
}
To get this instance in XAML, x: Static is used.
You can get the entire instance, or create a binding to a separate property.
<SomeElement
DataContext="{x:Static vm:MainWindowViewModel.Instance}"/>
<SomeElement
Command="{Binding ButtonCommandEvent,
Source={x:Static vm:MainWindowViewModel.Instance}}"/>
2) If:
- ViewModel is in another assembly, it has open constructors, but the current assembly needs only one instance of it;
- if this does not create any security problems, since the static instance can be accessed by everyone;
- if static values are sufficient to create a single instance. In most cases, this means that the ViewModel has only one non-parameterized constructor.
In this case, you should use a static class with a single instance.
This static class is created in the current assembly.
Usually it is a View project.
Your “class Globals” is an example of such an implementation.
Example usage in XAML:
<SomeElement
DataContext="{x:Static local:Globals.MainWindowViewModel}"/>
<SomeElement
Command="{Binding ButtonCommandEvent,
Source={x:Static local:Clobals.MainWindowViewModel}}"/>
3) If:
- instances of ViewModel can replace each other, but only one instance is used at a time;
- if this does not create any security problems, since the static instance can be accessed by everyone.
In this case, it is worth using a static class with the current instance of the ViewModel and providing notification of the replacement of this instance.
An example of such an implementation:
public static class Globals
{
private static MainWindowViewModel _mainWindowViewModel = new MainWindowViewModel();
public static MainWindowViewModel MainWindowViewModel
{
get => _mainWindowViewModel;
set => Set(ref _mainWindowViewModel, value);
}
public static event EventHandler<PropertyChangedEventArgs> StaticPropertyChanged;
private static void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
StaticPropertyChanged?.Invoke(null, new PropertyChangedEventArgs(propertyName));
}
private static void Set<T>(ref T propertyFiled, in T newValue, [CallerMemberName] in string propertyName = null)
{
if (!Equals(propertyFiled, newValue))
{
propertyFiled = newValue;
RaisePropertyChanged(propertyName);
}
}
}
Example usage in XAML:
<SomeElement
DataContext="{Binding Path=(local:Globals.MainWindowViewModel)}"/>
<SomeElement
Command="{Binding Path=(local:Globals.MainWindowViewModel).ButtonCommandEvent}"/>
4) If this is only needed for all windows in the application, then it is better to instantiate the ViewModel in the App Resources.
An example of such an implementation:
<Application x:Class="***.App"
---------------------
--------------------->
<Application.Resources>
<local:MainWindowViewModel x:Key="mainViewModel"/>
</Application.Resources>
</Application>
Example usage in XAML:
<SomeElement
DataContext="{StaticResource mainViewModel}"/>
<SomeElement
Command="{Binding ButtonCommandEvent,
Source={StaticResource mainViewModel}}"/>
5) If this is needed for all windows in the application, but you need the ability to replace an instance, or to create an instance you need a constructor with parameters that are calculated after the application starts, then it is better to create an additional class that will provide this instance and other data necessary for all Windows.
An example of such an implementation:
public class Locator : INotifyPropertyChanged
{
private MainWindowViewModel _mainWindowViewModel = new MainWindowViewModel();
public MainWindowViewModel MainWindowViewModel
{
get => _mainWindowViewModel;
set => Set(ref _mainWindowViewModel, value);
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(null, new PropertyChangedEventArgs(propertyName));
}
private void Set<T>(ref T propertyFiled, in T newValue, [CallerMemberName] in string propertyName = null)
{
if (!Equals(propertyFiled, newValue))
{
propertyFiled = newValue;
RaisePropertyChanged(propertyName);
}
}
#endregion
}
<Application x:Class="***.App"
---------------------
--------------------->
<Application.Resources>
<local:Locator x:Key="locator">
<local:Locator.MainWindowViewModel>
<local:MainWindowViewModel/>
</local:Locator.MainWindowViewModel>
</local:Locator>
</Application.Resources>
</Application>
Or:
<Application x:Class="***.App"
---------------------
---------------------
Startup="OnStartup">
<Application.Resources>
<local:Locator x:Key="locator"/>
</Application.Resources>
</Application>
public partial class App : Application
{
private void OnStartup(object sender, StartupEventArgs e)
{
Locator locator = (Locator)Resources["locator"];
// Some Code
locator.MainWindowViewModel = new MainWindowViewModel(/* Some Parameters*/);
}
}
Example usage in XAML:
<SomeElement
DataContext="{Binding MainWindowViewModel,
Source={StaticResource locator}}"/>
<SomeElement
Command="{Binding MainWindowViewModel.ButtonCommandEvent,
Source={StaticResource locator}"/>
With regard to the “Communication” class.
It does not work directly with UI elements, so there is no need to use a “DispatcherTimer” in it.
The main thread (namely it the DispatcherTimer uses) performs many of its tasks and does not need to load it with your tasks own unnecessarily.
Replace DispatcherTimer with any asynchronous timer:
System.Timers.Timer, System.Threading.Timer, etc.
5
solved Is it a correct approach to create static viewModel in MVVM? [closed]