MVVM - Model data available for multiple viewModels - c#

currently I am learning MVVM and now I have a newbie question.
Let's assume my application is structured as follows
viewModel
ViewModelA
ViewModelB
ViewModelC
Model
ModelX
The model should save all informations from a "config.xml" file which read at the start of the application.
Programm step chain
Application starts
viewModelA would execute the function called "readXML"
Some informations of config.xml should be displayed in view1 and some other in view2 and so on. Thats the reason for my following question...
How can I provide now the read XML data for all viewModels?
Thanks in advance :)

The structure of mvvm should be as follows. You can use a singleton class to use a shared resource
Model
ModelA
ModelB
ModelC
View
ViewA
ViewB
ViewC
ViewModel
ViewModelA
ViewModelB
ViewModelC
Now define the ModelX class as a singleton class. You can use the following class in all viewmodels.
public class XMLData
{
private XMLData()
{
}
private static XMLData instance = null;
public static XMLData Instance
{
get
{
if (instance == null)
{
instance = new XMLData();
}
return instance;
}
}
public static void ReadXMl()
{
//to do
}
}

Related

Two ViewModels are using one Model which holds data

I've searched for almost 3 hours and found alot about PRISM and VM data sharing, but I can't get my brain to understand it.
I have the two ViewModels MainViewModel and ListViewModel. The MainViewModel has a dockpanel where I want to have a common menu to add, remove items and close the application for example. There is also a usercontrol in the XAML which is showing the ListView. The ListView contains a few ObservableCollection<ProjectModel>.
From the MainVM I want to add more Projects - so to a collection. To access the ListModel, which contains the data, I pass it to the constructor of both ViewModels:
public MainModuleViewModel(ListModel listModel)
{
ListModel = listModel;
InitializeCommands();
}
public ListModuleViewModel(ListModel listModel)
{
ListModel = listModel;
InitializeCommands();
}
But it seems that during initializaiton to different objects of ListModel are created.
So my question is: What do I have to do, to work just on the one object of ListModel, which I'm passing to the constructors?
You have two options, as always:
register the ListModel as singleton (ContainerControlledLifetimeManager in Unity)
or register a service as singleton that gives the data to the view model
I'd go with the second one as it's far more flexible - you can use a different ListModel instance elsewhere with different content, you can make the service to give each view model its own instance (though with the same content) or you can make it give each view model the same instance and so on...
public interface IDatasource
{
ListModel Data { get; }
}
internal class StephensService : IDatasource
{
ListModel Data { get; } = new ListModel(); // or however you plan to procure the data
}
// ... bootstrapper / module initialization ...
Container.RegisterType<IDatasource, StephensService>( new ContainerControllerLifetimeManager() );
// ...
internal class ListModuleViewModel
{
public ListModuleViewModel( IDatasource datasource )
{
var heresMyData = datasource.Data;
}
}
Depending on your needs, the service can implement INotifyPropertyChanged or the ListModel can be an ObservableCollection...

How to bind data from host to plugin

