One model, multiple view models and an updatable view using WPF? - c#

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));
}
}

Related

Pass parameter to UserControl viewmodel

I'm writing a monitoring program for a test rig. I'm using Prism and try to maintain good MVVM practice. However, I find it really hard to find a solution for my problem:
I have a service that receives measurement values from different sensors periodically. It passes the values with unique ids for each sensor to the data class.
The data class is the backbone of my program. It maintains a list with the last measurement of each sensor, updates their values and notifies value changes. It's a static class so every class has access to it.
//Rudimentary version of my data class, static and backbone of app.
public static class Data
{
// Event signaling the change of a measurement, includes modified id in event
#region event
public static event EventHandler<MeasurementChangedEventArgs> MeasurementChanged;
static void OnMeasurementChanged(MeasurementChangedEventArgs e) { MeasurementChanged?.Invoke(typeof(Data), e); }
#endregion
// List with custom measurement object containing multiple properties (e.g. Name, Value, Unit)
private static List<Measurement> measurements = new List<Measurement>();
// Used by server to set values
public static void SetMeasurementValue(int id, float value)
{
// Get entry(s)
IEnumerable<Measurement> data = measurements.Where(p => p.ID == id);
if (0 < data.Count())
{
// Set value
foreach (var item in data) { item.Value = value; }
// Signal change
OnMeasurementChanged(new MeasurementChangedEventArgs { id = id });
}
}
}
The ui is rather complex. It displays all sensor values, many in different representations. To make it easier for the developer to expand the program, I created UserControls e.g. a group of labels displaying name, value and unit of a sensor. I want to reuse these so I dont have to create them for every sensor. Here is how I#M doing it currently: The UserControl VIEW binds to variables like name, value and unit of the VIEWMODEL. The VIEWMODEL subscribes to the MeasurementChanged event of the data class. To know what sensor to display the VIEWMODEL need to know what sensor I want to display when I place a UserControl in the visual studio ui editor.
How do I tell the VIEWMODEL what sensor it should display, in xaml, when I place the UserControl in the visual studio ui editor?
Thanks!
EDIT 1 11.03:
I already researched a lot but can't find a solution that solves my problem. What I need is something like a dependency property (doesn't work because VIEWMODEL derives from BindableBase) or pass a constructor argument to the VIEMODEL in xaml (not really possible)...
You could use a PubSubEvent from the IEventAggregator to pass the information from your data class to your ViewModel.
It seem to me that your question is on how to implement drag and drop in MVVM.
I suggest you to add a reference in your project to GongSolutions.WPF.DragDrop.
then in xaml you can reference that package:
xmlns:dragDrop="clr-namespace:GongSolutions.Wpf.DragDrop;assembly=GongSolutions.Wpf.DragDrop"
Add this property to the Dragsource (the element you drag from):
dragDrop:DragDrop.IsDragSource="True"
and these to the DropTarget (the element you drop to)
dragDrop:DragDrop.IsDropTarget="True"
dragDrop:DragDrop.DropHandler="{Binding}"
Your ViewModel should implement the IDropTarget interface:
public class M_ViewModel : BaseViewModel, IDropTarget
And define the following functions:
public void DragOver(IDropInfo dropInfo)
{
}
public void Drop(IDropInfo dropInfo)
{
}
Now you should be able to trigger the DropEvent in your viewmodel.
About the information on which sensor the user selected, you can use the information from the selected element when you start the drag (maybe you have a list of elements, you can do a binding to the selected one and use that info). I did it that way, there may be a more elegant solution using DropInfo but I didn't research it.
Afterwards you give the parameter to the viewmodel of your usercontrol.

Model not implementing INotifyPropertyChanged

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.

Fody-PropertyChanged raising on Dependent Property Change

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.

Subscribe to INotifyPropertyChanged for nested (child) objects

