I haven't worked with WPF or the MVVM pattern before.
However I want to create a simple document management system and would like to do so using the aforementioned technologies.
I've modeled a hierarchical file system in my Database and want to display it in a treeview.
EER-Diagramm
As you can see each directory can have multiple sub-directories and multiple files in it.
I've read some tutorials on the topic and if I understood them correctly then I should create model classes for directory and file in which the data from the database is stored directly.
Example:
public class Directory
{
private int id;
public int Id
{
get { return id; }
set { id = value; }
}
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
private int parent;
public int Parent
{
get { return parent; }
set { parent = value; }
}
private DateTime dateCreatedOn;
public DateTime DateCreatedOn
{
get { return dateCreatedOn; }
set { dateCreatedOn = value; }
}
}
Then each model class should have an associated view-model class which implements additional properties which are only relevant for describing the UI element to which objects of this class will be bound.
In this case the view-model class should have the isExpanded and isSelected Property of the treeviewitem.
Then I would need another view-model class for the entire treeview which would contain the collection of directorys and files which should be displayed.
My questions are:
Have I understood the mvvm concept correctly?
Which class, the model or the view-model, of directory should implement the iNotifyPropertyChanged interface?
Should the view-model class of directory contain the same properties as the model class or is a reference to a model-object in the view-model class sufficient?
If the view-model class should contain the same properties of the model class again, then whats the best way to make sure that model-objects and the associated view-model objects always stay synchronized?
I hope this question is understandable and thanks for all help.
Andahari
answer 1) yes.
answer 2) view-model should have the iNotifyPropertyChanged.
answer 3) yes. and they should be explicitly mapped. i.e.:
this.property1 = model.property1
answer 4) use the same names, and see answer 3.
If you use a private-public property pair in the view-model, use iNotifyPropertyChanged in the view-Model, and map the properties of the model to the corresponding properties in the view-model, you should be set.
Model also can implement the iNotifyPropertyChanged, you no need to repeat the properties in View Model again.
https://msdn.microsoft.com/en-us/library/gg405484(PandP.40).aspx
"The model may also support data validation and error reporting through the IDataErrorInfo (or INotifyDataErrorInfo) interfaces. The IDataErrorInfo and INotifyDataErrorInfo interfaces allow WPF data binding to be notified when values change so that the UI can be updated. They also enable support for data validation and error reporting in the UI layer."
Related
I'm writing an application to read and analyze some logs my company software uses. There are multiple types of logs, but let's take only two for the purpose of describing my problem. Namely, logs of TypeA and TypeB. I have designed one class to hold a single line of log data, named LogLine which looks like below.
public class LogLine
{
public long LineNum { get; set; }
public string Msg { get; set; }
}
So here's my problem/requirement.
In my main ViewModel, I'd like to read logs of each type only once when the application loads. Read TypeA logs one time, and store in an ObservableCollection of LogLine instances, do the same for TypeB. Then depending on my choice the DataGrid displays logs from one type, and if I click a button at any time, the same DataGrid should display logs from the other type. Note that my logs data doesn't change, I simply want to display my choice of logs.
For this I created three classes, namely, ControllerMain, ControllerA, and ControllerB. The last two derive from the former like so:
public class ControllerMain
{
public ControllerMain()
{
LogLineList = new ObservableCollection<LogLine>();
}
private ObservableCollection<LogLine> logLineList;
public ObservableCollection<LogLine> LogLineList
{
get { return logLineList; }
set { logLineList = value; }
}
}
public class ControllerA : ControllerMain
{
public ControllerA() { }
// More stuff here
}
public class ControllerB : ControllerMain
{
public ControllerB() { }
// More stuff here
}
As you can guess ControllerA is intended to hold logs of TypeA, and associated properties and methods unique to those logs. Same goes for TypeB logs.
In my ViewModel, I have instances of each of the classes above like so, and at application load I read log data and store in appropriate class object.
public ControllerMain COMMON_LOG { get; set; }
public ControllerA A_LOG { get; set; }
public ControllerB B_LOG { get; set; }
public ViewModelMain()
{
isAType = true;
ClickCommand = new CustomCommand(ClickCmd, CanClickCmd);
A_LOG = new ControllerA
{
// This simulates reading logs from files - done only once
LogLineList = DataService.GetAData()
};
B_LOG = new ControllerB
{
// This simulates reading logs from files - done only once
LogLineList = DataService.GetBData()
};
// This simulates switching to already loaded logs.
// When I do this the log lines don't change, but I want to refresh the datagrid and display correct info.
LoadAppropriateLog();
}
private void LoadAppropriateLog()
{
if (isAType)
{
COMMON_LOG = A_LOG;
isAType = false;
}
else
{
COMMON_LOG = B_LOG;
isAType = true;
}
}
My View binds to the COMMON_LOG instance like below:
<DataGrid Grid.Row="0" Margin="5"
Name="dgLogs"
AutoGenerateColumns="False" SelectionUnit="CellOrRowHeader"
ItemsSource="{Binding COMMON_LOG.LogLineList}">
Then at the click of a button, I call the above LoadAppropriateLog() method, so it will simply assign the instance of appropriate type to COMMON_LOG which is the instance I've used to data bind.
The problem is that when I do so, since the actual data in each instance's LogLineList doesn't change, the DataGrid doesn't automatically update to reflect my choice of logs.
Is there a way to manually refresh the DataGrid from my ViewModel after every time I switch the type of log?
If you'd like to run the project and see, here's a download link:
Download the VS Project
If you're binding to a property of a class in XAML, either
The property should never change its value after a binding would first see it, and should usually be readonly just to avoid mishaps -- or
The class should implement INotifyPropertyChanged and the property should raise PropertyChanged in its setter.
In your case, you're changing the value of COMMON_LOG, and you're never changing the value of its LogLineList.
tl;dr: So your main viewmodel needs to implement INotifyPropertyChanged, and raise PropertyChanged in the setter for COMMON_LOG. Anything that doesn't do those things isn't a viewmodel.
LogLineList being an ObservableCollection won't accomplish anything: What that class does is raise notifications when items are added, removed or replaced. That doesn't happen at any time after the binding sees it. Those instances of ObservableCollection don't even know that the main viewmodel even exists, so they certainly can't be expected to raise notification events when its properties change. Nor should they: Everybody is responsible for exactly his own notifications.
In fact, if you've made a design decision that those collections never change after initialization, use ReadOnlyCollection instead of ObservableCollection. Creating one is easy: Call List<T>.AsReadOnly<T>(). For any IEnumerable<T>, just call e.ToList().AsReadOnly(). ObservableCollection signals "you can add stuff to this". But nobody should. So don't give them ideas.
I am trying to make a picture galerie in the mvvm pattern. The viewmodel should have a model of the type IGalerieModel so that the viewmodels supports different model types in case I want to create a view with other pictures in it.
The viewmodel looks like this:
GalerieViewModel<TView, TModel>
where: TView is Galerieview
where: TModel is IGalerieModel
To call methods from code behind to the viewmodel I need to cast the datacontext. For that I have a DataContext property. It is of the type:
GalerieViewModel<GalerieView, IGalerieModel>();
But if I try to cast the DataContext into this type it return null. The datacontext isn't null I checked that, it just can't cast it to the viewModel type.
Why is this and does anybody have an alternative solution?
Edit:
Some related code, where I try to cast the datacontext.
private void MenuItem_Click(object sender, System.Windows.RoutedEventArgs e)
{
var selectedItem = Images.SelectedItem as Image;
if(selectedItem != null)
{
var datacontext = DataContext as GalerieViewModel<GalerieView, IGalerieModel>;
datacontext?.RemoveImage(selectedItem);
}
}
I suggest using inheritance in this case.
I'm using the following terminology:
ViewModel an object which is an encapsulation of the data (both bidirectional and unidirectional) displayed and/or consumed by a single specific View.
For example, if your system has 3 screens/windows (LoginView, OrdersListView, OrderDetailView), each would have its own ViewModel (LoginVM, OrdersListVM, OrderDetailVM).
If your application is built around a single large view (and your application does not permit multiple instances of the view) then it is permissable to have a monolithic ViewModel with nested/child ViewModels for any child views (have fun with event binding, though!)
Model a business domain entity, of whose provenance is not the concern of the ViewModel. I believe it is valid to expose a domain entity object directly to the View for rendering as it helps DRY. In many simplistic and trivial cases you can use the domain entity object as the ViewModel directly, assuming you don't need bi-directional binding and data-transfer. But in many cases it is highly inappropriate.
View the presentation layer of your system, in this context we specifically refer to some specific subclass of System.Windows.Controls.Window or System.Windows.Controls.Control which acts as a discrete component.
As an aside, when MVVM is implemented correctly it is possible to substitute the entire WPF layer for some other presentation/view system or even the entire runtime environment, such as WinForms or UWP (but not ASP.NET MVC as the entire stateful paradigm breaks down).
In your case, I'm assuming IGalerieModel is an interface for your business domain entity types and it generally wraps/encapsulates bitmap data. I can't think of how the data could be different, but I'll just take your word for it.
So you have a single view, GalerieView, which I assume is a simple *.xaml view, with a lightweight, view-logic-only .cs CodeBehind file.
Your ViewModel should therefore look something like this:
public class GalerieViewModel {
public String WindowTitle { get; set; }
public String WelcomeMessage { get; set; }
public virtual ObservableCollection<IGalerieModel> Images { get; set; }
}
So there is no need to use generics here at all.
If you do want to use generics because elsewhere in your system there is knowledge about the exact type of object in the Images collection, you should implement that functionality in a generic subclass:
public class GalerieViewModel<TImage>
where TImage : IGalerieModel {
public new ObservableCollection<TImage> Images { get; set; }
}
(Note that you will need to write code to keep both ObservableCollection collections in-sync, note that ObservableCollection<IGalerieModel> is more permissive in what it accepts as input compared to ObservableCollection<TImage> - this is a downside of using mutable collections in generics, as well as how ObservableCollection works in practice).
Then you can consume it like so (using your example):
private void MenuItem_Click(Object sender, RoutedEventArgs e)
{
IGalerieModel selectedItem = this.Images.SelectedItem as IGalerieModel ;
if( selectedItem != null )
{
GalerieViewModel viewModel = (GalerieViewModel)this.DataContext;
viewModel.Images.Remove( selectedItem );
}
}
For convenience you can add a strongly-typed property to get the ViewModel in the View (this is okay because the View knows about the ViewModel, but the ViewModel doesn't know about the View):
protected GalerieViewModel ViewModel {
get { return (GalerieViewModel)this.DataContext; }
}
private void MenuItem_Click(Object sender, RoutedEventArgs e)
{
IGalerieModel selectedItem = this.Images.SelectedItem as IGalerieModel;
if( selectedItem != null )
{
this.ViewModel.Images.Remove( selectedItem );
}
}
You might want to subclass Window to add strongly-typed ViewModel support:
public abstract class Window2<TViewModel> : Window {
protected TViewModelViewModel {
get { return (TViewModel)this.DataContext; }
}
}
GalerieViewModel<GalerieView, SomeModel> and GalerieViewModel<GalerieView, IGalerieModel> are two completely independent types that cannot be casted.
SomeModel implementing IGalerieModel does not propagate out of the generic.
I am just getting started with the MVVM pattern, and have settled on using the MVVM Light toolkit for my Windows Phone app.
My question is this:
I am having trouble producing a view model that correctly binds a view to a model with properties that are more than just primitive types---what is a functioning way of structuring a model and accompanying view model such that a view will bind to the model through the view model, and properly update when values in the model change?
As in, how do I correctly reference the values of the model from the view model?
An example of this sort of model would be a class Foo, which has a property, b, that is a primitive, and another property, a, that is of type Bar, with its own properties---primitives or otherwise.
Foo:
public class Foo : ObservableObject
{
public Foo()
{
a = new Bar();
}
public const string aPropertyName = "a";
private Bar _a;
public Bar a
{
get
{
return _a;
}
set
{
Set(aPropertyName, ref _a, value);
}
}
public const string bPropertyName = "b";
private bool _b;
public bool b
{
get
{
return _b;
}
set
{
Set(bPropertyName, ref _b, value);
}
}
}
Bar:
public class Bar : ObservableObject
{
public const string cPropertyName = "c";
private bool _c;
public bool c
{
get
{
return _c;
}
set
{
Set(cPropertyName, ref _c, value);
}
}
}
Edit: To clarify, when setting up the properties in the view model, should I be doing something like:
public const string cPropertyName = "c";
public bool c
{
get
{
return model.a.c;
}
set
{
model.a.c = value;
RaisePropertyChanged(cPropertyName);
}
}
or something different?
I think the question you are asking is Nested vs Flat view model. Both are correct approaches depending on the situation [though I prefer Nested View models].
I can't suggest you what you use, but in case of Foo-Bar example, wrapping up Bar's property with a property in Foo will make more sense [Flat View model approach]. So what u did by creating property c is right.
Below is my bit on both approaches
Advantage of Nested View Model
That's what object oriented is all about.
If you use LINQ to SQL or Entities, or an ORM, you can simply pass the ORM objects and not have to pass all kinds of properties.
You can pass other views, so you can create separate models for partial views, and if that view uses a partial, you can pass the partial view model class as a property of the view model class.
Advantage of Flat View Model
Keeps things simple and more readable.
In the last two WPF-applications we developed in our company, we used MVVM with Caliburn.Micro.
For the most of ViewModels we didn't create Models, because you should create a Model not because otherwise you will violate some OOP-rules, but because you need it.
When you should create a separated Model?
In two cases, in my opinion:
when you can reuse a Model in the other application;
when a ViewModel becomes complicated and you start to realize, that you want to simplify it by separating of concerns. Almost always, extracting a Model from a ViewModel is simple, if not trivial.
There is no sense to always create a Model for every ViewModel. Going this way, you overcomplicate a system, when you can avoid complications.
As to binding to a non-primitive type. Well, there is no any problems to add the support of notifying directly to the type.
It really depends on your usage of ViewModel.
If you only need one or two properties of your complex type (Bar in your example), you can flatten it in your ViewModel. If you know you always/mostly need all of your complex properties, then it may make sense to create an own ViewModel for your complex model and expose your ViewModel instead of the model.
public class Foo : ObservableObject
{
public BarViewModel A
{
public const string aPropertyName = "a";
private Bar _a;
get
{
return _a;
}
set
{
Set(aPropertyName, ref _a, value);
}
}
}
Last but not least:
Does your complex model needs two way binding? If no, you could just implement a property for your model.
But be warned! Binding objects that do not implement INotifyPropertyChanged may result in memory leaks! See this answer
I relised I have lots of models view models with those two properties
public Visibility OkButtonVisibility
{
get{ return _OkButtonVisibility;}
set{
_OkButtonVisibility = value;
RaisePropertyChanged("OkButtonVisibility");
}
}
public Visibility CancelButtonVisibility
{
get{ return _CancelButtonVisibility;}
set{
_CancelButtonVisibility = value;
RaisePropertyChanged("CancelButtonVisibility");
}
}
I wanted to create attachable interface for them like this:
Interface IOKandCancelButtonsVM
{
public Visibility OkButtonVisibility
{
get{ return _OkButtonVisibility;}
set{
_OkButtonVisibility = value;
RaisePropertyChanged("OkButtonVisibility");
}
}
public Visibility CancelButtonVisibility
{
get{ return _CancelButtonVisibility;}
set{
_CancelButtonVisibility = value;
RaisePropertyChanged("CancelButtonVisibility");
}
}
and have my viewmodels that use this to inherite them and another interfaces with proxy properties like this
class VM1:BaseVM,IOKandCancelButtonsVM,IOtherCommonPropertyVM
{
}
but then I relaised my new interfaces don't impliment INotifyChanged.
would it be a bad idea to haveIOKandCancelButtonsVM impliment INotifyChanged
and have VM1 explicitly impliment BaseVM?
I never dealt with class inheriting same interface twice and not sure what to do.
this is only an example less than half of the VMs use those I have a few hundred properties, many shared over dozens of screens. i need a more elegant solution than sticking all on thos into the BaseViewModel
You cannot have an implementation in your interface definition. This means that your IOKandCancelButtonsVM definition is incorrect.
would it be a bad idea to have IOKandCancelButtonsVM impliment BaseVM
Yes it would be a very bad idea - impossible in fact - interfaces cannot implement or extend classes.
Since you asked for opinion on how to structure your code, I will also throw this out there: remove any reference to 'visibility' from your viewmodel. Do not return a System.Visibility value from your viewmodel.
A viewmodel shouldn't know anything about the view. The semantically correct way to do this is to simply return a flag from the viewmodel, and use a converter to change it to a Visibility value in the binding - there is even a ready made converter in the framework for doing this: BooleanToVisibilityConverter. Try to avoid any mention of UI related terms in your VM even if it is returning a bool - it is a good practice which leads to tighter and more disciplined code, using those names willy nilly will eventually lead to smelly code.
I have a blog post which illustrates a nice approach to property change notifications: Streamlining property notifications in MVVM.
Just to make it clear, I am advising that you get rid of any ideas you have about IOKandCancelButtonsVM interfaces, by all means include simple boolean properties on a base viewmodel that will be extended by many other viewmodels that will be bound to dialogs with OK/Cancel buttons.
To diagram this in text it would look like this:
YourBaseVm <-- BaseVmUsedByDialogs <-- SpecificDialogVm
(note the italicising which indicates the class is abstract)
It seems you already have a base viewmodel class. Just add the two properties there. You might want to create a second base inheriting from the first, which you use when those two properties are required, so in case their not, there is no overhead.
Why not to go for virtual properties in your base class BaseVM with default functionality implemented in it. In case other derived classes want to extend it they can always override it as per funcitonality required.
Make sure your BaseVM is implementing INotifyPropertyChanged.
I'd just create an abstract base class, and have the ViewModels that need those properties inherit from it. Keep it simple.
abstract class BaseDialogVM : BaseVM
{
private Visibility _OkButtonVisibility;
public Visibility OkButtonVisibility {
get { return _OkButtonVisibility; }
set {
_OkButtonVisibility = value;
RaisePropertyChanged("OkButtonVisibility");
}
}
private Visibility _CancelButtonVisibility;
public Visibility CancelButtonVisibility {
get { return _CancelButtonVisibility; }
set {
_CancelButtonVisibility = value;
RaisePropertyChanged("CancelButtonVisibility");
}
}
}
class VM1 : BaseDialogVM { /*...*/ }
Case
Say I have a Person class, a PersonViewModel and a PersonView.
Updating properties from PersonView to the Person model is simple enough. PersonViewModel contains a Person object and has public properties the PersonView binds to in order to update the Person model.
However.
Imagine the Person model can get updated by Service. Now the property change needs to be communicated to the PersonViewModel and then to the PersonView.
This is how I would fix it:
For each property on the Person model I would raise the PropertyChanged event. PersonViewModel subscribes to the PropertyChanged event of Person. PersonViewModel would then raise another PropertyChanged in order to update the PersonView.
This to me seems the most obvious way but I kind of want to throw this question out there in the hope of someone showing me a nicer way. Is it really this simple or are there better ways to mark the model as modified and update the respective properties on the ViewModel?
Additions
The PersonView's DataContext is PersonViewModel. Person gets populated from JSON and gets updated many times during its lifetime.
Feel free to suggest architectual changes for my particular case.
Answer
I marked aqwert as the answer of my question since it provided me with an alternative to the solution I already proposed.
When the view binds directly to the model (which is also the case when the ViewModel exposes the Model) you are mixing UI code and data code. The goal of MVVM is to separate these two code domains. That's what the ViewModel is for.
The view model has to have it's own properties the view can bind to. An example:
class PersonViewModel
{
private Person OriginalModel { get; set; }
public ValueViewModel<string> Name { get; set; }
public ValueViewModel<int> Postcode { get; set; }
protected void ReadFromModel(Person person)
{
OriginalModel = person;
Name.Value = OriginalModel.Name;
Postcode.Value = OriginalModel.Postcode;
}
protected Person WriteToModel()
{
OriginalModel.Name = Name.Value; //...
return OriginalModel;
}
}
Using such a ViewModel-design really separates your data objects from your user interface code. When the structure of the class Person is changed, the UI doesn't need to be fit accordingly, because the ViewModel separates them from each other.
Now to your question. As you can see in the example above, I used a generic ValueViewModel<T>. This class implements INotifyPropertyChanged (and some other stuff). When you receive a new Person instance, you only have to call ReadFromModel(newPerson) on your ViewModel to have the UI updated, because the ValueViewModels the View binds to will inform the UI when their value changes.
Here an extremely simplified example of the internal structure of the ValueViewModel:
class ValueViewModel<T> : INotifyPropertyChanged
{
private T _value;
public T Value
{
get { return _value;}
set
{
_value = value;
RaisePropertyChanged("Value");
}
}
}
This is an approach we used in our MVVM library. It has the advantage that it forces the developer to clearly separate code from the designers concerns. And, as a side effect, it generates a standardized code layout in all your Views and ViewModels and thus improves code quality.
If the view is binding to the Model directly then as long as the service is using the same instance any changes to the model properties will be propogated to the view.
However if you are recreating a new model in the service then you will give the viewmodel the new model. I expect to see the model as a property on the view model so when you set that property all binding should be alerted to the change.
//in the ViewModel
public Person Model
{
get { return _person; }
set { _person = value;
RaisePropertyChanged("Model"); //<- this should tell the view to update
}
}
EDIT:
Since you state there are specific ViewModel logic then you can tailor those properties in the ViewModel
private void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if(e.PropertyName == "Prop1") RaisePropertyChanged("SpecicalProperty");
...
}
public string SpecicalProperty
{
get
{
reutrn Model.Prop1 + " some additional logic for the view";
}
}
IN XAML
<TextBlock Text="{Binding Model.PropertyDirect}" />
<TextBlock Text="{Binding SpecicalProperty}" />
This way only both the Model and ViewModel propertys are bound to the view without duplicating the data.
You can get fancier creating a helper to link the property changes from the model to the view model or use a mapping dictionary
_mapping.Add("Prop1", new string[] { "SpecicalProperty", "SpecicalProperty2" });
and then find the properties to update by getting the list of properties
private void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
string[] props;
if(_mapping.TryGetValue(e.PropertyName, out props))
{
foreach(var prop in props)
RaisePropertyChanged(prop);
}
}