How do I bind data from host to a plugin with MEF?
So the thing is:
I work with MVVM so I have my Models, ViewModels and Views.
I want to use MEF to be able to expand my application.
I want to store all the data in the MainViewModel so every plugin can work with the actual data.
The plugin is a UserControl wich will be displayed as a ContentControl in the MainViewModel.
What I have so far:
MainViewModel
Models
Databinding from MainViewModel to View.
Import plugins from folder X
What I need:
- the plugins need to bind the data from the MainViewModel to the plugin UI.
- changing the property in the plugin UI must update the data in the MainViewModel and update the UI from all other plugins.
The PluginInterfaces:
public interface IPlugin
{
}
public interface IPluginData
{
string Name { get; }
}
The MainViewModel: (part of it)
private MyModel myfirstmodel;
private DirectoryCatalog catalog;
private CompositionContainer container;
[ImportMany]
IEnumerable<Lazy<IPlugin, IPluginData>> Plugins;
public MainWindowViewModel()
{
string pluginPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
pluginPath = Path.Combine(pluginPath, "plugins");
if (!Directory.Exists(pluginPath))
Directory.CreateDirectory(pluginPath);
catalog = new DirectoryCatalog(pluginPath, "*.dll");
container = new CompositionContainer(catalog);
try
{
this.container.ComposeParts(this);
}
catch (CompositionException compositionException)
{
Console.WriteLine(compositionException.ToString());
}
}
The Model
public class MyModel
{
private string message;
private int number;
private DateTime date;
public string Message { get { return message; } set { message = value; } }
public int Number { get { return number; } set { number = value; } }
public DateTime Date { get { return date; } set { date = value; } }
}
The Plugin
[Export(typeof(IPlugin))]
[ExportMetadata("Name", "MyFirstPlugin")]
public partial class MyFirstPlugin : UserControl, IPlugin
{
public MyFirstPlugin()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
//Change the message in MainWindowViewModel and the date when it gets changed.
}
}
I tried using INotifyPropertyChanged but did not came that far..
Does anybody got a really good tutorial for that or can show me how to do this?
I would appreciate a "how to" and not just a "just use INotifyPropertyChanged".
Is this even possible?
Personally I think you're going about this the wrong way, MEF was specifically designed to do this type of plumbing so that you don't have to.
In a proper MVVM application the views usually get created by way of data templates, and even if you don't do this there is usually other global data you need to import like brushes and behaviours etc. And if you do use templates (which you really should be doing) then you don't need to explicitly export your controls; simply referencing them by the templates will result in them getting imported as well.
You can achieve this in practice with another pass of the importer. Give each plugin a custom ResourceDictionary and put all the global data/templates/UI resources etc in there (so it's effectively the plugin's version of App.xaml). The trick is to then give those ResourceDictionaries their own code-behind file and decorate those classes with Export. Then all your main application has to do is Import all the classes of type ResourceDictionary (or preferably a derived class or interface that returns the dictionary) and add each one to Application.Current.Resources.MergedDictionaries. From that point on the development of your plugins is pretty much the same as if they were in a DLL project that you were statically linking the usual way.
For exchange data with ModelView you will need an interface representing the host. You will also need a container or method to insert a visual control representing the plugin.
interface IPluginHost
{
MyModelView ModelView {get;}
MyMainWindow View {get;}
}
interface IPlugin
{
void Register(IPluginHost host);
void Unregister();
}
After the composition you register plugins with the host, which will gives them an access to the model view:
this.container.ComposeParts(this);
foreach(var plugin in Plugins)
{
plugin.Value.Register(this);
}
What I need: - the plugins need to bind the data from the
MainViewModel to the plugin UI.
Plugin has access to MainViewModel in it's Register method were it can perform all the necessary bindings. Here is one of many ways it could be done:
public partial class MyFirstPlugin : UserControl, IPlugin
{
...
void Register(IPluginHost host)
{
_host = host;
// Attach main model view as data context
this.DataContext = host.ModelView;
// Add control to the host's container
var mainWindow = host.View;
mainWindow.AddPluginControl((UserControl)this);
}
void Unregister()
{
if (_host == null)
{
return;
}
this.DataContext = null;
_host.View.RemovePluginControl(this);
_host = null;
}
IPluginHost _host;
}
AddPluginControl() ,RemovePluginControl() are public methods to insert a visual element of the plugin into container.

Using one view model in multiple windows

How can I use one view model for many windows in WPF? I need model to be updated from one window only and handle these changes in others (for ex. property 'Locked').
I have one view model incapsulating the most general info that should be used not only on the A (suppose it is 'Company') window but also on windows child window B (suppose it is 'Person'). So the 'General' view model should be determined by A entity but be passes to all children entity. While updating this view model on A window - we should se changes on all B windows.
public partial class A : WindowBase
{
private GeneralViewModel general;
public GeneralViewModel General
{
get
{
return this.general ?? (this.general = new GeneralViewModel ());
}
}
}
public partial class B : WindowBase
{
private GeneralViewModel general;
public GeneralViewModel General
{
get
{
return this.general ?? (this.general = new GeneralViewModel ());
}
}
public B(GeneralViewModel g)
{
this.general = g;
}
}
I wish the model should be updated only in A and B was simply displaying that changes were maid. In case I pass model as it is shown in this code or if I implement 'General' as property with getter and setter changes are not applied.
Thanks for any help.
You could use a singleton-class as your ViewModel.
Example:
public Window()
{
this.DataContext = ViewModel.Instance.
}
EDIT:
public GeneralViewModel
{
public DataType Model
{
get { return DataType.Instance; }
}
}
Now everytime you access the Model in one of your GeneralViewModels, it is locked for all others.
Initialise your view model in a static member somewhere and have the windows return the value as their GeneralViewModel.
In the scenario you mentioned, your GeneralViewModel is a kind of Dependency to both your Window classes and for these purposes you can use some of the available IoC containers or MEF built into .Net 4. You can register your Dependencies including the GeneralViewModel in some application startup event.
Below is some sample code that will make your GeneralViewModel instance to be then located from the container it is registered with (MEF in below case):
[Export(typeof(B))]
public partial class B : WindowBase
{
private GeneralViewModel general;
public GeneralViewModel General
{
get
{
return this.general ?? (this.general = new GeneralViewModel ());
}
}
[ImportingConstructor]
public B(GeneralViewModel g)
{
this.general = g;
}
}
To learn more about MEF,see these articles:
CodePlex
Ten Reasons to use the Managed Extensibility Framework
Managed Extensibility Framework Overview
An Introduction to Managed Extensibility Framework (MEF) - Part I
There are many other DI and IoC containers available as Open Source downloads.
There is no problem if you use MVVM. In this case your ViewModel will correspond to some View which is basically the UserControl and you can place it to as many Windows as you wish.
And When you implement MVVM you should also use INotifyPropertyChanged or ObservableCollections

