I want to access my ViewModel from a class which is not the View. Is it OK if I do the following? Is this breaking the pattern?
namespace MyApp
{
public class GameView
{
protected new GameViewModel ViewModel
{
get { return (GameViewModel)base.ViewModel; }
}
}
}
// Derived class
namespace MyApp
{
public class InAppPurchase: GameView
{
public void BuyCoins()
{
ViewModel.PurchasedCoins += ViewModel.CoinsForSale;
}
}
}
If you want to access a ViewModel from whichever place you want you might want to send messages (MvxMessage) and handle them within the ViewModel (publish/subscribe with IMessenger). This is the proper way to communicate between ViewModels or ViewModels and other components like services in the Mvvm pattern.
Related
I Have a view model and i want to access all the properties value in another class which is not a viewmodel. So how to pass whole viewmodel as a parameter into another class method or is there some other way to do this thing?
i wantto achieve something like below
Public Class ViewModel
{
var res = XYZClass.ExecuteAsync(ViewModel);
}
Public Static Class XYZClass{
public Static Task ExecuteAsync(ViewModel request)
{
throw new NotImplementedException();
}
}
Put in a public static property reference to the instantiated view model on the app class. Then it can be accessed it from any other class.
I worked on a Silverlight project which did that very thing with the VM and other globally shared data.
Suppose that I've a ViewModel class with different property such as:
//MainVm will inherit ViewModel that contains IPropertyChanged implementation
public class MainVM : ViewModel
{
publi bool Foo { get; set; }
}
I instatiated this class on the main controller in this way:
MainController mc;
public MaiWindow()
{
mc = new MainController();
DataContext = mc;
InitializeComponent();
}
the MainController have this implementation:
public class MainController : MainVM
{
some methods
}
so each time I need to access to a property of MainController on each UserControls I need to do: Application.Current.MainWindow.mc.Foo
and this is't elegant at all.
Is possible access to the property of specific ViewModel without call the code line above?
Thanks.
UPDATE
For add more details and clarification to this question: so in my UserControl I need to access to the Foo property that is part of MainVM. I'm spoke about the UserControl xaml code (not the controller of the UserControl), this is an example:
public partial class ControlName : UserControl
{
public ControlName()
{
InitializeComponent();
}
public btnAdd_Click(object sender, RoutedEventArgs e)
{
//Suppose that I need to access to the property Foo of MainVM here
//I need to access to MainWindow instance like this:
Application.Current.MainWindow.mc.Foo
//isn't possible instead access to the property directly inherit the class, like:
MainVm.Foo
}
}
In order to get the configuration App-Wide, you could use the
ConfigurationManager.AppSettings["Whatever"];
These settings are located in the App.Config in the following form
<appSettings>
<add key="Whatever" value="Hello" />
</apSettings>
But as I undertand, you have a ViewModel that let Users change the settings, in this case you should go for:
Properties.Settings.Default.myColor = Color.AliceBlue;
You ViewModel could expose this property as:
public Color MyColor
{
get {
return Properties.Settings.Default.myColor;
}
set {
Properties.Settings.Default.myColor = value; RaisePropertyChanged();
}
}
public void Persist()
{
Properties.Settings.Default.Save();
// Raise whatever needed !
}
From other ViewModels you can access these setting as well:
Properties.Settings.Default.myColor
Have a look here https://learn.microsoft.com/en-us/dotnet/framework/winforms/advanced/using-application-settings-and-user-settings and here https://learn.microsoft.com/en-us/dotnet/framework/winforms/advanced/how-to-create-application-settings
I know that in MVVM pattern (or possibly in any design pattern of this kind) we should keep our layers decoupled. From my understanding it also means, that I should keep my ViewModels separate. I'm having a bit trouble following this rule.
Say - I have a ConversationViewModel and a MessageViewModel - the former needs to create instances of the later. When ConversationViewModel gets notification about incoming message it spawns a new MessageViewModel instance and fills it with data.
The question is - if I create new MessageViewModel instances explicitly in the ConversationViewModel won't it make my app a bit harder to test? I mean - one unit of code is the ConversationViewModel and other is the MessageViewModel - I'd like to test both separate, so when somebody breaks something in the later, test for the former won't be affected. How do I achieve it?
I'm using MVVMLight, so I thought I would register MessageViewModel as an implementation of some interface, and then create a class like MockMessageViewModel implementing the same interface, but used only in tests. Then in the ConversationViewModel I'd ask the IOC container to just give me the registered implementation. Is it a good approach, or am I overreacting? Example code:
public class ViewModelLocator {
public ViewModelLocator() {
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
if (//in test) {
SimpleIoc.Default.Register<IMessageViewModel, MockMessageViewModel>();
}
else {
SimpleIoc.Default.Register<IMessageViewModel, MessageViewModel>();
}
}
public class ConversationViewModel : ViewModelBase {
public void MessageReceived(string data) {
//I'm thinking about doing this:
var vm = SimpleIoc.Default.GetInstance<IMessageViewModel>();
// instead of doing this
var vm = new MessageViewModel();
//do stuff with vm
}
}
Whether to use interface bases approach to separate the view models from each other is the design decision based on complexity of your application.
If you want to dynamically create instance of IMessageViewModel inside IConvesationViewModel; I would recommend instead of referring to IoC container in your ViewModel class inject a factory for creating IMessageViewModel in the ConversationViewModel constructor. Later you can use this factory to create instances of IMessageViewModel. A simple implementation of factory could be Func delegate.
public class ConversationViewModel
{
private Func<IMessageViewModel> _messageViewModelFactory;
public ConversationViewModel(Func<IMessageViewModel> messageViewModelFactory)
{
_messageViewModelFactory = messageViewModelFactory;
}
public void MessageReceived(string data) {
var messageViewModel = _messageViewModelFactory();
}
}
This way you are exposing dependencies of your ConversationViewModel class through the constrctor instead of hiding them inside the class implementation.
The IoC containers like Autofac provide way to inject Func in the constructor when you create object of ConversationViewModel using it.
I believe a better way to do that is by using interfaces. You can have both your real and mock ViewModels implement the same interface and use that interface everywhere where you would use a ViewModel class.
If it was me and I may not have all the information about your application but I would have a single ViewModel IConversationViewModel. And in the IConversationViewModel I would have a collection of IMessageModel instances. I would not go nesting ViewModels.
What you can do is create the MessageViewModel immediately in ViewModelLocator and register for receiving messages in MessageViewModel using the MVVMLight MessengerInstance in its constructor. Something like this:
public class ViewModelLocator
{
public class ViewModelLocator()
{
//creates instance immediately
SimpleIoc.Default.Register<MessageViewModel>(true);
}
}
public class MessageViewModel:ViewModelBase
{
public MessageViewModel()
{
MessengerInstance.Register<string>(this,DoSomething);
}
public void DoSomething(string data)
{
//do stuff
}
}
public class ConversationViewModel:ViewModelBase
{
public void MessageReceived(string data)
{
MessengerInstance.Send<string>(data);//this will trigger DoSomething in MessageViewModel
}
}
I have a ModuleLoader : NinjectModule which is where I bind everything.
Firstly I use
Bind<Form>().To<Main>();
to Bind a System.Windows.Forms.Form to my Main form.
Is this correct?
Secondly in the Program.cs I use this:
_mainKernel = new StandardKernel(new ModuleLoader());
var form = _mainKernel.Get<Main>();
Where _mainKernel is a ninject standard kernel.
Then I use Application.Run(form)
Is this correct?
I'm unsure as to what to bind together when it comes to Windows.Forms.
Thanks for any help.
You shouldn't really be binding to System.Windows.Forms.Form. Ninject is primarily meant for binding interfaces to concrete types so that you can pass around dependencies as interfaces and switch out the concrete implementation at runtime/during tests.
If you just want to use Ninject to create your Form in this way though, you'd simply use Bind<MyForm>().ToSelf() then do kernel.Get<MyForm>(). If you are requesting the concrete type directly though and it doesn't take any dependencies, there's not much point in using Ninject to initialise it.
In your situation, if your form implements an interface then you would do: Bind<IMainForm>().To<MainForm>() and request the interface type from Ninject. Usually your interface shouldn't be bound to the concept of a "form" though, it should be agnostic of the implementation (so later you could produce a CLI and website version and simply swap the Ninject bindings).
You could use the Model-View-Presenter design pattern (or a variant) to achieve this like:
public interface IUserView
{
string FirstName { get; }
string LastName { get; }
}
public class UserForm : IUserView, Form
{
//initialise all your Form controls here
public string FirstName
{
get { return this.txtFirstName.Text; }
}
public string LastName
{
get { return this.txtLastName.Text; }
}
}
public class UserController
{
private readonly IUserView view;
public UserController(IUserView view)
{
this.view = view;
}
public void DoSomething()
{
Console.WriteLine("{0} {1}", view.FirstName, view.LastName);
}
}
Bind<IUserView>().To<UserForm>();
Bind<UserController>().ToSelf();
//will inject a UserForm automatically, in the MVP pattern the view would inject itself though
UserController uc = kernel.Get<UserController>();
uc.DoSomething();
enter code hereMaybe the title is not so specific.
The situation which I'm having is. I've got an ItemsControl where I insert many ViewModels, and this ItemsControl should have to show the View through DataTemplates.
So, I write these in a ResourceDictionary:
And then, I add this ResourceDictionary to the ApplicationResources.
This is so redundant and tiredsome.
I'm using MVVM also, so I was thinking if could be a way to use MEF to discover the corresponding the View that should draw. I was investigating that creating a custom attribute tag could be a good idea to simplify these redundant code, maybe adding this tag in the view telling it that this ViewModel should draw for this View, but I get lost with MEF.
The plan is to remove the ResourceDictionary.
Can you lend me a little hand?
Thanks in advance.
In my host WPF application, I added this Import:
[ImportMany("ApplicationResources", typeof(ResourceDictionary))]
public IEnumerable<ResourceDictionary> Views { get; set; }
code behind for the ResourceDictionary:
[Export("ApplicationResources", typeof(ResourceDictionary))]
public partial class ItemView : ResourceDictionary
{
public ItemView()
{
InitializeComponent();
}
}
For reference, the Xaml for the example ResourceDictionary looks like this:
<DataTemplate DataType="{x:Type local:ItemViewModel}">
...
</DataTemplate>
in WPF application, before the main window:
// Add the imported resource dictionaries
// to the application resources
foreach (ResourceDictionary r in Views)
{
this.Resources.MergedDictionaries.Add(r);
}
[System.ComponentModel.Composition.InheritedExport(typeof(ProblemView))]
public abstract class ProblemView : UserControl // or whatever your Views inherit
{
public abstract Type ViewModelType { get; }
}
[System.ComponentModel.Composition.InheritedExport(typeof(ProblemViewModel))]
public abstract class ProblemViewModel : BaseViewModel // or whatever your ViewModels inherit
{
}
// in your App class
{
[ImportMany(typeof(ProblemView))]
public ProblemView[] Views { get; set; }
[ImportMany(typeof(ProblemViewModel))]
public ProblemViewModel[] ViewModels { get; set; }
void MarryViewViewModels()
{// called during MEF composition
foreach (ProblemView view in Views)
{
foreach(ProblemViewModel vm in ViewModels)
{
if(Equals(view.ViewModelType, vm.GetType())
{// match -> inject the ViewModel
view.DataContext = vm;
break;
}
}
}
}
}
// example of usage
public partial class SomeView : ProblemView
{
public override Type ViewModelType { get { return typeof(SomeViewModel); } }
}
Let me explain you how to setup something like this. You can look for further information in official documentation
Best implementation would be using interface and duck typing.
public interface IModule {
DataTemplate Template { get; set; }
string Name{get;set;}
...
}
Then for each plugin, inherit this interface
[Export(typeof(IModule ))]
public class SampleModule : IModule {
private DataTemplate template;
public DataTemplate IModule.Template {
get { return this.teplate; }
set { this.template = value; }
}
private string name = "SamplePlugin";
public string IModule.Name{
get { return this.name ; }
set { this.name = value; }
}
...
}
Class SampleModule is in separate assembly while IModule is in common with both Application and every module assembly.
Now you need to load every module available to application. This code snippet is from window of application
...
[ImportMany]
public IEnumerable<IModule> ModulesAvailable {get;set;}
...
public void LoadModules(string path) {
DirectoryCatalog catalog = new DirectoryCatalog(path);
catalog.ComposeParts(this);
}
Now you can just use foreach loop and add them to Application Resource
foreach(IModule module in ModulesAvailable) {
Application.Current.Resources.Add(module.Template, module.Name);
}
This is just concept and code is not tested.
I ssed MEF in my high-school final project I did few months ago, so you could take a look at my code. It is spreadsheet application with formula support where all operations and operands are loaded as plugins so it is very flexible.