WPF mvvm navigation another way - c#

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}" />

Related

How to pass Events from Model to ViewModel in WPF/C#?

In my class I registered an Event from an external DLL, that will be raised when there are changes on variables from the external code.
public class Model
{
....
public void Connect
{
....
client.OnNotification += (s, e) =>
{
this.OnNotification(s,e);
}
}
}
And I have a ViewModel in which I want get notified when this event is raised in class Model.
public class ViewModel
{
...
// call method when Event in class Model is raised
public void DoSomething()
{
}
}
Any Ideas for a clean and easy way to do that?
Thank you.
Solution 1:
Pass in the client to the viewmodel's constructor and let the viewmodel subscribe to OnNotification() itself (pass in an interface if available)
Solution 2:
Make also the model implement INotifyPropertyChanged if you're using MVVM; pass in the interface into the viewmodel's constructor and subscribe to PropertyChanged.
If you're not using MVVM, you can use the same methodology by adding a custom ClientNotification event to the model, pass in the entire model into the viewmodels constructor, and subscribe to the event.
Solution 3:
Use a messaging system (aka message bus) such as Prism's Event Aggregator class or MVVM Light's Messenger class, or write your own.
EDIT: Here's an example using MVVM Light: (note: coding from memory, not tested)
Add a using reference to GalaSoft.MvvmLight.Messaging;
Create a small message class containing the properties you need. You can inherit from MVVM Light's MessageBase class if you want but its not necessary.
public class ClientNotificationMessage : MessageBase
{
public string SomeProperty { get; set;}
public int AnotherProperty { get; set;}
}
In you model's event handler, you send a message by:
client.OnNotification += (s, e) =>
{
var msg = new ClientNotificationMessage() { ... };
Messenger.Default.Send<ClientNotificationMessage>(msg);
}
In the viewmodel constructor, register to receive messages by:
Messenger.Default.Register<ClientNotificationMessage>(this, msg =>
{
// handle incoming ClientNotificationMessage
// if (msg.SomeProperty != ) ...
});
I'm sure there are other additional solutions that other ppl can add.
The solution is basic OOP design and it is not related to MVVM.
In C# you just don't pass events. You subscribe to events. Whenever something interesting happens in Model, fire en event. You can than subscribe to that event in ViewModel for example.
public class Model
{
public event EventHandler SomethingHappened; // e.g. you notification
}
public class ViewModel
{
public ViewModel(Model model)
{
model.SomethingHappend += SomethingHappend;
}
void Model_SomethingHappend(object sender, EventArgs e)
{
DoSomething();
}
void CleanUp()
{
/*
In order to prevent memoryleak:
If you subscribe to event of an object you have not created in this class
(Model.SomethingHappend in this case), you should also unsubscribe.
Otherwise model instance will keep reference to ViewModel instance.
*/
model.SomethingHappend -= SomethingHappend;
}
}
In your case, the event could be named NotificationRecieved instead of SomethingHappend
You should subscribe to the event directly in the object you want to "react" to the event.
The viewModel in this case. Define an EventHandler there and subscribe

Events, methods using these events and inheritance

I am creating the program-side architecture of a software developped in WPF, I designed the architecture as being compliant with the MVVM pattern.
For many sakes (design, coherence, reusability, maintainability, scalability, etc) I created the class BaseViewModel implementing the interface INotifyPropertyChanged and some other methods:
public class BaseViewModel: INotifyPropertyChanged
{
private PropertyChangedEventHandler property_changed;
public event PropertyChangedEventHandler PropertyChanged
{
add { property_changed += value; }
remove { property_changed -= value; }
}
//Here several methods using PropertyChanged and easing the usage of ViewModels
public BaseViewModel() { }
}
The above-defined class BaseViewModel is used as a base class for all the other ViewModels of the application (or, at least, is meant to be so), for example:
public class SampleViewModel : BaseViewModel
{
//private PropertyChangedEventHandler property_changed;
//public event PropertyChangedEventHandler PropertyChanged
//{
// add { property_changed += value; }
// remove { property_changed -= value; }
//}
public String Name
{
get { return name; }
set
{
if(value != name)
{
name = value;
var handler = PropertyChanged;
if(handler != null)
{
handler(this, new PropertyChangedEventArgs("Name"));
}
}
}
}
private String name = "";
public SampleViewModel ()
: base() { }
}
I use the class SampleViewModel as the DataContext of SampleUserControl which bares a DependencyProperty:
public partial class SampleUserControl : UserControl
{
#region ViewModel
public SampleViewModel ViewModel
{
get { return view_model; }
}
private SampleViewModel view_model = new SampleViewModel();
#endregion
#region DependencyProperty
public String Text
{
get { return (String)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(String), typeof(SampleUserControl),
new FrameworkPropertyMetadata(String.Empty, FrameworkPropertyMetadataOptions.AffectsRender,
new PropertyChangedCallback(TextPropertyChangedCallback)));
private static void TextPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
SampleUserControl sender = d as SampleUserControl;
if (sender != null)
{
sender.ViewModel.Name = (String)e.NewValue;
}
}
#endregion
public SampleUserControl()
{
InitializeComponent();
LayoutRoot.DataContext = ViewModel;
ViewModel.PropertyChanged += new PropertyChangedEventHandler(ViewModel_PropertyChanged);
}
void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
SampleViewModel viewmodel = sender as SampleViewModel;
if (viewmodel != null)
{
switch (e.PropertyName)
{
case "Name":
SetValue(TextProperty, viewmodel.Name);
break;
default:
break;
}
}
}
}
To sum up, the data relative to SampleUserControl are contained at three locations : the instance of SampleViewModel, within TextProperty and within the property Text of a TextBox in the xaml part of SampleUserControl(this property Text is twoway-bound through Binding with the field Name of ViewModel).
To synchronize the three values, I added the methods TextPropertyChangedCallback and ViewModel_PropertyChanged which update the fields which need to be updated.
The above code works and the three above-mentionned locations are kept up-to-date, events fire and so on, things are fine when SampleUsercontrol is consumed with data-binding.
But SampleViewModel fires the event BaseViewModel.PropertyChanged, and since BaseViewModel is meant to be extensively used, I would like each ViewModel to have its own event PropertyChanged, at least in order to avoid overlapping events.
So I uncomment the code of SampleViewModel thus redefining the event PropertyChanged but it breaks down the synchronization between the field Name of the instance of SampleViewModel and the property TextProperty of SampleUserControl.
Am I making some mistakes on the conception side?
Do you have any guidance for me?
What is the best economic way of defining a different event PropertyChanged for each ViewModel inheriting from BaseViewModel while still using the general-purpose methods defined within that base class (such methods use PropertyChanged)?
(I would like to avoid having heavy pieces of code to copy-paste.)
I know that it is more about optimization, but such optimizations can make a difference between a slow software and a fast one. I am at the stage of code-factoring, so I fancy nicely-shaped, elegant and factorized code.
End of the day happening, I may miss some obvious solutions.
Thanks in advance for any clue,
Julien
TL;DR: Basically, I would double-check that you are doing your DC/DP on that user control correctly, and toss out any concept of multiple definitions of PropertyChanged
In detail:
You defined PropertyChanged in the base class, which is great. There is no reason to ever redefine it anywhere else. Really, you are just asking for trouble by doing this.
Related to that, you should really just make a method to do the event invocation rather than doing the whole handler bit in the settter. Insta-reduction of copy paste.
The fact that you are having to use TextPropertyChanged is a huge red flag here. Which relates to the real problem, that you are probably abusing your dependency property. DPs are used to allow parent controls to bind to a property of your user control. You typically won't use them in conjunction with a data context internal to the control because, as you have seen, keeping them in sync is a nightmare.
In general, user controls should only have their own data context if they are set up to stand apart from any other control (ie, a sub-view). If they are just a fancy control, then giving them a view model rarely gets you anything.

