I have a multi-page MVVM application that has a few ViewModels. (It's a hobby app, storing statistics and stuff)
The following classes are
GamePageViewModel
MenuViewModel
PlayersViewModel
StartPagePresenter
Controller (This acts as a PageSwitcher and as the model that stores all of my TableAdapters from a database)
All of them have a few of the same variables
Universe
Character
Season
What I'd like to do is move these variables to my MainMenuViewModel (which is the menubar on all of the pages), where you select Universe, Character, Season, etc, and they populate in the other ViewModels.
my MenuViewModel Class
public class MenuViewModel : ObservableObject
{
private DataView _Universes = Controller.UniverseTableAdapter.GetData().DefaultView;
private DataRowView _SelectedUniverse;
public DataView Universes
{
get { return _Universes; }
set
{
_Universes = value;
RaisePropertyChangedEvent("Universes");
}
}
public DataRowView SelectedUniverse
{
get { return _SelectedUniverse; }
set
{
_SelectedUniverse = value;
Controller.SelectedUniverse = _SelectedUniverse;
RaisePropertyChangedEvent("SelectedUniverse");
}
}
public ICommand GoHome { get { return new DelegateCommand(NavigateHome); } }
public ICommand GoPlayers { get { return new DelegateCommand(NavigatePlayers); } }
public ICommand GoGame { get { return new DelegateCommand(NavigateGame); } }
public void NavigateHome() { Controller.Switch(new StartPage()); }
public void NavigatePlayers() { Controller.Switch(new PlayerPage()); }
public void NavigateGame() { Controller.Switch(new GamePage()); }
}
I thought I'd try to store them in the Controller Class like this, but not sure how I would send out the RaisePropertyChangedEvent Events to each of the ViewModels
public static class Controller
{
#region Variables
//Variables
private static MainWindow _Window;
private static MaddenDBDataSet _MaddenDB = new MaddenDBDataSet();
private static CharacterTableAdapter _CharacterTableAdapter = new CharacterTableAdapter();
private static FranchiseTableAdapter _FranchiseTableAdapter = new FranchiseTableAdapter();
private static GamePlayerTableAdapter _GamePlayerTableAdatper = new GamePlayerTableAdapter();
private static GameTableAdapter _GameTableAdapter = new GameTableAdapter();
private static PersonTableAdapter _PersonTableAdapter = new PersonTableAdapter();
private static SeasonTableAdapter _SeasonTableAdapter = new SeasonTableAdapter();
private static UniverseTableAdapter _UniverseTableAdapter = new UniverseTableAdapter();
private static UserYearTableAdapter _UserYearTableAdapter = new UserYearTableAdapter();
private static CollegeTableAdapter _CollegeTableAdapter = new CollegeTableAdapter();
private static view_PlayersTableAdapter _PlayerView = new view_PlayersTableAdapter();
private static view_UniverseCharactersTableAdapter _UniverseCharacterView = new view_UniverseCharactersTableAdapter();
private static UniverseCharacterTableAdapter _UniverseCharacterTableAdapter = new UniverseCharacterTableAdapter();
private static view_ScheduleTableAdapter _ScheduleView = new view_ScheduleTableAdapter();
private static view_YearsTableAdapter _YearView = new view_YearsTableAdapter();
private static View_GameStatsTableAdapter _GameStatsView = new View_GameStatsTableAdapter();
private static view_RegularSeasonTableAdapter _SeasonView = new view_RegularSeasonTableAdapter();
private static view_PlayoffsTableAdapter _PlayoffsView = new view_PlayoffsTableAdapter();
//Keep a tab of all of our ViewModels
private static DataRowView _SelectedUniverse = null;
public static DataRowView SelectedUniverse { get { return _SelectedUniverse; } set { _SelectedUniverse = value; } }
#region Methods
//Navigation
public static void Switch(UserControl newPage)
{
_Window.Navigate(newPage);
}
public static void Switch(UserControl newPage, object state)
{
_Window.Navigate(newPage, state);
}
#endregion
}
Any pointers on storing global variables would be much appreciated!
This is something I did using MvvmCross, with an example for storing selected language (a global var needed). I also use the same pattern for versions, etc. Here is part of my SettingsPreferences class:
public static string SelectedLanguage
{
get { return Settings.AppSettings.GetValueOrDefault(StringConstants.SelectedLanguage, SelectedLanguageDefault); }
set { Settings.AppSettings.AddOrUpdateValue(StringConstants.SelectedLanguage, value); }
}
public static bool FirstRun
{
get { return Settings.AppSettings.GetValueOrDefault(StringConstants.FirstRun, FirstRunDefault); }
set { Settings.AppSettings.AddOrUpdateValue(StringConstants.FirstRun, value); }
}
public static int LatestUpdateVersion
{
get { return Settings.AppSettings.GetValueOrDefault(StringConstants.LatestUpdateVersion, LatestUpdateVersionDefault); }
set { Settings.AppSettings.AddOrUpdateValue(StringConstants.LatestUpdateVersion, value); }
}
public static string LatestUpdateVersionDate
{
get { return Settings.AppSettings.GetValueOrDefault(StringConstants.LatestUpdateVersionDate, LatestUpdateVersionDateDefault); }
set { Settings.AppSettings.AddOrUpdateValue(StringConstants.LatestUpdateVersionDate, value); }
}
Settings is a wrapper to a nuget package for persisting stuff (supports Android, iOS, Windows Store (Windows Phone 8.1 and Windows Store 8.1) and WPF projects):
public static class Settings
{
public static ISettings AppSettings
{
get
{
return CrossSettings.Current;
}
}
}
Now my LanguageViewModel calls it simply like this:
var lang = SettingsPreferences.SelectedLanguage;
To update the setting, simply:
SettingsPreferences.SelectedLanguage = "en";
It can be anything really.
Related
I want to organize my code as well as I can, but I have some trouble with its organization. I try to use SOLID principles and make separate entities. I want to use MVVM to view (unity-weld as well), and DI container (zenject as well).
It's my first project and I'm trying to organize code.
My question is how to inject LoginViewModel into LoginController as static class using zenject container from GameInstaller class.
[Binding]
public class LoginViewModel : MonoBehaviour, INotifyPropertyChanged
{
private string _username = "";
private string _passsword = "";
private string _errorMessage = "";
private bool _autologin = false;
[Inject]
private LoginController _loginController;
[Binding]
public string Username {
get
{
return _username;
}
set
{
if (_username == value)
{
return; // No change.
}
_username = value;
Debug.Log($"SET username: {value}");
OnPropertyChanged("Username");
}
}
[Binding]
public string Password {
get
{
return _passsword;
}
set
{
if (_passsword == value)
{
return; // No change.
}
_passsword = value;
Debug.Log($"SET password: {value}");
OnPropertyChanged("Password");
}
}
[Binding]
public bool Autologin {
get
{
return _autologin;
}
set
{
if (_autologin == value)
{
return; // No change.
}
_autologin = value;
Debug.Log($"SET autologin: {value}");
OnPropertyChanged("Autologin");
}
}
[Binding]
public void LoginButtonClick()
{
Debug.Log("LoginButtonClick");
_loginController.Login(this);
//ErrorMessage = "blabla";
}
[Binding]
public string ErrorMessage
{
get
{
return _errorMessage;
}
set
{
if (_errorMessage == value)
{
return; // No change.
}
_errorMessage = value;
Debug.Log($"SET errorMessage: {value}");
OnPropertyChanged("ErrorMessage");
}
}
/// <summary>
/// Event to raise when a property's value has changed.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class LoginController
{
private readonly ApiController _apiController;
[Inject]
private readonly LoginViewModel _loginViewModel;
public LoginController(ApiController apiController)
{
_apiController = apiController;
}
public void Login(LoginViewModel loginViewModel)
{
try
{
string userJson = _apiController.PostLogin(loginViewModel.Username);
_loginViewModel.ErrorMessage = "bla bla trololo";
Debug.Log(userJson);
}
catch (Exception ex)
{
throw ex;
}
}
}
public class GameInstaller : MonoInstaller
{
[Inject]
Settings _settings = null;
public override void InstallBindings()
{
InstallViewModels();
InstallServices();
InstallSignals();
InstallControllers();
}
private void InstallViewModels()
{
Container.Bind<LoginViewModel>().AsSingle();
}
private void InstallControllers()
{
Container.Bind<LoginController>().AsSingle().NonLazy();
Container.Bind<ApiController>().AsSingle().NonLazy();
}
private void InstallServices()
{
}
private void InstallSignals()
{
}
[Serializable]
public class Settings
{
}
}
Let's start from here: https://unitylist.com/p/ja3/Unity-MVVM
A ViewModel (or VM) is what holds the data that will be presented on a view. It contains all the properties that can be bound to view elements. All ViewModels inherit from INotifyPropertyChanged which alerts the system when data changes and a UI element needs to be updated.
Since the ViewModel is in it's essence a simple object and not a service, I argue that it does not need go be injected anywhere.
What you could do, is inject a Factory and get your ViewModel from it.
In your code you are trying to inject the controller into your ViewModel and not the other way round.
So, I have a statusbar as UserControl.
Model:
public class StatusBarModel : BindableBase
{
private string _status;
public string Status
{
get { return _status; }
set
{
_status = value;
RaisePropertyChanged("Status");
}
}
private int _p_value;
public int P_Value
{
get { return _p_value; }
set
{
_p_value = value;
RaisePropertyChanged("P_Value");
}
}
}
ViewModel:
public class StatusBarVM : BindableBase
{
readonly source.elements.StatusBar.StatusBarModel _model = new source.elements.StatusBar.StatusBarModel();
public StatusBarVM()
{
_model.PropertyChanged += (s, e) => { RaisePropertyChanged(e.PropertyName); };
}
public string Status
{
get { return _model.Status; }
set { _model.Status = value; }
}
public int P_Value
{
get { return _model.P_Value; }
set { _model.P_Value = value; }
}
}
And for example I wanna change Status variable from others ViewModels.
How I can do it? I have seen examples with only buttons and etc.
There are multiple ways to achieve your requirement. as #bitclicker says, you can use static class that hold its value. But I think It is too much that makes it static class, because that variable value may be used only two viewmodel.
I suggest you communicate between two view model. you will find Prism's event aggregator or you could implement your own event publish-subscriber model. making your own event pub-sub model would help you to make a first step into the design pattern.
You could create a static class to hold that value.
public static class Globals()
{
public static StatusBarModel GlobalStatus { get; set; }
}
Then whenever you want to alter it you just do
Globals.GlobalStatus.Status = "something";
Globals.GlobalStatus.P_Value = 14;
does that accomplish what you need?
I'm trying to create a UserControl that exposes 2 other controls, UI and Host, so that I can use MediaPlayerWpf and set properties in the designer like UI.AllowPause and Host.Volume
I understand Attached Properties are what I need. Furthermore, I need to allow setting the Host, while UI only exposes its properties but can't be changed.
I can't get properties UI and Host to display in the designer. What am I doing wrong?
public partial class MediaPlayerWpf {
public MediaPlayerWpf() {
InitializeComponent();
SetUI(this, MediaUI);
}
public static DependencyPropertyKey UIPropertyKey = DependencyProperty.RegisterAttachedReadOnly("UI", typeof(PlayerControls), typeof(MediaPlayerWpf), new PropertyMetadata(null));
public static DependencyProperty UIProperty = UIPropertyKey.DependencyProperty;
public PlayerControls UI { get => (PlayerControls)base.GetValue(UIProperty); private set => base.SetValue(UIPropertyKey, value); }
[AttachedPropertyBrowsableForType(typeof(MediaPlayerWpf))]
public static PlayerControls GetUI(UIElement element) {
return (PlayerControls)element.GetValue(UIProperty);
}
public static void SetUI(UIElement element, PlayerControls value) {
element.SetValue(UIPropertyKey, value);
}
public static DependencyProperty HostProperty = DependencyProperty.RegisterAttached("Host", typeof(PlayerBase), typeof(MediaPlayerWpf), new PropertyMetadata(null, OnHostChanged));
public PlayerBase Host { get => (PlayerBase)base.GetValue(HostProperty); set => base.SetValue(HostProperty, value); }
private static void OnHostChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
MediaPlayerWpf P = d as MediaPlayerWpf;
if (e.OldValue != null)
P.HostGrid.Children.Remove(e.OldValue as PlayerBase);
if (e.NewValue != null) {
P.HostGrid.Children.Add(e.NewValue as PlayerBase);
GetUI(P).PlayerHost = e.NewValue as PlayerBase;
}
}
[AttachedPropertyBrowsableForType(typeof(MediaPlayerWpf))]
public static PlayerBase GetHost(UIElement element) {
return (PlayerBase)element.GetValue(HostProperty);
}
public static void SetHost(UIElement element, PlayerBase value) {
element.SetValue(HostProperty, value);
}
}
This is my UnityResolver Class to create the instance of IUnityContainer
public sealed class UnityResolver
{
private static IUnityContainer _unityContainer;
private static volatile UnityResolver _unityresolverinstance;
private static object syncRoot = new Object();
public static IUnityContainer UnityContainerInitiation
{
get
{
if (_unityContainer == null)
{
if (_unityresolverinstance == null)
{
lock (syncRoot)
{
if (_unityresolverinstance == null)
_unityresolverinstance = new UnityResolver();
}
}
}
return UnityResolver._unityContainer;
}
}
public UnityResolver()
{
_unityContainer = new UnityContainer();
_unityContainer.RegisterType<MaintainRouteViewModel>();
}
}
Below is my Base View and Its ViewModelCode
public partial class MaintainRouteView : UserControl
{
public MaintainRouteViewModel maintainRouteViewModel = null;
IUnityContainer container;
public MaintainRouteView()
{
InitializeComponent();
container = UnityResolver.UnityContainerInitiation;
maintainRouteViewModel = container.Resolve<MaintainRouteViewModel>();
this.DataContext = maintainRouteViewModel;
}
///This button will navigate to the child view.
private void AddRoute_Click(object sender, RoutedEventArgs e)
{
pageAnimationControl.ShowPage(new AddNewRouteView());
}
}
Its ViewModel..
public class MaintainRouteViewModel : viewModelbase
{
private string _statusSuccessMessage = null;
private string _statusFailMessage =null;
private ObservableCollection<RouteDetailsModel> _routeDetailsCollection;
public ObservableCollection<RouteDetailsModel> routeDetailsCollection
{
get
{
return this._routeDetailsCollection;
}
set
{
this._routeDetailsCollection = value;
RaisePropertyChanged("routeDetailsCollection");
}
}
public string StatusSuccessMessage
{
get
{
return _statusSuccessMessage;
}
set
{
_statusSuccessMessage = value;
this.RaisePropertyChanged("StatusSuccessMessage");
}
}
public string StatusFailMessage
{
get { return _statusFailMessage; }
set
{
_statusFailMessage = value;
this.RaisePropertyChanged("StatusFailMessage");
}
}
public MaintainRouteViewModel()
{
///it will load some data to the Observablecollection
getAllCurrentRouteData();
}
}
Now Below is my Child View and its ViewModel....
public partial class AddNewRouteView : UserControl
{
public AddNewRouteView()
{
InitializeComponent();
IUnityContainer container = UnityResolver.UnityContainerInitiation;
this.DataContext = container.Resolve<AddNewRouteViewModel>();
}
}
Its ViewModel....
public class AddNewRouteViewModel : viewModelbase
{
private MaintainRouteViewModel maintainRouteViewModel;
public ICommand SaveCommand
{
get;
set;
}
[InjectionConstructor]
public AddNewRouteViewModel(MaintainRouteViewModel maintainRouteViewModel)
{
this.maintainRouteViewModel = maintainRouteViewModel;
SaveCommand = new DelegateCommand<object>((a) => ValidateNewRoute());
}
private void ValidateNewRoute()
{
bool flag = saveAndValidate();
if(flag)
{
updateRouteStatus();
}
}
public void updateRouteStatus()
{
maintainRouteViewModel.StatusSuccessMessage = "New Route successfully Added..";
}
}
}
Can Anyone Tell me how to use this way to get the same object of MaintainRouteViewModel in my Child VM Constructor So that i will show the Updated Status Message in my Base view MaintainRouteView???
*It will Work Fine If i replace my MaintainRouteView with below code :
this Is an another approach to use IOC .i previously using this in my project. it Works Fine for me but now i want to implement the same thing using Unity Container. Please Help.
public partial class MaintainRouteView : UserControl
{
public MaintainRouteViewModel maintainRouteViewModel = null;
public MaintainRouteView()
{
InitializeComponent();
maintainRouteViewModel = new MaintainRouteViewModel();
this.DataContext = maintainRouteViewModel;
}
private void AddRoute_Click(object sender, RoutedEventArgs e)
{
pageTransitionControl.ShowPage(
new AddNewRouteView
{
DataContext = new AddNewRouteViewModel(maintainRouteViewModel)
});
}
}
I am able to solve this issue using the LifeTime Management of Unity Container Register Types.
it will work fine if i tell the container to create a singleton instance of the MaintainRouteViewModel Class.
using :
container.RegisterType<MaintainRouteViewModel>(
new ContainerControlledLifetimeManager());
But it's just a workaround to get the expected result. i want to achieve it using a proper dependency injection without any singleton instance principle. Can anyone please help to provide the solution.
I have a C# WPF application built with Visual Studio 2015. I'm using MVVM and the Observer Pattern.
My Provider is a user control called 'ucClientFilter1ViewModel' that contains two text box controls where the user can search for a client(s):
namespace NSUCClientControls
{
public class ucClientFilter1ViewModel : ViewModelBase, IObservable<ClientFilterParameter>
{
private string filterLocation;
private string whereSearch1;
private string whereSearch2;
private List<IObserver<ClientFilterParameter>> observers;
public ucClientFilter1ViewModel()
{
observers = new List<IObserver<ClientFilterParameter>>();
}
public string FilterLocation
{
get { return filterLocation; }
set { filterLocation = value; }
}
public string WhereSearch1
{
get { return whereSearch1; }
set
{
whereSearch1 = value;
TestUpdateGrid(filterLocation);
}
}
public string WhereSearch2
{
get { return whereSearch2; }
set
{
whereSearch2 = value;
TestUpdateGrid(filterLocation);
}
}
private void TestUpdateGrid(string _filterLocation)
{
var filterInfo = new ClientFilterParameter(this);
foreach (var observer in observers)
{
observer.OnNext(filterInfo);
}
}
public IDisposable Subscribe(IObserver<ClientFilterParameter> observer)
{
// Check whether observer is already registered. If not, add it
if (!observers.Contains(observer))
{
observers.Add(observer);
// Provide observer with existing data
var filterInfo = new ClientFilterParameter(this);
observer.OnNext(filterInfo);
}
return new Unsubscriber<ClientFilterParameter>(observers, observer);
}
internal class Unsubscriber<ClientFilterParameter> : IDisposable
{
private IObserver<ClientFilterParameter> observer;
private List<IObserver<ClientFilterParameter>> observers;
public Unsubscriber(List<IObserver<ClientFilterParameter>> _observers, IObserver<ClientFilterParameter> _observer)
{
observers = _observers;
observer = _observer;
}
public void Dispose()
{
if (observers.Contains(observer))
{
observers.Remove(observer);
}
}
}
}
}
My Observer is a user control called 'ucClientGrid1ViewModel' that contains a datagrid where the search results are displayed.
namespace NSUCClientControls
{
public class ucClientGrid1ViewModel : ViewModelBase, IObserver<ClientFilterParameter>
{
private IDisposable cancellation;
private ObservableCollection<Client> clientsMultiple;
public ucClientGrid1ViewModel()
{
}
public ObservableCollection<Client> ClientsMultiple
{
get
{
var myClientDataAccess = new ClientDataAccess();
clientsMultiple = myClientDataAccess.GetClientListFromSQL_Test2();
return clientsMultiple;
}
set
{
}
}
public virtual void Subscribe(ucClientFilter1ViewModel provider)
{
cancellation = provider.Subscribe(this);
}
public void OnNext(ClientFilterParameter myFilter)
{
OnPropertyChanged("ClientsMultiple");
var myDummyWindow = new dummyWindow();
myDummyWindow.Show();
myDummyWindow.Close();
}
public void OnError(Exception error)
{
throw new NotImplementedException();
}
public void OnCompleted()
{
throw new NotImplementedException();
}
}
}
This all works and I get the search results that I am expecting. But what I don't understand is why the inclusion of the following lines actually speed things up!
var myDummyWindow = new dummyWindow();
myDummyWindow.Show();
myDummyWindow.Close();
I'm new to MVVM and the observer pattern, so as I was writing the code I had included message boxes at various points to help me to follow the flow of it. It was all working as expected. Then I removed the message boxes and it still worked but the application was pausing at the end before you could continue to keep searching.
Putting a message box back in at the end prevented this pause. Replacing the message box with a "DummyWindow" that just opens and closes has the same affect and prevents the pause at the end. This is what I currently have but I'd rather not leave this in there.
Presumably opening the window causes something else to happen which stops some redundant process, and this then prevents the pause? What else could I do to prevent the pause at the end, without using this DummyWindow?
I've tried searching on here and with Bing with no luck.
Thanks in advance!
Edit:
ViewModelBase...
namespace NSCommon
{
public abstract class ViewModelBase : INotifyPropertyChanged, IDisposable
{
protected ViewModelBase()
{
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
public void Dispose()
{
OnDispose();
}
protected virtual void OnDispose()
{
}
}
}
ClientFilterParameter...
namespace NSCommon
{
public class ClientFilterParameter
{
public ClientFilterParameter(ucClientFilter1ViewModel myFilter)
{
FilterLocation = myFilter.FilterLocation;
WhereSearch1 = myFilter.WhereSearch1;
WhereSearch2 = myFilter.WhereSearch2;
}
private string filterLocation;
private string whereSearch1;
private string whereSearch2;
public string FilterLocation
{
get { return filterLocation; }
set { filterLocation = value; }
}
public string WhereSearch1
{
get { return whereSearch1; }
set { whereSearch1 = value; }
}
public string WhereSearch2
{
get { return whereSearch2; }
set { whereSearch2 = value; }
}
}
}