I'm looking for a clean and elegant solution to handle the INotifyPropertyChanged event of nested (child) objects. Example code:
public class Person : INotifyPropertyChanged {
private string _firstName;
private int _age;
private Person _bestFriend;
public string FirstName {
get { return _firstName; }
set {
// Short implementation for simplicity reasons
_firstName = value;
RaisePropertyChanged("FirstName");
}
}
public int Age {
get { return _age; }
set {
// Short implementation for simplicity reasons
_age = value;
RaisePropertyChanged("Age");
}
}
public Person BestFriend {
get { return _bestFriend; }
set {
// - Unsubscribe from _bestFriend's INotifyPropertyChanged Event
// if not null
_bestFriend = value;
RaisePropertyChanged("BestFriend");
// - Subscribe to _bestFriend's INotifyPropertyChanged Event if not null
// - When _bestFriend's INotifyPropertyChanged Event is fired, i'd like
// to have the RaisePropertyChanged("BestFriend") method invoked
// - Also, I guess some kind of *weak* event handler is required
// if a Person instance i beeing destroyed
}
}
// **INotifyPropertyChanged implementation**
// Implementation of RaisePropertyChanged method
}
Focus on the BestFriend Property and it's value setter. Now I know that I could do this manually, implementing all steps described in the comments. But this is going to be a lot of code, especially when I'm planning to have many child properties implementing INotifyPropertyChanged like this. Of course they are not going to be always of same Type, the only thing they have in common is the INotifyPropertyChanged interface.
The reason is, that in my real scenario, I have a complex "Item" (in cart) object which has nested object properties over several layers (Item is having a "License" object, which can itself have child objects again) and I need to get notified about any single change of the "Item" to be able to recalculate the price.
Do you some good tips or even some
implementation to help me to solve
this?
Unfortunately, I'm not able/allowed to use post-build steps like PostSharp to accomplish my goal.
since I wasn't able to find a ready-to-use solution, I've done a custom implementation based on Pieters (and Marks) suggestions (thanks!).
Using the classes, you will be notified about any change in a deep object tree, this works for any INotifyPropertyChanged implementing Types and INotifyCollectionChanged* implementing collections (Obviously, I'm using the ObservableCollection for that).
I hope this turned out to be a quite clean and elegant solution, it's not fully tested though and there is room for enhancements. It's pretty easy to use, just create an instance of ChangeListener using it's static Create method and passing your INotifyPropertyChanged:
var listener = ChangeListener.Create(myViewModel);
listener.PropertyChanged +=
new PropertyChangedEventHandler(listener_PropertyChanged);
the PropertyChangedEventArgs provide a PropertyName which will be always the full "path" of your Objects. For example, if you change your Persons's "BestFriend" Name, the PropertyName will be "BestFriend.Name", if the BestFriend has a collection of Children and you change it's Age, the value will be "BestFriend.Children[].Age" and so on. Don't forget to Dispose when your object is destroyed, then it will (hopefully) completely unsubscribe from all event listeners.
It compiles in .NET (Tested in 4) and Silverlight (Tested in 4). Because the code in seperated in three classes, I've posted the code to gist 705450 where you can grab it all: https://gist.github.com/705450 **
*) One reason that the code is working is that the ObservableCollection also implements INotifyPropertyChanged, else it wouldn't work as desired, this is a known caveat
**) Use for free, released under MIT License
I think what you're looking for is something like WPF binding.
How INotifyPropertyChanged works is that the RaisePropertyChanged("BestFriend"); must only be fored when the property BestFriend changes. Not when anything on the object itself changes.
How you would implement this is by a two step INotifyPropertyChanged event handler. Your listener would register on the changed event of the Person. When the BestFriend gets set/changed, you register on the changed event of the BestFriend Person. Then, you start listening on changed events of that object.
This is exactly how WPF binding implements this. The listening to changes of nested objects is done through that system.
The reason this is not going to work when you implement it in Person is that the levels can become very deep and the changed event of BestFriend does not mean anything anymore ("what has changed?"). This problem gets larger when you have circular relations where e.g. the best friend of your monther is the mother of your best fiend. Then, when one of the properties change, you get a stack overflow.
So, how you would solve this is to create a class with which you can build listeners. You would for example build a listener on BestFriend.FirstName. That class would then put an event handler on the changed event of Person and listen to changes on BestFriend. Then, when that changes, it puts a listener on BestFriend and listens for changes of FirstName. Then, when that changes, it sends raises an event and you can then listen to that. That's basically how WPF binding works.
See http://msdn.microsoft.com/en-us/library/ms750413.aspx for more information on WPF binding.
Interesting solution Thomas.
I found another solution. It's called Propagator design pattern. You can find more on the web (e.g. on CodeProject: Propagator in C# - An Alternative to the Observer Design Pattern).
Basically, it's a pattern for updating objects in a dependency network. It is very useful when state changes need to be pushed through a network of objects. A state change is represented by an object itself which travels through the network of Propagators. By encapsulating the state change as an object, the Propagators become loosely coupled.
A class diagram of the re-usable Propagator classes:
Read more on CodeProject.
I have been Searching the Web for one day now and I found another nice solution from Sacha Barber:
http://www.codeproject.com/Articles/166530/A-Chained-Property-Observer
He created weak references within a Chained Property Observer. Checkout the Article if you want to see another great way to implement this feature.
And I also want to mention a nice implementation with the Reactive Extensions #
http://www.rowanbeach.com/rowan-beach-blog/a-system-reactive-property-change-observer/
This Solution work only for one Level of Observer, not a full Chain of Observers.
I wrote an easy helper to do this. You just call BubblePropertyChanged(x => x.BestFriend) in your parent view model. n.b. there is an assumption you have a method called NotifyPropertyChagned in your parent, but you can adapt that.
/// <summary>
/// Bubbles up property changed events from a child viewmodel that implements {INotifyPropertyChanged} to the parent keeping
/// the naming hierarchy in place.
/// This is useful for nested view models.
/// </summary>
/// <param name="property">Child property that is a viewmodel implementing INotifyPropertyChanged.</param>
/// <returns></returns>
public IDisposable BubblePropertyChanged(Expression<Func<INotifyPropertyChanged>> property)
{
// This step is relatively expensive but only called once during setup.
MemberExpression body = (MemberExpression)property.Body;
var prefix = body.Member.Name + ".";
INotifyPropertyChanged child = property.Compile().Invoke();
PropertyChangedEventHandler handler = (sender, e) =>
{
this.NotifyPropertyChanged(prefix + e.PropertyName);
};
child.PropertyChanged += handler;
return Disposable.Create(() => { child.PropertyChanged -= handler; });
}
Check-out my solution on CodeProject:
http://www.codeproject.com/Articles/775831/INotifyPropertyChanged-propagator
It does exactly what you need - helps to propagate (in elegant way) dependent properties when relevant dependencies in this or any nested view models change:
public decimal ExchTotalPrice
{
get
{
RaiseMeWhen(this, has => has.Changed(_ => _.TotalPrice));
RaiseMeWhen(ExchangeRate, has => has.Changed(_ => _.Rate));
return TotalPrice * ExchangeRate.Rate;
}
}
Please take a look at EverCodo.ChangesMonitoring. This is a framework to handle PropertyChanged and CollectionChanged events on arbitrary hierarchy of nested objects and collections.
Create a monitor to handle all change events of the object tree:
_ChangesMonitor = ChangesMonitor.Create(Root);
_ChangesMonitor.Changed += ChangesMonitor_Changed;
Do arbitrary modifications on the object tree (all of them will be handled):
Root.Children[5].Children[3].Children[1].Metadata.Tags.Add("Some tag");
Root.Children[5].Children[3].Metadata = new Metadata();
Root.Children[5].Children[3].Metadata.Description = "Some description";
Root.Children[5].Name = "Some name";
Root.Children[5].Children = new ObservableCollection<Entity>();
Handle all events in one place:
private void ChangesMonitor_Changed(object sender, MonitoredObjectChangedEventArgs args)
{
// inspect args parameter for detailed information about the event
}