How to synchronize Model and View Model bidirectional in MVVM?

It's a simple question and I searched the Internet for hours without success...
I have a model and a view model with one property. To make this property viewable in the view, I use a view-model-object which should automatically be generated from the model-object and vice versa. Of course, the following code will throw an StackOverflowException, because the updating of the model-object in the model causes an update of the view-model-object in the view-model and this causes an update of the model-object in the model and so on...
class ModelObject
{
...
}
class ViewModelObject
{
...
}
class Model : INotifyPropertyChanged
{
private ModelObject modelObject = new ModelObject();
...
public ModelObject ModelObject
{
get
{
return this.modelObject;
}
set
{
this.modelObject = value;
this.NotifyPropertyChanged("ModelObject");
}
}
}
class ViewModel : INotifyPropertyChanged
{
private ViewModelObject viewModelObject = new ViewModelObject();
private Model model = new Model();
...
public ViewModel()
{
this.model.PropertyChanged += new PropertyChangedEventHandler(this.propertyChangedEvent);
}
public ViewModelObject ViewModelObject
{
get
{
return this.viewModelObject;
}
set
{
this.viewModelObject = value;
this.model.ModelObject = new ModelObject(...);
this.NotifyPropertyChanged("ViewModelObject");
}
}
private void propertyChangedEvent(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName.Equals("ModelObject"))
{
this.ViewModelObject = new ViewModelObject(...);
}
}
}
What is the common way to synchronize these two objects?
There is no silver bullet. As model is a representation of the database and viewmodel is more closer to the interface, there is always some business logic needed to convert the model to view model and vice versa.
I usually have two methods in my view model class - SyncModel(ViewModel viewModel) and SyncViewModel(Model model)
One more suggestion -
Model should not implement INotifyPropertyChanged. The view model should implement this as its bound to the user interface. Why does the model ever need to change? It represents whats in the db. You can refresh it but why do you need change notifications for the model?
Edit: MVVM: Binding to Model while keeping Model in sync with a server version
Hard reference. Each class having a reference to another, listens to property change event and updates itself accordingly.
Observer Pattern - Have an observer class, each class register itself with an observer, observer listens for any changes and updates all its subscribers.
There's also an event aggregator which might be useful.
If you want a deferred update, an isDirty property would be required. You know your application better, google for more articles and choose wisely.

Property injection with Unity causing stack overflow

I have been using Unity for quite a while but I have always used it with constructor injection. In an effort to reduce the number of classes I have to inject into my view models (as my commands rely on them) I thought I would try creating a concept that uses Property Injection and thus quash the requirement for the large constructor parameter lists. Here is the scenario...
I am creating a View Model that has Commands located on properties that use/update the hosing View Model in some way. I wish to pass the instance of the View Model into the constructors of the Commands located on the View Models properties. E.g.
public MainViewModel
{
public MainViewModel()
{
Customers = new ObservableCollection<CustomerViewModel>();
}
[Depedency("LoadCommand")]
public ICommand LoadCustomersCommand { get; set; }
public ObservableCollection<CustomerViewModel> Customers { get; private set; }
}
public LoadCustomersCommand : ICommand
{
public LoadCustomersCommand(MainViewModel mainViewModel)
{
//Store view model for later use
}
//... implementation
}
//Setup code in App.Xaml
IUnityContainer unityContainer = new UnityContainer();
unityContainer.RegisterType<ICommand, LoadCommand>("LoadCommand");
unityContainer.RegisterType<MainViewModel>(new ContainerControlledLifetimeManager());
When I resolve the MainViewModel class I get a StackOverflow exception (if Visual Studio comes back at all). Now I would expect Unity to create an instance of the MainViewModel first then as it is basically a singleton, then look at the instance of the View Model and create the Command passing in the newly created MainViewModel, but obviously I am wrong.
Any ideas?
This is Circular References error, and this as it said, this is developer's responsibility to avoid it. So MainViewModel references to LoadCustomersCommand wich is refferences to MainViewModel -> StackOverflow.
So the only you can do is
public class MainViewModel
{
public MainViewModel()
{
Customers = new ObservableCollection<CustomerViewModel>();
}
//no dependency.
public ICommand LoadCustomersCommand { get; set; }
public ObservableCollection<CustomerViewModel> Customers { get; private set; }
}
and to resolve you'll need to do the following
var mainModel = unityContainer.Resolve<MainViewModel>();
mainModel.LoadCustomersCommand = unityContainer.Resolve<ICommand>("LoadCommand");

Categories

Resources