I am relative new to OOP and C# and wanted to ask what would be the "best practice" to make two classes use Methodes from each other.
Example: I have a "Main Class", this instantiates a Class "UI" which manages the communication to a touch-display and a class "Sensor" which communicates with an external Sensor.
When someone is pushing a button on the Display, an event is triggered in the UI Class. As a reaction, the "ReadSensor" Methode form the Sensor Class needs to be called. (And the other way around, the "SensorDataCallback" needs to write stuff to the Display).
Now in C++ I would have made both Class Objects global, but thats no option in C#.
I tried adding Methodes to the Class which accept a ref to the respective other class instance and store them, but this does not seem like the best aproach. My other way was making both classes static since there is only one Display and one Sensor, but that also can't be the best way I guess.
Sketch
Can anybody give me a hint?
I wouldn't recommend tying two classes so closely to each other, indeed you'll end up with circular references.
Try and keep your classes self contained, so that there remains a definite separation of concerns.
Thus the Sensor class only deals with retrieving sensor data and raising notification of new values (if it is to read values automatically at regular intervals or asynchronously).
Your UI class only deals with the display and reacting to user input.
Your Main class acts as a data handler between the two.
The UI display code notifies the Main class of a user request for information; the Main class handles this and triggers the Sensor class to retrieve a new sensor value.
The Sensor class notifies/passes the new sensor value to the main class; the main class then notifies the UI that a new value is available.
Thus the Sensor does not need to know about the display, not does the display need to know about the sensor.
The above fits the current "best practice" of using MVVM (Model-View-ViewModel) structure for C# programmes with a user interface. In this case your View is the display, the ViewModel is your main class and the Model is your sensor class.
In this arrangement the View class uses databinding to retrieve the sensor value from the ViewModel. The View will raise an event (bound to a Command maybe) on the press of a button that the ViewModel reacts to, triggering it to request for updated data from the sensor. The sensor supplies that data to the ViewModel which when it updates its sensor value property raises a property changed notification that triggers the View to update.
Here's a very basic example of MVVM in a WPF app. I've not included the whole project just the basic classes:
View: (MainWindow.xaml)
<Window x:Class="SimpleWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SimpleWPF"
mc:Ignorable="d"
Title="MainWindow" Height="204.673" Width="219.626">
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Grid>
<TextBox x:Name="textBox" Text="{Binding SensorValue, Mode=OneWay }" HorizontalAlignment="Left" Height="23" Margin="38,47,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
<Button x:Name="button" Command="{Binding ReadSensorCommand}" Content="Read" HorizontalAlignment="Left" Margin="62,94,0,0" VerticalAlignment="Top" Width="75"/>
</Grid>
ViewModel (MainViewMode.cs) with a helper class:
public class MainViewModel :INotifyPropertyChanged
{
public MainViewModel()
{
ReadSensorCommand = new RelayCommand(new Action<object>(ReadFreshSensorData));
}
private ICommand _ReadSensorCommand;
private int _sensorValue;
private readonly SensorModel _sensor = new SensorModel();
private void ReadFreshSensorData(object o)
{
SensorValue =_sensor.ReadSensor();
}
public int SensorValue
{
get=>_sensorValue;
private set
{
_sensorValue = value;
OnPropertyChanged(nameof(SensorValue));
}
}
public ICommand ReadSensorCommand
{
get => _ReadSensorCommand;
set => _ReadSensorCommand = value;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
class RelayCommand : ICommand
{
private readonly Action<object> _action;
public RelayCommand(Action<object> action)
{
_action = action;
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_action(parameter ?? "");
}
}
Finally the Model (SensorModel.cs)
public class SensorModel
{
private Random _rnd = new Random();
public int ReadSensor()
{
return _rnd.Next(1000);
}
}
Admittedly this only demonstrates a user triggering a read from the sensor, but SensorModel could have an event property on its interface and MainViewModel a handler. SensorModel could then invoke the event to pass a value to MainViewModel which would then update SensorValue from the event handler.
Related
I'm not sure how to make navigation using mvvm. I'm a beginner so I haven't used any framework like mvvm light.
I found good example https://rachel53461.wordpress.com/2011/12/18/navigation-with-mvvm-2/. But it is not exactly what I'm looking for because in my app each view will cover all window. So when I will change page i will have no controls access from the mainview.
So I decided to make one MainViewModel for changing ViewModels (as in Rachel Blog) but each ViewModel should know about MainViewModel to execute change view. So when I create PageViewModel, I pass in constructor MainViewModel with public method, for example, changeview().
Is it a good way of doing this? Or, maybe, there's a better way to achieve this?
The child viewmodels should not know about main viewmodel.
Instead they should raise events with names like Forward or Back and so forth. ChangeView is the only example you give, so we’ll go with that.
We'll have the child viewmodel expose commands that cause the events to be raised. Buttons or MenuItems in the child view's XAML can bind to the commands to let the user invoke them. You can also do that via Click event handlers calling viewmodel methods in the child view code behind, but commands are more "correct", because at the cost of a little more work in the viewmodel, they make life a lot simpler for creators of views.
Main viewmodel handles those events and changes the active page viewmodel accordingly. So instead of child calling _mainVM.ChangeView(), child raises its own ChangeView event, and the main VM’s handler for that event on the child calls its own method this.ChangeView(). Main VM is the organizer VM, so it owns navigation.
It’s a good rule to make code as agnostic as possible about how and where it’s used. This goes for controls and viewmodels. Imagine if the ListBox class required the parent to be some particular class; that would be frustrating, and unnecessary as well. Events help us write useful child classes that don’t need to know or care anything about which parent uses them. Even if reuse isn’t a possibility, this approach helps you write clean, well-separated classes that are easy to write and maintain.
If you need help with the details, provide more code, and we can go through applying this design to your project.
Example
public class MainViewModel : ViewModelBase
{
public MainViewModel()
{
FooViewModel = new FooViewModel();
FooViewModel.Back += (object sender, EventArgs e) => Back();
}
public FooViewModel FooViewModel { get; private set; }
public void Back()
{
// Change selected page property
}
}
public class FooViewModel : ViewModelBase
{
public event EventHandler Back;
private ICommand _backCommand;
public ICommand BackCommand {
get {
if (_backCommand == null)
{
// It has to give us a parameter, but we don't have to use it.
_backCommand = new DelegateCommand(parameter => OnBack());
}
return _backCommand;
}
}
// C#7 version
public void OnBack() => Back?.Invoke(this, EventArgs.Empty);
// C# <= 5
//protected void OnBack()
//{
// var handler = Back;
// if (handler != null)
// {
// handler(this, EventArgs.Empty);
// }
//}
}
// I don't know if you already have a DelegateCommand or RelayCommand class.
// Whatever you call it, if you don't have it, here's a quick and dirty one.
public class DelegateCommand : ICommand
{
public DelegateCommand(Action<object> exec, Func<object, bool> canExec = null)
{
_exec = exec;
_canExec = canExec;
}
Action<object> _exec;
Func<object, bool> _canExec;
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return _canExec == null || _canExec(parameter);
}
public void Execute(object parameter)
{
if (_exec != null)
{
_exec(parameter);
}
}
}
How to invoke BackCommand from child XAML:
<Button Content="Back" Command="{Binding BackCommand}" />
Background
In order to implement async commands in an MVVM app I went through the following tutorials by Stephen Cleary.
https://msdn.microsoft.com/magazine/dn605875 Async Programming : Patterns for Asynchronous MVVM Applications: Data Binding
https://msdn.microsoft.com/en-us/magazine/dn630647.aspx Async Programming : Patterns for Asynchronous MVVM Applications: Commands
Problem
When reimplementing what he proposed step by step I stumbled upon the problem that the PropertyChanged event handler in the command is always null. When running Steves sample code it is not null.
In order to understand this better I started from scratch with implementing the most basic command that one could think of:
public class SimpleCommand : ICommand, INotifyPropertyChanged
{
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
// having a break point on the following line
throw new NotImplementedException();
}
public event EventHandler CanExecuteChanged;
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Note that I have a break point set on the line that throws the NotImplementedException because I just want to see whether PropertyChanged is null or not.
The viewmodel basically just creates this command and the view binds to it via a button.
public class MainViewModel : INotifyPropertyChanged
{
public ICommand SimpleAction { get; set; }
public MainViewModel()
{
SimpleAction = new SimpleCommand();
}
...
}
The window just contains one button to call that command
<Window ...>
<Window.DataContext>
<viewModels:MainViewModel></viewModels:MainViewModel>
</Window.DataContext>
<Grid>
<Button
Command="{Binding SimpleAction}"
Content="Click Me!"></Button>
</Grid>
</Window>
My assumption is that when a command implements INotifyPropertyChanged then the framework listens onto the PropertyChanged event which is obviously wrong.
So how does it work in Steves examples then? There he just implements INotifyPropertyChanged on NotifyTaskCompletion or AsyncCommand and PropertyChanged is not null.
Looking at other SO posts the usual answer in general is that DataContext is not set. Still I don't see how to set this for a command.
Actual Question
How to implement INotifyPropertyChanged properly on a command (based on ICommand) in MVVM (C#)?
The command itself has no subscribers unless you bind to a property of the command itself.
That's what #Stephen Cleary does in his article. He binds to the Execution property of the AsyncCommand<TResult> class. You bind to the SimpleAction property of the MainViewModel class.
So in your sample code, the SimpleCommand object has no subscribers and that's why the event handler returns a null reference.
After a major edit to this quesiton, I'm hoping it's now clear.
I'm very lost with binding in WPF when 1 change should affect multiple properties.
I regularly use VVM to bind my ViewModel to my View and I would say I'm OK with it.
I am trying to implement a state controller. This means that, what ever settings I made in part of my UI, the reflection is through out.
For example in my part of my UI, I can toggle a feature on or off, such as "show images"
When I make this change, I'd like everything in my application to be notified and act accordingly.
So, my StateController class will have a property
public bool ShowImages
And in my View, I'd likely have something like
<image Visible ="{Binding ShowImages", Converter={StaticConverter ConvertMe}}" />
The problem I have is how I go about making the StateController alert all of my ViewModels of this.
Currently, in each ViewModel I'm assuming I'd have to have the same property repeated
public bool ShowImages
EG
public class StateController : BaseViewModel
{
public bool ShowImages{get;set;}//imagine the implementation is here
}
public class ViewModelB : BaseViewModel
{
public bool ShowImages{}//imagine the implementation is here
}
public class ViewModelB : BaseViewModel
{
public bool ShowImages{}//imagine the implementation is here
}
So, my question is, if I updated ViewModelB.ShowImages, how would I first inform the StateController which in turn updates all ViewModels.
Is this something the INotifyPropertyChanged can do automatically for me since they all share the same propertyName, or do I have to implement the logic manually, eg
public static class StateController
{
public bool ShowImages{get;set;}//imagine the implementation is here
}
public class ViewModelA : BaseViewModel
{
public bool ShowImages
{
get { return StateController.ShowImages; }
set { StateControllerShowImages = value;
OnPropertyChanged("ShowImages"); }
}
}
public class ViewModelB : BaseViewModel
{
public bool ShowImages
{
get { return StateController.ShowImages; }
set { StateControllerShowImages = value;
OnPropertyChanged("ShowImages"); }
}
}
I hate the idea of the above implementation but it does show what I'm trying to achieve. I just hope there is a better way!
The PropertyChange notification is only raised for that one object model.
So raising a change notification of the "Name" property of ClassA will only update the UI in cases where it's bound to that specific ClassA.Name. It won't trigger a change notification for any ClassB.Name, or other instances of ClassA.Name.
I would suggest using a Singleton here for your StateModel, and having your other models subscribe to the StateModel.PropertyChanged event to know if it should update, like this answer.
public ViewModelA
{
public ViewModelA()
{
StateController.Instance.PropertyChanged += StateController_PropertyChanged;
}
void StateController_PropertyChanged(object sender, NotifyPropertyChangedEventArgs e)
{
// if singleton's ShowImages property changed, raise change
// notification for this class's ShowImages property too
if (e.PropertyName == "ShowImages")
OnPropertyChanged("ShowImages");
}
public bool ShowImages
{
get { return StateController.Instance.ShowImages; }
set { StateController.Instance.ShowImages = value; }
}
}
If I understood you correctly, you are looking for a mechanism that allows your different ViewModels to communicate between each other.
One possible way would be to implement the Observer Pattern (a code example can be found here: "Observer pattern with C# 4"). In this way your ViewModel subscribe each other to receive change notifications from a "publisher", i.e. the ViewModel that had its value changed. You have a good control over who receives which notification from which publisher. The downside of this approach is a tight coupling between your models.
My approach would be this:
Use a message dispatcher. Your ViewModels can subscribe to a certain type of message, e.g. ShowImagesChanged. If any of your ViewModels changed the ShowImages property, that ViewModel calls the dispatcher to send out such a ShowImagesChanged message with your current values.
This way you can keep you ViewModels decoupled from each other. Still, although the ViewModels do not know each other this gives a way to exchange data between them.
Personally, I have used the Caliburn Micro MVVM framework several times for this, but there should be enough other MVVM frameworks that provide the same functionality to fit your taste.
The Calibiurn Micro documentation and how easily the dispatcher can be used is here: Event Aggregator
To avoid code repetition you can create a class derived from BaseViewModel that implements your property and have ViewModelA, ViewModelB extend it. However, this does not solve the problem of keeping each instance updated.
In order to do so, you may:
Use a static class (your current solution) or a Singleton as suggested in one of the comments. This is simple but has potential problems such as race conditions and coupling.
Have your ShowImages binding property repeated in each ViewModel and update it by subscribing to a ShowImagesChanged event. This could be published through a Command executed from the UI. I'd say this is the WPF approach and has the benefit of decoupling the ShowImages state management from its consumption.
Assign the ShowImagesupdate responsibility to a single ViewModel and subscribe to the its PropertyChanged in the other ViewModels so that they update accordingly. Better than the first option, but still huge coupling.
Why repeat properties at all? Just bind to StateController itself.
Say we have singleton StateController:
public class StateController : INotifyPropertyChanged
{
private static StateController instance;
public static StateController Instance {
get { return instance ?? (instance = new StateController()); }
}
//here`s our flag
private bool isSomething;
public bool IsSomething
{
get { return isSomething; }
set
{
isSomething = value;
PropertyChanged(this, new PropertyChangedEventArgs("IsSomething"));
}
}
private StateController(){}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
}
Then in base VM class just make a reference to this controller:
public StateController Controller { get { return StateController.Instance; } }
And where needed bind like this:
<CheckBox IsChecked="{Binding Controller.IsSomething}">
Test
</CheckBox>
This way every binding will work with one property and react to one property. If you need some custom code to work you can subscribe to PropertyChanged of StateController where needed and take action.
in my C# WPF Project (working with VS2012) my goal is to use existing Data from a class in a new Window I created...
Therefore I added a new Window (WPF) to my Project and called it DijkstraWindow. In my MainWindow there is a Menu and when you click the suitable item the DijkstraWindow is opened. In my MainWindow.xaml.cs this is the Code to do this:
private void Dijkstra_Click(object sender, RoutedEventArgs e)
{
var DWindow = new DijkstraWindow();
DWindow.Show();
}
Now I need to access data (which is created while the application is running) and this is stored in a list which is stored in a class. But I have no idea how to do this.
I tried the following:
1.
Creating a new object in DijkstraWindow:
var mwvm = new MainWindowViewModel();
The data is accessible (in my new DijkstraWindow) but it just takes the data which is initialized when starting the application. So this is the wrong way. Because there a some list which is filled while the application is running. I want to use this data in my new Window.
2.
In my DijkstraWindow.xaml.cs I tried to inherit from the class where my data is, but then the compiler is complaining
"Partial declarations must not specify different base classes"
So I read you have also to changed your xaml file, so changed it to:
<local:MainWindowViewModel x:Class="Graphomat.DijkstraWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Graphomat"
Title="DijkstraWindow" Height="300" Width="300">
<Grid/>
</local:MainWindowViewModel>
This is also not working, then my DijkstraWindow has no information about the show method?
Could someone please help me out with this?
Thank you!
edit
Here ist the Class Declaration:
*/using somestuff */
namespace Graphomat
{
/// <summary>
/// Interaction logic for DijkstraWindow.xaml
/// </summary>
public partial class DijkstraWindow : MainWindowViewModel
{
public DijkstraWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
}
}
}
Tried to inherit from class "MainWindowViewModel", but this doesn't work because the xaml file..
The compiler is complaining because your view's type Graphomat.DijkstraWindow doesn't declare the same base type between the xaml and the .cs file. Your cs file likely says that it inherits from the Window type.
One way to transfer data between ViewModels is dependency injection. Consider the following:
public class FooView : Window
{
//require data from the parentview to the child view through dependency injection.
//very simplistic, might meet your needs. If you need a full view lifecycle, see MVVM frameworks like
//cliburn.micro
public FooView(INavigationData navigationData)
{
//do something with your data.
}
}
It is very common to use a base class for all the view models in you project. Consider that you are binding all your views to individual view models, it only makes sense that you would create a base implementation of INotifyPropertyChanged on a base class:
public class MainViewModel : BaseViewModel
{
}
public abstract class BaseViewModel : INotifyPropertyChanged
{
public object Model { get; set; }
#region PropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if(handler != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
#region Commands
public ICommand OpenFooWindowClicked
{
get
{
//implement your ICommand here... beyond the scope of the question.
}
}
#endregion
}
As far as the class problem is concerned, if you are following the typical MVVM naming convention, then it looks like you're trying to define your ViewModel in xaml. While that's not unheard of, you likely want to define your View in xaml.
Please do check out the SO question: MVVM: Tutorial from start to finish? The tutorials linked in that thread should get your head wrapped around the concepts vital to successful execution of the MVVM pattern.
I'm relatively new to MVVM and I'm trying to understand how INotifyPropertyChanged interface works and how to implement it in my models. The approach that I decided to take was to implement it in each of my Business Object classes. The problem with that approach is that when I bind my View to a property in a Base class the PropertyChanged event in that base class never gets initialized (is null) and therefore the View does not refresh the data for that element when my Model changes. I was able to reproduce the problem with the example below.
I have a Person Base class:
public class Person : INotifyPropertyChanged
{
#region INotifyProperty
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
public String Name
{
get
{
return _name;
}
set
{
_name = value;
RaisePropertyChanged("Name");
}
}
private String _name;
}
And I have an Employee class inheriting from my Person Base class:
public class Employee : Person,INotifyPropertyChanged
{
#region INotifyProperty
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
public String EmployeeID
{
get
{
return _employeeId;
}
set
{
_employeeId = value;
RaisePropertyChanged("EmployeeID");
}
}
private String _employeeId;
}
Here my View Model:
public class ViewModel : ViewModelBase<ViewModel>
{
private Employee _employee;
public ViewModel()
{
ChangeModelCommand = new RelayCommand(param=>this.ChangeModel() , param=>this.CanChangeModel);
Employee = new Employee()
{
Name = "BOB",EmployeeID = "1234"
};
}
public ICommand ChangeModelCommand { get; set; }
public Employee Employee
{
get
{
return _employee;
}
set
{
this._employee = value;
NotifyPropertyChanged(m=>m.Employee);
}
}
public void ChangeModel()
{
MessageBox.Show("CHANGING MODEL");
this.Employee.Name = "MIKE";
this.Employee.EmployeeID = "5678";
}
public bool CanChangeModel
{
get{ return true;}
}
}
And finally my View:
<Window.Resources>
<MVVM_NotificationTest:ViewModel x:Key="Model"></MVVM_NotificationTest:ViewModel>
</Window.Resources>
<Grid DataContext="{StaticResource Model}">
<StackPanel>
<Label Content="Employee Name"/>
<TextBox Text="{Binding Path=Employee.Name,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
<Label Content="Employee ID"/>
<TextBox Text="{Binding Path=Employee.EmployeeID,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="Change Model" Height="30" Width="100" Margin="5" Command="{Binding Path=ChangeModelCommand}"/>
</StackPanel>
</Grid>
In this example I initialize my Employee VM Property in the VM constructor and then I have a command to modify the EmployeeID (from Employee class) and Name (from Person Class). However, the only UI element in the View that gets updated is the EmployeeID and not the Name (I expected Bob to update to Mike). While debugging I found that PropertyChanged event was always null in my base class (Person). I also noticed that when I remove the whole #INotifyProperty region from my Employee class everything works fine since it is using the Base Type event and methods.The problem I have with that is that all my current model classes implement INotifyPropertyChanged explicitly. They all define a PropertyChanged event and implement the RaisePropertyChanged method, which obviously will impact my bindings in my MVVM application. Lastly, I want to clarify that I do not want wrap my Model properties in my ViewModel and rely on the VM INPC mechanism. I would like to use my Model INPC implementation already in place whithout having to conditionally remove the INPC implementations depending on whether I am inheriting or not from a base type.
In summary, I would like to know what's the best way to implement the INPC in my deeply hierarchical model so that inheritance doesn't break the PropertyEvent propagation as we saw in this example and so my independent classes can be self sufficient as well. Any ideas or suggestions will be greatly appreciated :)
Simply make RaisePropertyChanged protected and move it into the base class. Currently you will have a lot of duplication that is not necessary.
Something like this:
protected virtual void RaisePropertyChanged(string propertyName);
Many MVVM frameworks provide this for you. For example PRISM has a NotificationObject ViewModel base class.
You should only implement INPC once, you can use the same raising method in the subclasses.
I would also change the raise property changed method to use reflection instead of passing in hard coded strings. I see you did it in your view model but not in your models (where most of the errors tend to occur).