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.
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));
}
}
I'm working on a UWP application that is essentially a control panel for several of the same objects - let's call them Plate objects.
Functionally, a user needs to be able to create & remove Plate objects with specific properties, and all of the currently available Plate objects are shown on the Main Page, with their corresponding unique properties as well as controls to modify them.
They way I've implemented this is to create a Grid on the Main Page, and for each available Plate, add a Frame into a grid column, and each Frame navigates to a custom PlateView page to show what's basically a horizontal list of PlateView columns.
My problem is that I want to be able to two-way bind data for each control/property from each Plate to its corresponding PlateView.
Right now I store the List of all Plates as a public variable in App.cs, as I need to be able to get and modify this master list from multiple parts of the application through its complete lifecycle.
The way I understood the data binding description in the UWP documentation, either my Plate object can implement INotifyPropertyChanged or I can create a separate PlateViewModel class that implements it.
With Plate implementing it, the PlateView will set its ViewModel to the correct index of Plate in the List (this.ViewModel = App.plateList[1]), but I assume that makes a copy...? So if I modify a variable in PlateView, it won't actually change the Plate object in App.cs.
With a new PlateViewModel class, I don't understand how I wouldn't have the same problem, but inside the PlateViewModel class. For example, MS's documentation shows:
public class RecordingViewModel
{
private Recording defaultRecording = new Recording();
public Recording DefaultRecording { get { return this.defaultRecording; } }
}
Even if I set an internal Plate object inside PlateViewModel, don't I have to call a variable from the XAML {x:bind ...} syntax? So I'd have to make a copy of every variable from the correct Plate into PlateViewModel, and they wouldn't necessarily link to the original Plate object in App.cs?
Thanks!
To simply answer your question without getting into a whole discussion about MVVM, having the additional ViewModel property to bind to does not create a new instance of the List. What I would do is create a View Model property that presents your model with a getter and setter (in this case the App.cs property is your model):
ViewModel:
public List<Plate> MyPlates
{
get
{
return ((App)Application.Current).MyGlobalListofPlates;
}
set
{
((App)Application.Current).MyGlobalListofPlates = value;
OnPropertyChanged("MyPlates");
}
}
That code makes it obvious that it's not a new object being made and it gives you some control over what changes to the data are allowed. Another option is to assign the property in the constructor. This does not create a new object either. It is a reference to the original and any changes you make to it will be reflected everywhere.
public List<Plate> MyPlates { get; set; }
public MyViewModel() //Constructor
{
MyPlates = ((App)Application.Current).MyGlobalListofPlates;
}
This second code block has problems because there's no INotify (which might not matter if it's 2-way binding? Not sure...). Anyway I'm just showing you that assigning the object to another object just creates a reference. You definitely want to read on up on value types versus reference types in C#. Almost any time you do "ThisObject = ThatObject" in C# you're just creating a pointer to the same object in memory.
I tried UIElement.InvalidateVisual but it didn't work. I read about this INotifyPropertyChanged but I don't know exactly how it is used, not to mention I'm talking about User Controls, it seems I need to use that INotifyPropertyChanged to all of the controls.
I'm having trouble refreshing combo boxes changes and data grid changes, what I did is to close and reopen the form but I don't want that approach because it seems sluggish in re-executing every user control constructors.
this is my code:
if (linkSource.ToString() == "BreedList.xaml")
{
this.InvalidateVisual();
}
else if (linkSource.ToString() == "PetTypeList.xaml")
{
this.InvalidateVisual();
}
else if (linkSource.ToString() == "IrritantList.xaml")
{
this.InvalidateVisual();
}
This answer is an attempt to give you a peek into how WPF applications work, but I am not offering to teach you WPF... that is your job.
As #HighCore correctly pointed out, there is no UserControl.Refresh method. In a WPF Application (using MVVM), we create custom class objects called view models that contain the data and functionality that the UserControls and/or Windows (called views) need to provide. They are paired by setting the view DataContext property to an instance of the relevant view model. There are several ways to do that... this is just one way:
public SomeView()
{
...
DataContext = new SomeViewModel();
}
Now, all of the controls declared in SomeView have access to all of the (public) properties and ICommands from the SomeViewModel class. We use them with data binding:
<TextBox Text="{Binding SomePropertyInSomeViewModel}" ... />
...
<ListBox ItemsSource="{Binding SomeCollectionPropertyInSomeViewModel}" />
During initialisation of the SomeViewModel class, we might see something like this:
public SomeViewModel()
{
SomeCollectionPropertyInSomeViewModel = SomeDataSource.GetSomeCollection();
}
This gets the data from any external data source and populates the SomeCollectionPropertyInSomeViewModel property. In turn, the SomeCollectionPropertyInSomeViewModel property provides the collection items for the ListBox. So finally, to answer your question, How do we refresh the data?, the answer is to call the methods that get the data again. So you could do something like this:
public void RefreshData()
{
SomeCollectionPropertyInSomeViewModel = SomeDataSource.GetSomeCollection();
}
Ok, so here ends today's lesson. For any further questions, please refer to the proper Microsoft documentation on MSDN. For follow up information, please see the Introduction to WPF page on MSDN.
To add to previous answer: there are two ways to create "SomeViewModel". In both ways it should notify about a property change.
1) Use a DependencyObject. This is a standard WPF way, all UserControls are also DependencyObjects. Taken from here, read full explanation: http://wpftutorial.net/DependencyProperties.html
public class SomeViewModel : DependencyObject
{
// Dependency Property
public static readonly DependencyProperty CurrentTimeProperty =
DependencyProperty.Register("CurrentTime", typeof(DateTime),
typeof(SomeViewModel), new FrameworkPropertyMetadata(DateTime.Now));
// .NET Property wrapper
public DateTime CurrentTime
{
get { return (DateTime)GetValue(CurrentTimeProperty); }
set { SetValue(CurrentTimeProperty, value); }
}
}
When CurrentTime changes everybody which is registered ( for example using {Binding CurrentTime} in XAML ) will get a notification and "Refresh"
2) Use INotifyPropertyChanged interface. Here's a small example:
http://msdn.microsoft.com/en-us/library/vstudio/ms229614(v=vs.100).aspx
There are some differences between this approaches ( for example you can't create binding to INotifyPropertyChanged property ). Please read here:
http://www.codeproject.com/Articles/62158/DependencyProperties-or-INotifyPropertyChanged
INotifyPropertyChanged vs. DependencyProperty in ViewModel
Good luck!
I am trying to implement a simple WPF data analysis application using the MVVM design pattern, in which one can use several different methods to analyse some data (loaded from files).
In the first screen, the user should be able to choose the method he likes to employ. After he has done that, and loaded the data, the area previously occupied by the method selection screen should be replaced by the analysis results.
Currently, my MainWindowViewModel has a property "CurrentViewModel" of type object, that can be set to either the method selection viewmodel or on of the analysis results viewmodels, which are then rendered by using datatemplates.
The problem I am facing is, that I don't know how the different viewmodels should communicate.
The method selection screen needs a
list of available methods.
The main screen needs to know what method was
selected and choose the approriate
viewmodel to display the results.
The data loaded somehow needs to get into the class doing the actual work, and the results viewmodel needs to know about this to know where to get its data from.
Everything I can think of leaves the MainWindowViewModel to do all the negotiating between the different viewmodel and model classes.
How would I optimally design this?
For what it's worth, I helped implement a module for a similar Composite Application. So what follows is based on anecdotal experience,
To your first point, our application or "shell" needs an explicit enumeration of "available" methods. We can supply this any way we wish, either explicitly (via config) or implicitly (via reflection). As a preference, I favor the former as it is less "magical".
To your second point, in addition to an explicit enumeration our shell must maintain a map from choice to implementation. Again, this may be accomplished any number of ways, but typically our enumeration is a list of "Types", and when a type is selected we request an implementation of that type from a factory. The easiest way to implement this pattern is to leverage an Inversion of Control container, such as Castle Windsor, Unity, Ninject, etc. To be honest, I don't remember what we used internally.
For example, consider,
// a simple Plain Old C Object that describes business methods.
public class BusinessMethod
{
// user-friendly name
public string Name { get; set; }
// type that actually implements
public Type ImplementationType { get; set; }
}
// ... meanwhile, back on the ranch ...
public void OnBusinessMethodSelection ()
{
// 1. if selected
if (BusinessMethodList.SelectedItem != null)
{
// 2. retrieve selected item
BusinessMethod selected =
(BusinessMethod)(BusinessMethodList.SelectedItem);
// 3. request implementation of selected item from
// IoC container
object implementation =
_container.Resolve (selected.ImplementationType);
}
}
To your third point, we need a way for disparate parts to communicate. Unfortunately we cannot rely on design-time methods (ie Command and Data bindings), so we must implement our own Event Aggregation service. Basically a "singleton" (as in single instance not static class) that knows about subscribers and publishers, and preferably an implementation that offers strong typing of arguments. Fortunately for us, many a greater man have gone before us, and we may benefit from their experience. Check out Kent Boogaart's Event Hub.
Here is an example of how we would use an Event Aggregator
// an example of a strongly typed subject. notice how subject
// defines content. semanticly, when someone creates and publishes
// an instance of this subject, they are requesting someone show
// an analysis view based on data content,
public class AnalysisSubject
{
// subject content, in this case a data result from
// a business method
public object Data { get; set; }
}
public class MainWindow : ISubscriber<AnalysisSubject> ...
{
// use whatever implementation of an IoC container we like
// here i assume we abstract from implementation and use a
// custom interface IContainer that exposes functionality
// that we need
private readonly IContainer _container = null;
public class MainWindow ()
{
// we're teh r00tz! we create an instance of IoC
// container for use throughout application
IContainer _container = new CustomContainer ();
// our container exposes both parameterized and
// type-parameterized resolve methods
IEventHub events = _container.Resolve<IEventHub> ();
events.Subscribe<AnalysisSubject> (this);
}
#region ISubscriber<AnalysisSubject>
// part of strongly typed subscriptions is that we
// may now handle strongly typed publications! yay!
public void Receive (AnalysisSubject subject)
{
// 1. request to display analysis of data
Type analysisType = subject.Data.GetType ();
// 2. get view control based on payload type
//
// NOTE: implicit usage below is not consistent
// with previous invocations, here we are submitting
// a type of something we already have, and actually
// want back something that knows how to handle it.
// most IoC containers can provide this functionality
// through "facilities" add ons that accept a
// parameter\discriminator like below, and produce
// something in return.
Control control = (Control)(_container.Resolve (analysisType));
// [alternatively] if the above is too "magical" where
// IAnalysisFactory is an interface we define for this
// express purpose
//IAnalysisFactory factory = _container.Resolve<IAnalysisFactory> ();
//Control control = factory.GetAnalysisControlFor (analysisType);
// 3. assign subject data to control
Control.DataContext = subject.Data;
// 4. display control
}
#endregion
}
And an example of publication
public class SomeBusinessView
{
private readonly IEventHub _events = null;
// we cannot function without an event aggregator of
// some kind, so we declare our dependency as a contructor
// dependency
public SomeBusinessView (IEventHub events)
{
_events = events;
}
public void DoMyThang ()
{
// 1. do some business
MyBusinessData data = SomeBusinessFunction ();
// 2. publish complete event
AnalysisSubject subject = new AnalysisSubject () { Data = data, };
_events.Publish (subject);
}
}
Every View typically gets a viewmodel. If you are dealing with nested usercontrols inside one window, using multiple viewmodels (one per control) may be overkill.
If you have a viewmodel per control and are disconnected regarding communication between them then you can either have a common model that is universal to all the viewmodels OR have a global event provider that allows models to talk to eachother. (Something they all can reference for change notifications etc).
If you don't use a viewmodel per control then have the viewmodel bound to the main window in which all nested controls report to the main window which reports to the viewmodel for the main window.
Could the data source of the selection be bound to the types of ViewModels you have and the path possibly be the name of each (be it a string property for a formatted name or the Type name explicitly). On Selection you then have the relevant object that has been selected.
Possibly each of the ViewModels references a common provider which as you say performs the results analysis and the two different Views simply display the same data in different ways.
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.