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);
}
Related
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.
I have my MainView and an associated MainViewViewModel which are linked by ViewModelLocator.
Within MainViewViewModel there is a command which should trigger a new Window to open which has it's own View and ViewModel (NewView and NewViewViewModel).
In a lot of the examples I've seen it is suggested to use Mvvmlight's Messenger to do something like this:
public class MainViewViewModel
{
private void OpenNewWindow()
{
Messenger.Default.Send(new NotificationMessage("NewView"));
}
}
And then register the NewViewViewModel and handle the message like this:
public class NewViewViewModel
{
public NewViewViewModel()
{
Messenger.Default.Register<NotificationMessage>(this, NotificationMessageReceived);
}
private void NotificationMessageReceived(NotificationMessage obj)
{
if (obj.Notification == "NewView")
{
NewView view = new NewView();
view.Show();
}
}
}
However, this doesn't work because the NewViewViewModel isn't yet instantiated (so isn't registered with Messenger). Additionally, this doesn't fit with MVVM because NewViewViewModel is responsible for creating NewView.
What is the correct way to achieve a simple command which instantiates and opens a new View and ViewModel pair which are linked via ViewModelLocator and setting of DataContext="{Binding NewView, Source={StaticResource Locator}}" in NewView.xml?
Use a window service:
MVVM show new window from VM when seperated projects
You may either inject the view model to with an IWindowService implementation or use a static WindowService class:
public static class WindowService
{
public static void OpenWindow()
{
NewView view = new NewView();
view.Show();
}
}
Dependency injection is obviously preferable for being able to unit test the view model(s) and switch implementations of IWindowService at runtime.
I am not sure if I am quite catching the idea behind the MVVM design specially regarding passing the data from viewmodel -> view and vice versa.
So for example let's say I have a model that has those properties (As simple as possible) :
public class User{
public string username {get; set;}
public string password {get; set;}
}
and here's my ViewModel that has a login method:
// dependency injection
private readonly Context _context;
public constructor(Context context)
{
_context = context;
}
// my method to verify login:
public async Task<bool> login(string username, string password)
{
// fetching data
var user = await _context.Users.FirstOrDefaultAsync(p => p.username== username&& p.Password == password);
return (user != null);
}
so my question is: how should I deliver these methods to the view class?
I usually just do this inside the button_click():
Var viewmodel = new Viewmodels.User();
login_state = viewmodel.login(email, password);
However I just feel this isn't the right way as it'd make the design very tightly coupled. How should I implement it?
First make sure your viewmodel is set as DataContext or it is accessible from it.
public SomeView(SomeViewModel vm)
{
DataContext = vm;
InitializeComponent();
}
Now your viewmodel is accessible from XAML:
<Button Command="{Binding LoginCommand}">
Create command in viewmodel:
class SomeViewModel
{
private readonly Context _context;
public SomeViewModel(Context context)
{
_context = context;
}
public ICommand LoginCommand => new RelayCommand(LoginAndStuff);
private void LoginAndStuff(object param)
{
// do stuff here
}
}
How to RelayCommand => this link
You have to use Bindings for displaying your user.
For calling actions from the view you can use Commands. They can be bound in your ViewModel. (Why RelayCommand)
Try to think about testing your code. If you are using your viewobjects in the testcode you will face a lot of problems. MVVM helps you to seperate your view completely. If you strictly seperate vm and view you will have an easy life for unittesting.
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 working ona WPF application that has a toolbar/menu that will have the use for several custom commands. Probably around 15-20. I have seen documentation on how to create custom commands, but none of them necessarily apply to what I am trying to do.
I am using a controller to handle the business logic in my application, and I am trying to keep my view from doing any logic at all.
What I would like to do is create a directory in my project that holds the custom command classes so that I can decouple them from the controller and the view, but I would still like them to be called from the view such as a normal commmand is.
I have also seen the use of a DelegateCommand class, but am not quite sure if that is the direction I want to head in.
I would like to be able to have an arbitrary custom command class such as the following
public CustomCommand: ICommandd
{
public bool CanExecute(object parameter)
{
//arbitrary logic
}
public void Execute(object parameter)
{
}
}
The idea is that I would have 10-20 of these, and I want to keep them separate from everything else, and have them be called when needed.
I know that there is a way I can separate my custom commands, but am not quite sure.
I am new to using commands, so I still am trying to get a hold of the concept.
thanks,
The concept is you bind a command to a button and the command drives two properties of this button: "on click" and "enabled" resulting in the interface you posted.
The main reason you want to do commanding is to be able to bind button clicks to actions in your view model.
If you create one custom command which takes an action as constructor parameter you can wire methods from your view model directly to your command.
public class RelayCommand: ICommandd
{
Action action;
Func<bool> canExecute;
public RelayCommand(Action action) : this(action, () => true) {}
public RelayCommand(Action action, Func<bool> canExecute)
{
this.action = action;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return canExecute();
}
public void Execute(object parameter)
{
action();
}
}
Usage in your view model would be
public RelayCommand SaveCommand { get; set; }
SaveCommand = new RelayCommand(OnSave);
public void Save()
{
// save logic...
}
If you want to wire CanExecute, too you can use the second ctor and provide a CanSave Method.
public RelayCommand SaveCommand { get; set; }
SaveCommand = new RelayCommand(OnSave, CanSave);
public void Save()
{
// save logic...
}
public bool CanSave()
{
return // ...
}
As you may noticed I dropped the command parameter in my implementation. This will be sufficient in most cases and saves you extra parameters in your handler methods. For the 10% left I implemented a RelayCommand<T> which takes an Action instead of Action and changes the Execute method to
public void Execute(object parameter)
{
action((T)parameter);
}
which requires a parameterized handler
SaveCommand = new RelayCommand<SomeType>(OnSave);
public void Save(SomeType toSave)
{
// save logic using parameter
}
This saves you all casting issues you encounter when using object variables and keeps your view models type safe.
Use RelayCommand, it doesn't require you to create a class for each command, you simply add both methods into the constructor as lambda expressions/delegates.
I use it all around my projects, it is a real time-saver.
I have ended up answering my own question through the following post,
http://www.codeproject.com/KB/WPF/CentralizingWPFCommands.aspx?display=Print