I have a ProjectModel and a ProjectViewModel class; my Model handles direct manipulation of a project, while the view model is providing the "view friendly" properties and commands
However, in my ViewModel I'm exposing the read-only project contract for view binding
private ProjectModel Model { get; set; }
public IProject Project
{
get
{
return Model.Project;
}
}
So here, the property Model doesn't change, but the property Model.Project will change, and so Model will raise its PropertyChanged event when it does
Now, I know Fody-PropertyChanged has the ability to detect if a property is dependent on another property (in the same class) and raise the change event if the other changes.
I was wondering whether it is possible for Fody-PropertyChanged to raise PropertyChanged("Project") when the Model object raises its changed notifier.
I could do it manually, of course; but I'd prefer to stick with Fody. Is this a bad idea? Alternatively, is this a bad MVVM practice to begin with?
No Fody-PropertyChanged does not currently support detecting wrapped properties in the way you describe.
Related
I'm creating a BPMN editor using WPF and need to include, among other things, a set of different task types.
Each task type share some common properties, but also include some type-specific information.
However, for a given task element, the user should be able to change its type and even the information specific to the given task type should be retained between such changes.
Therefore, I thought about creating:
one model class, TaskModel, which would combine properties of all task types
a separate XXXTaskViewModel class for each task type, exposing only properties related to that type
a common TaskView class representing the task as a visual element in the editor canvas
When a user changes the type of a task (using a property grid), a PropertyChanged event is fired, which is handled in TaskView to change the DataContext view model for the new one.
Since I'm totally new to C# and need to refactor and enhance someone else's code, my initial questions are:
Is such a design correct, i.e., in accordance with MVVM principles? Or maybe there exist other better patterns/solutions I could use?
Is it possible at all to handle PropertyChanged events in a view?
I tried to implement the INotifyPropertyChanged in my view (to change DataContext):
public partial class BaseElementView : DragableUserControl, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public BaseElementView()
{
InitializeComponent();
PropertyChanged += PropertyChangedHandler;
}
private void PropertyChangedHandler(object sender, PropertyChangedEventArgs e)
{
Console.WriteLine("A property has changed: " + e.PropertyName); // just a proof-of-concept
}
// ...
}
However, no PropertyChanged is actually catched (although the same event is handled by a view model class). Also, I get a code analyzer hint:
The event BaseElementView.PropertyChanged is never used.
I don't know why that happend.
Ok, I guess I found a solution to my problem - I've created a model containing all properties (for all different kind of tasks), a view model bound to that model (i.e., also containing all properties) and then dynamically adjusted the Browsable attribute of the appropriate properties in the view model (in the setter of the TaskType property).
This post was helpful to adjust the Browsable attribute: Make a Property Read-Only in PropertyGrid
I adjusted the proposed method to handle the BrowsableAttribute attribute:
public static void SetBrowsableAttribute(this PropertyDescriptor p, bool value)
{
var attributes = p.Attributes.Cast<Attribute>().Where(x => !(x is BrowsableAttribute)).ToList();
attributes.Add(new BrowsableAttribute(value));
typeof(MemberDescriptor).GetProperty("AttributeArray", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(p, attributes.ToArray());
}
and then used it like this:
[RefreshProperties(RefreshProperties.Repaint)]
public TaskType Type
{
get { return _taskActivity.Type; }
set
{
_taskActivity.Type = value;
TypeDescriptor.GetProperties(this)[nameof(AvailableImplementations)].SetBrowsableAttribute(UseImplementation);
// ...
NotifyOfPropertyChange(nameof(Type));
}
}
So I was looking at https://github.com/xamarin/Sport as an example I came across when googling something for my current project. It is similar to what Im working on because I use an azure backend as well.
I have a question about their mvvm layout. I thought that in mvvm the models were sort of POCOs and not supposed to implement INotifyPropertyChanged. Arent they acting as both a Model and a ViewModel in this case? Look at the Athlete model and the AthleteViewModel. The VM has a property for for Athlete and so the model is used as a VM as well.
In my project, if I had the same types, I would have an Athlete model, an AthleteViewModel and an AthletePageViewModel. Where the Athlete and AthleteVM would be automapped. The only reason to populate and or create the Athlete is to persist it to the service or local storage.
Is one way more "correct" than the other way? Or am I just doing it wrong and over complicating it? I almost don't want to continue with the way I'm doing it because I dont want to have a bunch of "extra" model files if I can just use some of my VMs as models.
Thanks.
There's no ultimate master set of strict rules that you need to follow in order to implement the MVVM design pattern. In fact, the guidelines are generally quite blurry.
From what I've seen, there are a couple of different methods of which a model may be exposed to the view. Here they are:
Method 1 - INotifyPropertyChanged in the Model
public class Car : INotifyPropertyChanged
{
private string _Model;
public string Model
{
get { return _Model; }
set
{
_Model = value;
NotifyOfPropertyChange();
}
}
...
}
public class CarViewModel
{
//The entire model is exposed to the view.
public Car Model { get; set; }
...
Method 2 - INotifyPropertyChanged in the View Model
public class CarViewModel
{
private Car _Car;
//The model property is exposed to the view, not the model itself.
public string CarModel
{
get { return _Car.Model; }
set
{
_Car.Model = value;
NotifyOfPropertyChange();
}
}
...
In terms of a preferred method, I would say method 2 is the better option. Why?
The Model object is not exposed to the view.
The View Model only exposes what the View needs.
Method 2 does have its downsides. Imagine if you needed to expose lots of model properties, or imagine if your model changes, it is certainly easier to simply implement INotifyPropertyChanged in the model and expose it to the view. Programmers are lazy by nature, therefore in order to save hassle, you'll see method 1 just as much as method 2.
But that isn't a bad thing.
Is one way more "correct" than the other way? Or am I just doing it wrong and over complicating it?
Remember, the MVVM design pattern is just a pattern. Neither options are correct, it's mostly down to the developers preference how they choose to approach the implementation of the pattern, as long as the main MVVM concepts are there, that's all that matters.
In the context of the MVVM pattern, how would one structure the ViewModel when Models do not implement the INotifyPropertyChanged interface?
I like to keep my Models as simple as possibile and implementing the INotifyPropertyChanged interface only for binding purposes seems like unwanted complexity. That's why most of the times i require my VMs to wrap model properties like in the following example:
class ViewModel : INotifyPropertyChanged
{
private Model model;
public int MyProperty
{
get { return model.MyProperty; }
set
{
if (value != model.MyProperty)
{
model.MyProperty = value;
// Trigger the PropertyChanged event
OnPropertyChanged("MyProperty");
}
}
}
/* ... */
}
This will make bindings work ok, including two-way ones.
Now, what would happen if a command executes a model method with complex logic (affecting the value of many properties of different objects)? The model is not implementing INotifyPropertyChanged so there's no way we can know it was updated. The only solution that comes to my mind is to use messaging (mediator pattern) to inform all VMs of the execution of the method so that each VM fires the PropertyChanged event for each potentially affected property:
// Sample ICommand.Execute() implementation
public void Execute(object parameter)
{
var model = (Model)parameter;
model.VeryComplexMethod();
// Just an example, the string "VeryComplexMethodExecuted" is
// sent to all listening VMs. Those VMs will in turn fire the
// PropertyChanged event for each property that may have changed
// due to the execution of the complex model method.
Messaging.Broadcast("VeryComplexMethodExecuted");
}
Please share your ideas, thanks.
Declare your members virtual and use something like Castle Dynamic Proxy to inject change notification automatically:
http://ayende.com/blog/4106/nhibernate-inotifypropertychanged
This has to be used with care when your models are being created in your data layer because it returns a new instance entirely. Your database code will think the object has changed and serialize it back out again which will in turn have a huge impact on performance. Fortunately all the good ORMs provide mechanisms for you to substitute in class wrappers at creation time.
Having passed a series of Edward Tanguay's questions refractoring the usage of MVVM for WPF app which can be found in Linked sidebar of his Fat Models, skinny ViewModels and dumb Views, the best MVVM approach?, I am a little confused by his
final WPF application in Big smart ViewModels, dumb Views, and any model, the best MVVM approach?
Its M (Model) is Customer class:
//model
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime TimeOfMostRecentActivity { get; set; }
public static Customer GetCurrentCustomer()
{
return new Customer
{ FirstName = "Jim"
, LastName = "Smith"
, TimeOfMostRecentActivity = DateTime.Now
};
}
}
which returns current user. Kind of, beause it returns duplicates of newly created "current" user...
But where is the M's data stored and updated in case of need?
Suppose, I want to change the model's current user's FirstName to "Gennady"?
I added a button for updating the model with this button click event handler:
private void button1_Click(object sender, RoutedEventArgs e)
{
}
aiming to change the model's data from it which will be reflected in GUI.
How can I do this, by clicking this button... sorry, by placing the code into this button1_Click()?
Or it is something wrong with my wish to do it?
Then. how to correctly update/change M in MVVM ?
Update:
All answers seem refer that I should not make changes in M but on VM.
Though I've specifically asked about referenced M-V-VM implementation with:
public CustomerViewModel()
{
_timer = new Timer(CheckForChangesInModel, null, 0, 1000);
}
private void CheckForChangesInModel(object state)
{
Customer currentCustomer = CustomerViewModel.GetCurrentCustomer();
MapFieldsFromModeltoViewModel(currentCustomer, this);
}
public static void MapFieldsFromModeltoViewModel
(Customer model, CustomerViewModel viewModel)
{
viewModel.FirstName = model.FirstName;
viewModel.LastName = model.LastName;
viewModel.TimeOfMostRecentActivity = model.TimeOfMostRecentActivity;
}
So, for example, upon implementing the code from Adolfo Perez's answer changes, the TextBox's content is changed from "Jim" to "Gennady" only for a period of interval set in _timer = new Timer(CheckForChangesInModel, null, 0, 1000);.
All logic of referenced by me M-V-VM in WPF approach is such that it is "M" should be updated, in order VM has caught up those changes, but not the "VM".
Even more, I cannot understand, if to make changes in VM how can they be reflected in M if the VM knows about M but - not vice versa - Model does not know about ViewModel).
In MVVM you should avoid code-behind. The reason is that you want to end up with testable classes, in this case your VM's that are completely independent from your V. You could run a set of unit tests on your VM without involving the V. You could also hook different types of Views without affecting your business logic.
Your button will bind its Command property to an ICommand property exposed in your VM. This Command in your VM will handle your click event in the method you specify.
In your View:
<Button Content="Change FirstName"
Command="{Binding Path=ChangeFirstNameCommand"}/>
In your ViewModel:
//Define your command
public ICommand ChangeFirstNameCommand {get;set;}
//Initialize your command in Constructor perhaps
ChangeFirstNameCommand = new RelayCommand(OnChangeFirstName,CanChangeFirstName);
private void OnChangeFirstName()
{
//Your FirstName TextBox in your V will be updated after you click the Button
this.FirstName = "Gennady";
}
private bool CanChangeFirstName()
{
//Add any validation to set whether your button is enabled or not.
// WPF internals take care of this.
return true;
}
It is very important to keep in mind that in this pattern your V knows about your VM and your VM knows about your M but not the other way around.
In your example if you want to change your Model FirstName property you woud have to do the following:
Create a VM which implements INotifyPropertyChanged
Expose your M FirstName property in your VM notifying changes
Create a TextBox in your XAML View and bind its Text property to your VM.FirstName setting Binding Mode=TwoWay.
<TextBox Text=
"{Binding Path=FirstName,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
As you type in the TextBox your FirstName will be directly populated in the VM-M. Also, thanks to the Two way binding, if you modify your FirstName property in your VM, that change will be reflected automatically in your V
Set your View.DataContext to your VM. This is what sets the Context for all your data bindings, unless you specify a different binding source.
If you want to persist changes in a DB then inject a service class in your VM which will take care of CRUD operations
Take a look at this simple example:
http://www.codeproject.com/Articles/126249/MVVM-Pattern-in-WPF-A-Simple-Tutorial-for-Absolute
Your model is your domain (business) objects. There are number of ways you can get them. For example you may have a repository class that gives you your data when you request it and handles the persistance when you wish to store it.
Your view-model is a class that handles UI logic, like updating fields, reacting on user actions, etc. In your case, you may pass an instance of CustomerRepository class to your view model. Then in view-model code you get the instance of Customer from the repository and fill your view-model properties on wich your UI elements are bound.
Your view is just a set of rules of how you wish to show the information to a user. It must be as declarative and logic free as possible.
Having a code like this:
private void button1_Click(object sender, RoutedEventArgs e)
{
}
in your view (or even worse - in your view-model) is a huge mistake wich breaks the pattern and may (and surely will) lead to troubles. You should bind ICommand fields of ViewModel to the buttons. You should not try to write a WPF application in a WinForm event-driven style.
That's how mvvm works in general and it's main purpose is to support multi-tier architecture in your application.
First off, you need to work on your V and VM.
If you are using a Click event for a button, you definatly aren't following this architecture.
You need to use WPF and XAML in your view to bind to your ViewModel, your ViewModel should be a subset of a particular or potentially many models and present the properties to the View which allows for binding.
I would also consider researching:
RelayCommand and ICommand for binding your buttons.
Repository pattern for interchanging your models and creating a way of CRUD
The tutorial which you have followed doesn't seem to be very good in that the concepts haven't really been put across properly or you haven't understood them.
If you have a pure WPF application it could be interesting to investigate the MVVM 'reverse' pattern, ViewModel-First.
For webdevelopment it is common to use MVVM because webpages get loaded through a browser and the View is constructed wich creates the ViewModel.
WPF users do not browse to pages (unless you use Page navigation) so it gets more interesting to follow VM-V-VM-M :
interface IMyView
Show();
//view implementations in different assemblies:
//silverlight
class SilverlightMyView:IMyView
Show();
//wpf
class WpfMyView:IMyView
Show();
class MyViewModel
IMyView _view;
MyModel _model;
//ex. view resolved by DI (Unity, ..)
MyViewModel(IMyView view)
_view = view
Show(model as MyModel)
_model = model;
_view.DataContext = this;
_view.Show();
What's the right way to get my viewmodel to trigger a custom lookup control to throw up a modal dialog that essentially represents that lookup viewmodel? The custom lookup control's data context is that of the parent record view model. The lookup control also has another DependencyProperty that has it bound to a lookupviewmodel property on the parent record view model and this represents a sub lookupviewmodel.
METHOD 1) I currrently use an event on the lookupviewmodel that the custom control knows to listen for.
METHOD 2) I tried throwing a validation exception within the setter of the property on the lookupviewmodel that the lookup control's text propery is bound too. Then I hooked the ErrorEvent in the custom lookup control. But it seems that if the user "corrects" the value from within the dialog while in this event, the original value sticks. And worse, even after I call Validation.ClearInvalid, another ErrorEvent still fires that somehow adds the error back. So everything works here in the sense that all the viewmodels have the correct data, it's just that it seems like the textbox is ignoring that the bound text property has changed on the underlying data source when inside an ErrorEvent. So it seems like I can't correct an error while inside the processing of that error?
Another sub issue within method 2 is that Validation.ClearInvalid doesn't remove the red error border. I had to manually clear the ErrorTemplate too. Is that right?
I'd like to find a way to use natural error handling within the control to get it to throw up the modal dialog.
This isn't what you use events for. Events exist to facilitate decoupling: the object raising the event shouldn't know or care what the object(s) listening to it are doing. You're expecting an event to be able to change the value of a property from inside the property's setter - or worse, your event handler is calling the very property setter that's raising the event that it's handling, which means that you have to do something pretty hackish to avoid a stack overflow.
Your description isn't very clear (you're describing both the problem you're having and the non-working solutions you're trying at the same time, which is confusing), but it sounds like what you're trying to do is something more like:
if (IsValid(value))
{
_Property = value;
}
else
{
_Property = GetValueFromDialog();
}
The problem is that you don't want to have code in your view model that throws up a dialog, since that creates a view model that can't be tested outside of your WPF application.
The answer in this case is to use dependency injection. Create an interface called IDialogService:
interface IDialogService
{
object GetValueFromDialog();
}
Now add this property to your view model:
public IDialogService DialogService { get; set; }
The above code becomes:
if (IsValid(value))
{
_Property = value;
}
else
{
_Property = DialogService.GetValueFromDialog();
}
Create a dialog service for use in your WPF application that actually throws up the dialog and gets the result. When you instantiate your view model in your application, do this:
MyViewModel vm = new MyViewModel { DialogService = new WpfDialogService(); }
Thus, in your application, the property setter will put up the dialog and get the result exactly as you expect it to.
For your unit tests, create a mock dialog that looks like this:
public class MockDialogService : IDialogService
{
private object Result;
public MockDialogService(object result)
{
Result = result;
}
public object GetValueFromDialog() { return Result; }
}
You can then write a test like:
MyViewModel vm = new MyViewModel { DialogService = MockDialogService(ExpectedResult) };
vm.Property = InvalidValue;
Assert.AreEqual(ExpectedResult, vm.Property);
The above is really more a sketch of a solution than a solution - depending on how your application uses dialogs, you may need a lot more features than what are sketched out here. If you take a look at MVVM frameworks you'll find that a lot of them implement dialog services of one kind or another.
You can use a framework like MVVMLight or Prism which allow you to pass payloads between different entities in totally decoupled ways. MVVMLight is very lightweight compared to Prism. It has a concept of Messanger which acts as a system wide event bus. Similarly you have EventAggregator in Prism.