In a MVP application my presenter wants to access / Update data in a list box which is on the View. And the Presenter is talking to the View through a interface IView. So I'm planing to expose the items in the list box through a property as follows. But I have a problem in using a single property in this case.
Interface IView
{
List<string> Permission;
}
class Form : IView
{
public List<string> Permission
{
get { return lstGivenPermissions; } // Casting error
set { lstGivenPermissions.DataSource = value; }
}
}
So that my Presenter should be able to access and update data in list as follows
class Presenter
{
UpdateModelFromView()
{
Model.Permission = View.Permission;
}
UpdateViewFromModel()
{
View.Permission = Model.Permission;
}
}
My Model would be like this
class Model
{
Ipublic List<string> Permission = new List<string>();
//
}
I know that the above code will not be compiled due to mismatches in datatypes. (Casting errors).
Could you please let me know how can I achieve this?
I was looking for this...
public List<string> Permission
{
get { return lstGivenPermissions.Items.Cast<string>().ToList(); } //
set { lstGivenPermissions.DataSource = value; }
}
And it solved my problem!!!
Related
I want to make a transition to a reactive view model / model.
I've used 3 scenarios so far:
"ValueA": The model value is only accessed from one view model at a time and the value is only changed through the view model
=> simple property in model, forwarding property with PropertyChanged in view model
"ValueB": The model value is accessed from several view models and/or changes from other sources
=> property with event in model, forwarding property and translation from changed event to PropertyChanged in view model
"ValueC": A value only used in the view model
=> no property in model, property backed by own field with PropertyChanged in view model
This is my "current" approach:
class Model
{
public string ValueA {get;set;}
private string valueB;
public event ValueBChangedEvent ValueBChanged;
public string ValueB
{
get
{
return valueB;
}
set
{
valueB = value;
ValueBChanged();
}
}
}
class ViewModel : INotifyPropertyChanged
{
private Model model;
public string ValueA
{
get
{
return model.ValueA;
}
set
{
model.ValueA = value;
OnPropertyChanged();
}
}
ViewModel()
{
model.ValueBChanged += model_ValueBChanged;
}
private void model_ValueBChanged()
{
OnPropertyChanged("ValueB");
}
public string ValueB
{
get
{
return model.ValueB;
}
set
{
model.ValueB = value;
// no change notification since done via model
}
}
private string valueC;
public string ValueC
{
get
{
return valueC;
}
set
{
valueC = value;
OnPropertyChanged();
}
}
}
This is how I intend to model them using reactive extensions:
class ReactiveModel
{
public string ValueA {get;set;}
private ISubject<string> valueB = new Subject<string>();
public ISubject<string> ValueB
{
get
{
return valueB;
}
}
}
class ReactiveViewModel : INotifyPropertyChanged
{
private ReactiveModel model;
public string ValueA
{
get
{
return ???;
}
set
{
???
}
}
private ReactiveProperty<string> valueB = model.valueB.ToReactiveProperty();
public Reactive<string> ValueB
{
get
{
return valueB;
}
// no setter since access via ValueB.Value which is read-write
}
private ISubject<string> _valueC = new Subject<string>();
private ReactiveProperty<string> valueC = _valueC.ToReactiveProperty();
public ReactiveProperty<string> ValueC
{
get
{
return valueC;
}
// no setter since access via ValueC.Value which is read-write
}
}
Summary:
"ValueA": I have no clue for this case
"ValueB": works at first glance but does neither propagate changes from view model to model nor the other way.
"ValueC": this works as intended
I'd be happy if I had a solution for ValueA and ValueB.
ValueB: View model is responsible for updating model. ReactivePropertyuses only IObservable interface from your model properties and reads values from ValueB(does not write anything).
ReactiveProperty is changed by view through Value property.
ReactiveProperty implements IObservable and you should subscribe to changes to get new values.
ValueA: We can make a ReactiveProperty on the view model side an subscribe to propagate the changed value to the model.
Here is the code for the solution:
class ReactiveModel
{
public string ValueA {get;set;}
private readonly Subject<string> valueB = new Subject<string>();
public IObservable<string> ValueB
{
get
{
return valueB;
}
}
public void UpdateB(string newValue)
{
valueB.OnNext(newValue);
}
}
class ReactiveViewModel
{
private readonly ReactiveModel model;
private readonly ReactiveProperty<string> valueA;
private readonly ReactiveProperty<string> valueB;
public ReactiveViewModel(ReactiveModel model)
{
this.model = model;
valueA = new ReactiveProperty<string>(model.ValueA);
valueA.Subscribe(x => model.ValueA = x);
valueB = model.ValueB.ToReactiveProperty();
valueB.Subscribe(model.UpdateB);
}
public IObservable<string> ValueA
{
get
{
return valueA;
}
}
public ReactiveProperty<string> ValueB
{
get
{
return valueB;
}
}
}
XAML would be in both cases:
<TextBox Text="{Binding ValueA.Value, UpdateSourceTrigger=PropertyChanged}"/>
This is a bit of a contentious topic but I personally don't see property change notification as being specific to the view model and view, I therefore use B but I add INPC to the models as well in my data layer. This can be done in a post-processing build step using Fody or by wrapping the models in a proxy using something like Castle Dynamic Proxy. I personally use the latter, although it requires integration with your ORM so as to not hammer performance i.e. you don't want your database code loading a model object and then thinking that object has changed because you've tried to update it use the proxy wrapper (this is especially true when you turn IList<> into an ObservableCollection).
Your current approach doesn't seem to make a lot of sense. You are implementing events to signal when the Model changes so the View Model can take action. However only the View Model should change the Model, therefore events are completely unnecessary.
The View Model is responsible for making changes to the Model, therefore it should know when a change has been performed, as it is the source of said change.
A pure MVVM approach would be something like this:
public class MyModel
{
public string MyValue { get; set; }
...
}
public class MyViewModel
{
private MyModel _Model;
public string MyModelValue
{
get { return _Model.MyValue; }
set
{
_Model.MyValue = value;
//Notify property changed.
}
}
...
}
It is not the responsibility of the Model to notify the View of changes, instead it is the responsibility of the ViewModel to signal these changes. The Model should not be exposed to the View, but instead the properties of the Model that the View requires should be exposed.
Think of it this way.
The user changes the MyModelValue property in a TextBox on the View.
The View notifies the ViewModel of the change.
The ViewModel changes the Model.
The only purpose of INotifyPropertyChanged is when the above process is reversed, where the ViewModel needs to tell the View that a property has changed:
A method in the ViewModel is called that updates MyModelValue.
The ViewModel notifies the View of the change.
The View updates the TextBox.
The pattern of exposing only properties of the Model that the view requires is not always followed, instead you may see the entire Model being exposed to the View, but as I have said many times before, MVVM is a pattern, not the law. Implementing INotifyPropertyChanged in the Model is perfectly acceptable.
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.
In ASP.NET web applications the information about the current session like username, permissions, state, and settings that are needed across the entire application or between postbacks can be stored in the session object. What is the equivalent technique or object for that in MVVM?
From my understanding of MVVM for wpf/windows store apps, the view model is used to store state and there is no concept of a page refresh. Is it normal then to create a SessionViewModel or UserViewModel to store this information that is needed across multiple view models and/or views for a user? Wouldn't this require that every view model have a static reference to it?
So far I have created the following view model to store session information related to a user. So, is it that this view model needs to be passed around some how to other view models and views or is this not how to handle session in MVVM?
public class UserViewModel : ViewModelBase
{
public User Model
{
get;
private set;
}
public string Username
{
get
{
return this.Model.Username;
}
set
{
if (this.Model.Username == value)
{
return;
}
this.Model.Username = value;
RaisePropertyChanged();
}
}
public ObservableCollection<Permission> Permissions
{
get;//TODO
set;
}
public ObservableCollection<Setting> Settings
{
get;//TODO
set;
}
public string Password
{
get
{
return this.Model.Password;
}
set
{
if (this.Model.Password == value)
{
return;
}
this.Model.Password = value;
RaisePropertyChanged();
}
}
public bool Authenticated
{
get
{
return this.Model.Authenticated;
}
set
{
if (this.Model.Authenticated == value)
{
return;
}
this.Model.Authenticated = value;
RaisePropertyChanged();
}
}
public UserViewModel(User model)
{
Model = model;
}
}
Well, if you need a session to store any data (I mean related to both presentstion and model layers) then this approach is ok. Of course if it could be split into logical pieces try to do it.
How do you access this object? I would probably use IOC container to get reference for it. Or even better share smth like this between view models
public interface IUserContext
{
UserViewModel Current { get; }
}
In ASP.Net, the current session (accessible from HttpContext.Current) is nothing more than a static instance that can be accessed, as it is static, from anywhere.
In MVVM, you could just do the same. Build whatever object you need (you can name it CurrentSession), make it static, and access it from your view models. Do note this object doesn't have to be a ViewModel itself (unless you really need to bind its properties on a view).
Say for example you have a LoginViewModel ViewModel for the Login view. You could populate your CurrentSession object in the ValidateUser method of this ViewModel, if the user credentials are valid. Then, anywhere in your viewModels, you could access this CurrentSession object.
Maybe you want a service locator pattern?
In your arbitrary viewmodel, you'll receive the service locator, and do something like this:
public ArbitraryViewModelCtor(IServiceLocator serviceLocator)
{
var authService = serviceLocator.GetAuthenticationService();
var currentLoggedInuser = authService.GetCurrentUser();
}
This seems quite interesting.
I have a DataGridView in UI and my presentation class needs to set and get data to and from the gird. So that I could use a public property in this case as follows.
public DataGridView DeductionDetailsInGrid
{
get { return dgEmployeeDeductions; }
}
public List <Deduction > DeductionDetails
{
set { dgEmployeeDeductions.DataSource = value; }
}
I'm using two properties here as the set property should be able to show the list of objects passed in by the presenter, on the grid and also the presenter should be able to somehow get the data from the grid to supply them for the upper layers.
Is using two get and set properties for the same DataGridView an acceptable solution?
I think that I should change the get property's data type (DataGridView) as exposing the DataGridView breaks encapsulation! How to do that?
EXTRA:
If using ListBoxes we could do something like this...
public List<string> GivenPermission
{
get { return lstGivenPermissions.Items.Cast<string>().ToList(); }
set { lstGivenPermissions.DataSource = value; }
}
As you said returning a DataGridView back to the presenter breaks encapsulation and couples the view and the presenter. The presenter should not be aware of what control the view is using to visualize the model. The presenter simply needs to pass data to the view.
Follow the example from your setter. Return a List<Deduction> in the getter. You can map the list of models in the getter and then return in to the presenter
public List<Deduction> DeductionDetails
{
get
{
List<Deduction> deductionsList = new List<Deduction>();
foreach(var deductionFromGrid in dgEmployeeDeductions.Items)
{
Deduction deduction = new Deduction();
// map properties here
deductionsList.Add(deduction)
}
return deductionsList;
}
}
public List <Deduction > DeductionDetails
{
get { return (List<Deduction>)dgEmployeeDeductions.DataSource; }
set { dgEmployeeDeductions.DataSource = value; }
}
I am developing Windows Universal app. I have one GridView which has one textblock and a button. The gridview gets data of un-purchased objects from a service. The button is for purchasing particular object. So if user clicks on button that object is purchased & gridview gets refreshed to remove purchased item from it.
I am illustrating my requirement in simplified manner. I tried two ways, both are not working. Can you please suggest me solution regarding it.
First way I used is to inherit Model class with ViewModel class so I can access methods of ViewModel class, but it throws StackOverflowException in ViewModelBase at SetProperty<T> method.
P.S. - I don't want to migrate to any framework like MVVMLight, etc.
ViewModel.cs
public class ViewModel : ViewModelBase
{
public ViewModel()
{
DataCollection = new ObservableCollection<Model>();
for (int i = 1; i < 10; i++)
{
DataCollection.Add(new Model { Number = i });
}
}
private ObservableCollection<Model> _DataCollection;
public ObservableCollection<Model> DataCollection
{
get { return _DataCollection; }
set { this.SetProperty(ref this._DataCollection, value); }
}
}
Model.cs
public class Model : ViewModel
{
public RelayCommand<int> DeleteCommand { get; set; }
public Model()
{
DeleteCommand = new RelayCommand<int>((x) => DeleteNumber(x));
}
private void DeleteNumber(int x)
{
var obj = DataCollection.Where(varNum => varNum.Number == x).FirstOrDefault();
if (obj != null)
{
DataCollection.Remove(obj);
}
}
private int _Number;
public int Number
{
get { return _Number; }
set { this.SetProperty(ref this._Number, value); }
}
}
2nd way I keep that isolated, so I was not able to access the methods.
ViewModel.cs is same as above
Model.cs
public class Model : ViewModelBase
{
public RelayCommand<int> DeleteCommand { get; set; }
public Model()
{
DeleteCommand = new RelayCommand<int>((x) => DeleteNumber(x));
}
private void DeleteNumber(int x)
{
// How to access ViewModel's DataCollection property or
// a method which sets un-purchased objects in DataCollection property
}
private int _Number;
public int Number
{
get { return _Number; }
set { this.SetProperty(ref this._Number, value); }
}
}
ViewModelBase.cs
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null)
{
if (object.Equals(storage, value)) return false;
storage = value;
this.OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var eventHandler = this.PropertyChanged;
if (eventHandler != null)
{
eventHandler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Well, in the first example you're getting a StackOverflowException because your ViewModel instantiates 9 Models each time - and since your Model is an extension of ViewModel, each one of those instantiates 9 more Models and an infinite recursion happens. That doesn't answer your main question, though :)
Your class names are confusing to me, because in MVVM a "Model" is simply a representation of the data and methods to manipulate it, whereas the ViewModel requests this data from the Model and presents it via publicly accessible properties that are retrieved from the View via binding. The View knows about the ViewModel, the ViewModel knows about the Model and the Model just knows about the data. In any case you shouldn't be binding directly from the View to the Model!
You'll want to house the RelayCommand in your ViewModel so your View can bind to it, and depending on what you want to happen when a user purchases an item (store it in a database, track this in another variable, simply remove from the view without doing anything else, etc.) you may or may not need to write additional logic for when this occurs. Generally you'll want the ViewModel to handle user input and update both the presentation object as well as notify the Model a change was made, if this is something your app requires. Think of it as the Model holds the actual data whereas the ViewModel only holds what the user sees.
Unfortunately, without knowing what you're trying to do in a little more detail it's hard to give more specific advice than this!