Most applications today are web apps. And as such, most of the guides and examples are written for web apps. Where I work, we are making WPF application, and there is one thing I cannot figure out:
What sits in place of the controller, in a WPF application (using the MVVM pattern)?
To elaborate:
In web applications, the UI typically talks to a controller over HTTP. The controller then talks to services/repositories/etc (depending on the architecture).
I have yet to see a clear example of the communication between the UI and back-end in a WPF application.
The examples I have seen either uses a very tight coupling (typically in tutorials) or an HTTP backend (which basically makes it a web app). So if you want the advantages of the loose coupling from the HTTP architecture, but it all has to be in a single app, how do I achieve this?
In MVVM, the view binds to a view model class. The view model class may be injected with models or services.
Let's for example say that you want to display a list of some items that you receive from some REST API.
In the view, you would define an ItemsControl that binds to a collection property of the view model:
<ItemsControl ItemsSource="{Binding Items}" />
You may also have a button that binds to a command that is responsible for fetching the data:
The view model may be implemented something like this:
public class ViewModel : INotifyPropertyChanged
{
private readonly IRestService _service;
public ViewModel(IRestService service)
{
_service = service;
GetDataCommand = new RelayCommand(GetData);
}
private IEnumerable<string> _items;
public IEnumerable<string> Items
{
get { return _items; }
set { _items = value; NotifyPropertyChanged(nameof(Items)); }
}
public ICommand GetDataCommand { get; }
private async void GetData(object _)
{
var data = await Task.Run(() => _service.GetData());
Items = data;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
RelayCommand is a custom implementation of the ICommand interface that executes an Action<object>. Most implementations you'll find online doesn't support async/await, i.e. you cannot await the command. That's why GetData returns void instead of Task in the sample above. The service method is called on a background thread to keep the UI responsive.
Related
is there any way to handle events from mediator inside the ViewModel. If VM implemetns interface INotification it must be singleton or creates/destroy based on mediators rules.
Or how to solve this issue. For example: when i create new Customer(), domain model generates event CustomerCreatedEvent() and i want to update counter on my mobile app UI.
I know there are events in c#, where can i subscribe/unsubscribe VM but it is "diffrend kind" of event.
Thanks for advice.
Update 01
I found a solution when VM and Notification are independet units.
public class CustomerCreatedHandler : INotification<CustomerCreatedEvent>
{
private readonly CustomerPageViewModel _viewModel;
public CustomerCreatedHandler(CustomerPageViewModel viewModel)
{
_viewModel = viewModel;
}
public Task Handle(CustomerCreatedEvent customerCreatedEvent)
{
// set property of VM
_viewModel.Count = customerCreatedEvent.Count;
return Task.CompletedTask;
}
}
It should be working. VM is singleton and Notification handler can be destroyed after work is finished.
Could I get help with a little issue I am encountering regarding splitting projects into different tiers. In my ViewModel logic I have code where I create a new instance of a window when a button is clicked (I use ICommand interface for that)
The problem is however, that this requires my View folder which is in the presentation layer, I can’t reach it as my presentation layer is dependent on my ViewModel in the logic layer.
I would just move the code that deals with the creation of the pages to the view code behind but I also pass the current instance of a viewmodel as a parameter for that new window being created (for eventhandling purposes).
Any help is much appreciated! Thanks.
A view model shouldn't create instances of windows. What you could do is to inject your view model with a service that is responsible for creating windows, e.g.:
public class MainWindowViewModel
{
private readonly IWindowService _windowService;
public MainWindowViewModel(IWindowService windowService)
{
_windowService = windowService;
CreateWindowCommand = new DelegateCommand(() =>
{
_windowService.CreateWindow(new SomeViewModel());
});
}
public ICommand CreateWindowCommand { get; }
}
Define the IWindowService interface in the view model project and the concrete implementation of it in the view/presentation project:
public class WindowService : IWindowService
{
public void CreateWindow(SomeViewModel vm)
{
Window win = new Window();
win.DataContext = vm;
win.Show();
}
}
I'm building WPF application which downloads links to articles from website and displays them in a grid.
Summary of the problem:
The problem I'm hitting is that I have downloading logic in the model which does not have access to the ObservableCollection Articles (which is defined in my ViewModel and bound to DataGrid).
How should I access this collection to update it as I'm downloading new articles (so that data grid will keep adding new ones one by one)?
Details:
Here's overview of my app which implements MVVM pattern (I cut some of the not important parts to keep it easy to read):
DisplayWindow.xaml
Is binding source to PresenterViewModel.cs and ItemSource is set to Articles which is ObservableCollection (see below)
<Grid DataContext="{Binding Source={StaticResource presenterViewModel}}">
<DataGrid ItemsSource="{Binding Articles}">
<DataGrid.Columns>
<DataGridTextColumn Header="Url" Binding="{Binding Url}"/>
...
</DataGrid.Columns>
</DataGrid>
</Grid>
it also has button which triggers downloading of the article links via
<Button Content="Download" Command="{Binding DownloadArticles, Mode=OneWay}"/>
DownloadArticles method is part of PresenterViewModel and returns instance of DownloadCommand which implements interface ICommand.
PresenterViewModel.cs
contains ObservableCollection<Article> Articles
private ObservableCollection<Article> articles;
public ObservableCollection<Article> Articles
{
get { return articles; }
set
{
articles = value;
RaisePropertyChangedEvent("Articles");
}
}
public ICommand DownloadArticles
{
get
{
return downloadCommand;
}
}
DownloadCommand contains reference to PresenterViewModel and in Execute method calls it's method DownloadArticles which calls DownloadArticles in the Article model itself.
in DownloadCommand.cs:
public void Execute(object parameter)
{
presenterViewModel.DownloadArticles();
}
Solutions I was considering:
Now, the problem is I'm in DownloadArticles method in Article.cs model and need to update Articles ObservableCollection which is part of PresenterViewModel...
how do I do that? The DownloadArticles method runs downloading logic in a separate thread.
Should I pass the reference to PresenterViewModel to the DownloadArticles method of the model?
This seems like an easy way to do it, however it just doesn't feel right - I don't think model should be coupled to the ViewModel and in this case one of the methods would accept PresenterViewModel object.
Another option would be having downloading logic directly in PresenterViewModel, but that doesn't feels right either as I'd prefer my ViewModel to be lightweight and not containing any logic.
What would be the best solution in this case? If you think my architecture is fundamentally wrong, please let me know what would be the best way of structuring it in this case.
I really appreciate your advices on this!
Now, the problem is I'm in DownloadArticles method in Article.cs model
and need to update Articles ObservableCollection which is part of
PresenterViewModel... how do I do that?
What you need to do is to separate your data access logic into another layer and inject it into your ViewModel as a dependency. By doing so you won't couple any of data access logic to your ViewModel because its task should be only to expose Model to the View and respond to user input.
Suppose that your model looks something like this:
public class Article : INotifyPropertyChanged
{
private string _url;
public string Url
{
get
{
return _url;
}
set
{
_url = value;
PropertyChanged("Url");
}
}
}
All the data access logic goes to a separate layer which I will call DataService. Its job is to access the external resource and download articles:
public interface IDataService
{
IList<Article> DownloadArticles();
}
public class DataService : IDataService
{
public IList<Article> DownloadArticles()
{
var articles = new List<Article>();
// you actually download articles here
return articles;
}
}
The data access layer is then injected in your ViewModel
public class PresenterViewModel : BaseViewModel
{
private readonly IDataService _dataService;
public PresenterViewModel(IDataService dataService)
{
_dataService = dataService;
}
}
Finally when user requests to download articles by triggering DownloadCommand you delegate this job to your service and wait for result which then will be exposed to your View by ObservableCollection<Article> Articles property of your ViewModel:
// your DownlodArticles command's execute method
public void Execute(object parameter)
{
var articles = _dataService.DownloadArticles();
Articles = new ObservableCollection(articles);
}
If for some reason you don't want to use dependency injection, you can implement your DataService as singleton and call it from your ViewModel:
public class DataService
{
private static DataService _instance;
public static DataService Instance
{
get
{
if (_instance == null)
{
_instance = new DataService();
}
return _instance;
}
}
public IList<Articles> DownloadArticles()
{
// ...
}
}
public void Execute(object parameter)
{
var articles = _DataService.Instance.DownloadArticles();
Articles = new ObservableCollection(articles);
}
UPDATE:
Your data service GetArticles method:
public Task DownloadArticles(ICollection<Article> articles)
{
// clear the collection if neccessary
articles.Clear();
return Task.Run(() =>
{
// load articles one by one and add them to the collection
}
}
Your ViewModel command execute method:
private async void Execute(object parameter)
{
await _dataService.LoadArticles(Articles);
}
I've following architecture:
desktop application, .Net 4.5, C#, WPF, MVVM Light, Messenger, IoC - ViewModel locator, so ViewModels doen't know anyhing about Views.
I have main view with data grid of some elements, and I want to display details of each individual element in new/child windows after double click on data grid.
I've bind event double click on main view to main view model. From this event handler in main view model, message is sent via Messanger.
New view (new/child window) is created in main view via delegate of also double click.
New/child window is a view which locate his view model and this view model register to the specific message in his constructor.
The problem is that new/child window (new view, and view model so on) is created too late, because message is already sent when new view model register for it.
Do you know maybe some patterns for such architecture. Any ideas will be appreciated.
It would help to know exactly what you try to do.
If your problem is just to display a detailed Window when double click on a row, I would say: create only one childWindow at start, and play with its visbility when required.
If you really need a new window each time, you could create it from your viewModel with an injected service for example.
In any case, you never has to create your window from main view! Either you create one window at start, either you dynamically create it from view model.
You cannot hope to create it from view and send the message in your view model.
Edit about the injected service, you could use something like that:
public interface IWindowService
{
void Open<TWindow>(ViewModelBase viewModel)
where TWindow : Window;
}
public class WindowService : IWindowService
{
private readonly IUIDispatcher _dispatcher;
public WindowService(IUIDispatcher dispatcher)
{
_dispatcher = dispatcher;
}
public void Open<TWindow>(ViewModelBase viewModel)
where TWindow : Window
{
_dispatcher.Run(() => OpenThreadSafe<TWindow>(viewModel));
}
private static void OpenThreadSafe<TWindow>(ViewModelBase viewModel) where TWindow : Window
{
var view = (TWindow) Activator.CreateInstance(typeof(TWindow), viewModel);
view.Show();
}
}
public class UIDispatcher : IUIDispatcher
{
public void Run(Action action)
{
var dispatcher = DispatcherHelper.UIDispatcher;
if (dispatcher == null)
{
action();
return;
}
DispatcherHelper.CheckBeginInvokeOnUI(action);
}
Note this DispatcherHelper come from MVVMlight, but you could erplace it easily.
Hope it helps.
The problem is that the ViewModel Locator creates the viewmodel instance only when it is needed (lazy loading).
just configure the ViewModelLocator to instantiate the viewmodel eager instead of lazy. This is done by passing the parameter "true" to the IoC Container.
Sample:
namespace Administration.ViewModel
{
public class ViewModelLocator
{
public ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
//Eager Loading
SimpleIoc.Default.Register<UserManagementViewModel>(true);
//Lazy Loading
SimpleIoc.Default.Register<InformationManagementViewModel>();
}
public UserManagementViewModel UserManagementViewModel
{
get
{
return ServiceLocator.Current.GetInstance<UserManagementViewModel>();
}
}
public InformationManagementViewModel InformationManagementViewModel
{
get
{
return ServiceLocator.Current.GetInstance<InformationManagementViewModel>();
}
}
public static void Cleanup()
{
SimpleIoc.Default.Unregister<UserManagementViewModel>();
SimpleIoc.Default.Unregister<InformationManagementViewModel>();
}
}
}
i am familier with mvc,mvp or mvvm patter. so i was searching google for implementing good design patter for win form apps. i found lots of article.few guys said mvc is good and few guys said mvp is perfect for win apps. i found a very small code which implement mvp in win apps. i go through the code and found that developer has to write lot of extra code to bind treeview or any control.
the code as follows
public interface IYourView
{
void BindTree(Model model);
}
public class YourView : System.Windows.Forms, IYourView
{
private Presenter presenter;
public YourView()
{
presenter = new YourPresenter(this);
}
public override OnLoad()
{
presenter.OnLoad();
}
public void BindTree(Model model)
{
// Binding logic goes here....
}
}
public class YourPresenter
{
private IYourView view;
public YourPresenter(IYourView view)
{
this.view = view;
}
public void OnLoad()
{
// Get data from service.... or whatever soruce
Model model = service.GetData(...);
view.BindTree(model);
}
}
please someone go through the code and help me to understand the flow because how code should be written in mvp patter that i dont know. thanks.
This code is already using the MVP pattern.
It declares an interface IYourView and a concrete class YourView that implements System.Windows.Form and this new interface. Essentially what that is doing is creating a new form with the added requirement that it also implements the BindTree() method defined in IYourView.
The YourView class (form) then has a dependency of YourPresenter in order to hook up the OnLoad event with the presenter, although I'd do it where the presenter subscribes to the form's OnLoad event instead.
The presenter YourPresenter takes as a dependency an instance of YourView and it can then use that instance in the rest of its logic.
Now, to use that you would follow a process similar to this:
Create a new instance of YourView (which then in turn creates the presenter)
Implement logic in the presenter (ie- create GetModel()) to create the Model that you want to bind to the tree
Call view.BindTree(model) in the presenter where model is what you just created in the previous step
So, create an instance of your view:
IYourView newView = new YourView();
Then in your presenter class:
Model model = GetModel();
newView.BindTree(model);