Extending a solution for simple binding to a 'Text property to multiple Controls to handle binding to any Type?

My question is : how to move beyond writing a custom implementation of a technique for databinding multiple controls (controls without built-in DataSource properties), for each possible type of data, to simple properties ... as described and demonstrated in code that follows ... to achieve a more poweful solution that will be independent of whether the binding is to a string, or an int, or other types.
My guess is: this will involve reflection; but, I'm stuck at that point. I'm looking for strategic advice on which "direction" to move next, hints, clues, not a complete code answer, but of course I appreciate all responses, and I'll sure study code if you post code in reply ! Marc Clifton's 2005 article on CodeProject Simple Databinding: appears to demonstrate a reflection based approach: but, honestly, I do not really grok his code, and, in terms of .NET, 2005 is a long time ago.
Background: Partly in response to various SO questions and answers, like: Update Usercontrol on Three Forms: I've evolved a successful technique for databinding text properties of various controls simultaneously to one source defined in a Public class; also been able to "abstract" some of the details of the binding process using a static class that defines one extension method, and two public methods.
I've verifed that TextBoxes on Controls in a "MainForm," TextBoxes on a UserControl on the MainForm, and a TextBox on a second Form opened "independently" (i.e., form2.Parent == null) all update properly (i.e., two-way binding is in effect) from the "DataSource equivalent" public class. Change one: change all.
Code: an instance of this class will supply the target property (theText) for databinding:
public class TextDataBinder
{
public event PropertyChangedEventHandler PropertyChanged;
private string _theText;
public string theText
{
get { return _theText; }
// note : if 'setter is declared 'internal : blocks
// auto-updating when run-time user modifies consumers
// but will still allow update via code
set
{
_theText = value;
OnPropertyChanged(new PropertyChangedEventArgs("theText"));
}
}
protected void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, e);
}
}
}
Code: this static class enables hiding some of the binding process complexity, and allows easy binding to multiple controls:
public static class TextBindingExtender
{
public static TextDataBinder CurrentDataSource;
public static void SetCurrentDataSource(TextDataBinder newCurrentDataSource)
{
CurrentDataSource = newCurrentDataSource;
}
// extension method for Control
public static void AddTextBinding(this Control theControl, string controlPropertyName, string targetPropertyName)
{
theControl.DataBindings.Add(controlPropertyName, CurrentDataSource, targetPropertyName, false, DataSourceUpdateMode.OnPropertyChanged);
}
// bind to all Controls in a List<Control>
public static void AddTextBindings(List<Control> theControls, string controlPropertyName, string targetPropertyName)
{
foreach (Control theControl in theControls)
{
theControl.AddTextBinding(controlPropertyName, targetPropertyName);
}
}
}
How the above classes are used (in a Form Load event) :
// create a new TextDataBinder
TextBindingExtender.CurrentDataSource = new TextDataBinder();
// bind to multiple textboxes, label, on a UserControl, on another Form, etc.
TextBindingExtender.AddTextBindings(new List<Control> { textBox1, textBox2, userControl11.tb, label1, instanceOfForm2.tb }, "Text", "theText");
// test assigning some initial text to the bound property
TextBindingExtender.CurrentDataSource.theText = "some initial text";
It really depends what you want to do; but ultimately common data-binding (for simple properties, done manually) consists of:
obtaining a property; preferably via TypeDescriptor.GetProperties(obj)[propName], giving you an abstraction (PropertyDescriptor)
asking the property if it is read-only (.IsReadOnly)
obtain (or set) the value (.GetValue(), .SetValue())
asking it for a converter to format / parse the value (.Converter, .ConvertFromString(), .ConvertToString()) THIS is a key bit that means you don't have to worry about what the data type is
asking it for the caption (.DisplayName, or .Name if that it empty/null)
asking it if it supports property-specific notification (.SupportsChangeEvents)
asking it to add/remove a change handler (.AddValueChanged(), .RemoveValueChanged())
you might also want to look at whether the object supports centralised notification (look for INotifyPropertyChanged)
If you might be binding to a list rather than a single object:
- the list might be abstracted behind IListSource
- the list might have custom properties, so check for ITypedList
- otherwise, identify the Type of the items and use TypeDescriptor.GetProperties(type)
- you need to consider a "currency manager" (i.e. should all the things bound to the same list be pointing to the same record in the list all the time)
There are also things like ICustomTypeDescriptor and TypeDescriptionProvider to consider, but most of the time TypeDescriptor handles this for you automatically.
As you can see - lots of things to think about! Lots of work... the one thing that you don't have to do is reflection; this is abstracted behind PropertyDescriptor. The reason for this is that not all data is static-typed; think about DataTable - the columns (which map to bindable data properties) are not fixed at compile-time, so reflection isn't appropriate. Likewise, some other types have custom "property bag" implementations. PropertyDescriptor lets your code handle either dynamic (not in the 4.0 sense) and reflective properties identically. It also works nicely with things like "HyperDescriptor", another property customisation.

Categories

Resources