WPF binding between ViewModel and Model

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.

Use elements from existing Class in a New Window

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.

ObservableCollection along with a repository

I am having trouble with grasping the concept of a ObservableCollection inside MVVM. For start I would like to point out that I am doing this in a Windows 8/Metro App, not WPF or Silverlight.
According to microsoft documentation, this collection has the following usefulness:
"Represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed." From what I understand this helps you a lot when binding is involved. On the net I found a lot of simple examples, by creating a ObservableCollection on runtime and then working on it, but I didn't find out what is the proper way of using this collection with a repository.
Let' say I have the following repository interface that is an implementation for a ORM database backend, or a raw ADO.NET implementation
public interface IRepository<T>
{
ObservableCollection<T> GetAll();
void Create();
void Update();
void Delete();
T GetByKey(object key);
}
and a simple ViewModel that use the repository as a model
public class ViewModel
{
private ObservableCollection<Dummy> _obsListDummy;
private RelayCommand _addCommand,_deleteCommand,_updateCommand;
private IRepository<Dummy> _repositoryDummy;
public class ViewModel()
{
_repositoryDummy=Factory.GetRepository<Dummy>();
}
public ObservableCollection<Dummy> ObsListDummy
{
get
{
return _repositoryDummy.GetAll();
}
}
public RelayCommand AddCommand
{
get
{
if (_addCommand == null)
{
_addCommand = new RelayCommand(p => DoAdd();
//DoAdd method shows a popup for input dummy and then closes;
);
}
return _myCommand;
}
}
........
}
My view would be a simple XAML with a grid, also Dummy object has INotifyPropertyChanged implemented.
Right now with this implementation after adding or updating or deleting, the ObservableCollection isn't refreshing, I know I could have put IEnumerable instead, but I dont'see an elegant solution of how would make repository to sync with the ObservableCollection that is in the model, other than subscrbing to CollectionChanged and there you treat all the states, but to it seems that I would repeat myself along with the logic that I do in the repository. And to make matters even worse, let's say I would like to get some push notification from my repository, towards the ObservableCollection.
I hope I was understand about my problem.
Thanks in advance.
You should implement INotifyPropertyChanged on your ViewModel and your ObsListDummy property should inform the ViewModel about changes applied to the collection. So it should look like this:
public class ViewModel: INotifyPropertyChanged
{
// Declare the event
public event PropertyChangedEventHandler PropertyChanged;
// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
private ObservableCollection<Dummy> _dummyCollection;
public ObservableCollection<Dummy> DummyCollection
{
get { return _dummyCollection; }
set
{
// Set the value and then inform the ViewModel about change with OnPropertyChanged
_dummyCollection = value;
OnPropertyChanged("DummyCollection");
}
}
}
This whole INotifyPropertyChanged interface and implementation includes some dirty work like declaring event and creating a helper method to raise the event so I would suggest you to use some libraries for that like MVVM Light.
You should use a member of type ObservableCollection to store your Dummy ViewModels. In your Initialize method you read the dummies from the repository, create Dummy ViewModels and put those in the ObservableCollection. Now your view will get updated, when you use Binding to ObsListDummy (and add / remove from that collection, also note that Binding only works with public properties).
Right now, you just have a new ObservableCollection on each read, no events involved, so your View will never know about a change.
Further your ViewModel shall implement INotifyPropertyChanged.

Categories

Resources