Collection of objects shared in different viewmodels - c#

in a Wpf application I've a collection of objects received via socket. Actually this collection is composed of a Dictionary<Int, IMyObject> And it's periodically filled/updated when an object is received on the socket.
I've 4 different ViewModels that gets a subset of this collection (I've a MyObjectHolder which is a static instance holding this item and I get a subset of it doing something as
public IList<MyObject> ListTypeOne
{
get
{
myList.Where(x => x.Type == myType)
}
}
Each time an item is inserted/updated, I send a NotifyOfPropertyChanged on all the Properties from TypeOne to TypeN.
Is there a better implementation that I don't see?
I've not heavily stress tested it but I don't know how it performs when I've a large number of objects in the collection.

I'm not sure if this is the best solution, but here's what I would do.
Create a static instance of your collection (You've done this already.)
Have a service, or something that will periodically update your collection (You've done this already)
Push notifications to all ViewModels to force them to refresh their bindings. (The tricky part)
Now, there are a few ways you can handle number 3. One of these ways is to use Caliburn Micro's Event Aggregator. You can find out about it here.
Essentially, what it does is allow your ViewModels to subscribe to an event. Your service, or whatever it is that's updating your collection will publish the event, and all subscribers will be called. Pretty cool, right?
This allows you to keep your ViewModels nice and decoupled, which is what MVVM likes.
Now, I'm not saying you have to cave in and install Caliburn Micro (I wouldn't blame you if you didn't). There's no reason why you can't implement your own event aggregator, heck, there's probably loads of tutorials and sample code you can find if you use the right search terms.
Personally, I'm not a massive fan of using a framework, it just feels dirty, but that's down to my personal preference.
But anyway, give it a try and see what works for you.

Related

Complex WPF UserControl in MVVM application

Having taken over maintenance of an existing WPF application, I was horrified to discover that two of the Views and ViewModels had large blocks of near-identical code. Obviously, I want to refactor this so they can both reuse a single block of functionality, but I'm not sure how best to go about it, architecturally.
The identikit code deals with processing UI data from a tab. However I split this, it is essential that the code in the other tabs (which is different in the two cases) has access to the properties and objects of the tab I need to split out.
To further complicate matters, the replicated code needs database access. We've got a repository object that handles this. Normally when creating new objects, I've been making them testable by passing a copy of the repository into the constructor. However, if I do that in this case I'll have two copies of the repository object - one in the ViewModel, one in the split out code - needing to handle the same data, which is going to cause concurrency issues.
My first thought was to make a UserControl for this, but the more I think about this, the more problematic the two issues above seem to be.
The other option I've considered is just to make a Helper class to do some of the identical processing. But that's only going to partially solve the problem as some identical UI code (raising property changed events, XAML, etc) is still going to be in both Views/ViewModels.
What's the best approach here? Is there a way I can get past the repository/access issues and make a UserControl? Or is than an alternative based on Interfaces or Inheritance I haven't considered?
EDIT - Code was asked for. It's a bit complex to give a comprehensive example, but here's a snippet from each VM:
public void CheckOrderHist(int months)
{
var endDate = DateTime.Today.AddMonths(months);
Dictionary<OrderHistory, bool> orders = new Dictionary<OrderHistory, bool>();
this.ordersToExclude.Clear();
foreach (var kvp in rep.OrderHistories.GetRecent(months))
{
if (kvp.Key.MailingDate >= endDate)
{
orders.Add(kvp.Key, true);
this.ordersToExclude.Add(((OrderHistory)kvp.Key).OrderID);
}
else
{
orders.Add(kvp.Key, false);
}
}
BuildOrderExclusionsOnCount(); //code in this is near-identical across VM's too
OrderHistoryMonths = Math.Abs(months); //OrderHistoryMonths is a property on the ViewModel
OnPropertyChanged("MajorityOrderBoolean");
}
And in the other VM:
private void CheckOrderHist(int months)
{
var endDate = DateTime.Today.AddMonths(-months);
ObservableCollection<Tuple<OrderHistory, bool>> orders = new ObservableCollection<Tuple<OrderHistory, bool>>();
this.ordersToExclude.Clear();
foreach (var tuple in rep.OrderHistories.GetRecent(-months))
{
if (tuple.Item1.MailingDate >= endDate)
{
orders.Add(new Tuple<OrderHistory,bool>(tuple.Item1, true));
this.ordersToExclude.Add(tuple.Item1.OrderID);
}
else
{
orders.Add(new Tuple<OrderHistory, bool>(tuple.Item1, false));
}
}
BuildOrderExclusionsOnCount(); //code in this is near-identical across VM's too
OrderHistoryMonths = months; //OrderHistoryMonths is a property on the ViewModel
OnPropertyChanged("OrderHistories");
OnPropertyChanged("GroupedOrders");
}
This illustrates the problem nicely - the function is essentially the same, but one uses a Dictionary and the other a Tuple (there's no good reason for this - they both need a Tuple really, for ease of ordering). And one arbitrarily takes a negative int parameter, and the other a positive.
Both contain different OnPropertyChanged events, and will use different copies of the repository object, making it hard to properly separate them using a Helper class. Yet putting it in a UserControl would isolate them from OrderHistoryMonths on the main ViewModel.
If I'm hearing the current comments right, the best solution here is to farm out the main ForEach loop to a helper class, and just put up with the rest of the duplication?
By all means, extract common logic where possible to a new 'helper' class that each ViewModel can construct; this is the standard pattern of re-use through composition. The code you've shown in your question is a good candidate for this kind of refactoring.
As far as boilerplate, though, it's a bit trickier. This is something that is difficult to address in general and must be examined on a case-by-case basis. There are various ways to simplify property changed notification, for instance (helper methods encapsulating property updates, AOP, etc.) but these are generally part of your MVVM framework and embraced application-wide. As far as XAML duplication, you can often use Styles, Data Templates and Value Converters to improve things, but again, it requires a careful analysis of your particular code base to identify the patterns that may merit this treatment. If you have more specific examples that you think are clear duplicates, but aren't sure how to refactor, those may make good questions.

How to "refresh" my ViewModels after database changes have been done in another ViewModel?

I'm currently writing a rather small desktop application using the MVVM Approach. It also utilizes Entity Framework 6 for the database access. Right now, my top-level ViewModel instantiates the 'smaller' ones, and passes them the DbContext I'm using. The smaller ViewModels I use correspond to UserControls sitting in a separate TabItem each. But if I change something in the database in one tab and switch the tab afterwards, the UI isn't keeping up, logically, since there is no OnPropertyChanged("SomeObservableCollection") Happening.
I thought about just "refreshing everything inside" when a TabItem becomes active, but on one hand, I don't know how to do this (it would basically be doing OnPropertyChanged(..) for every UI-relevant property, right?), and on the other hand, it does seem neither elegant nor 'correct'.
What should I do about this? And is using one global DbContext even good practice? I read about short-lived DbContext instances being better, but I also found the opposite statement regarding desktop applications...
How do you handle this scenario? It can't be that rare actually, can it? Thanks!
You have to look at using a Messenger (MvvMLight) or EventAggregator (Caliburn.Micro).
So when your context has changed you pass the message about it and update your SomeObservableCollection so OnPropertyChanged("SomeObservableCollection") will be raised.
Might Help . I have done this in small project . any better solutions are welcomed.
**Viewmodel 1 Where changes occurs**
//database call
string result = _dataService.Insert(data);
if(result=="Success")
{
//notify viewmodels using default messenger instance
MessengerInstance.Send(new NotificationMessage("notifycollection"));
}
Viewmodel 2 where we receive notification
public AssignTimeSlotViewModel(IDataService dataService)
{
// registering the notification
MessengerInstance.Register<NotificationMessage>(this, receiveNotification);
}
#region Messenger - receivers
private void receiveNotification(NotificationMessage msg)
{
if (msg.Notification == "notifycollection")
{
/// Call Database to keep collection updated.
// raise propety changed event if neccessary.
// Do Something
}
}
#endregion
Thats not an easy subject at all.
If you handle with a small amount of data and performance is not a problem for you, you could update your bindings every time the view gets loaded. Here you can see how to accomplish that.
A Problem if you do that is, that you have to do some extra logic for saving last selected items and reselect them after the view gets loaded.
Using a messenger could be an option too. But in my experience the messenger could make thinks messy if it is not implemented correctly. For example please dont use some magic strings as messeages.

Prism How to bind Module View to app wide property

I know this may seem silly but, what is the best way to bind to Application Wide properties using MVVM and Prism?
Problem: ConnectionStatus Status {...} (Implements INotifyPropertyChanged etc..)
How do I go about accessing this property in specific modules? I want to be able to use the Status property across different modules?
What is the best way of getting this data across different modules?
Solutions
Currently I am leaning towards having the following view model in the infrastructure project (shared across modules)
namespace Project.Infrastructure
{
public class AppViewModel
{
public ConnectionStatus Status {...}
}
}
And in IoC define AppViewModel as Singleton, and pass this in the constructor of ModuleViewModel
namespace Project.ModuleA
{
AppViewModel _appViewModel;
public class ModuleViewModel
{
public ModuleViewModele(AppViewModel appViewModel)
{
....
}
}
}
Question
Is this the best way to do this or is there a better way of doing this?
E.g.
Using EventAggregator and ConnectionStatusChangeEvent to subscribe and publish to changes in the connection? But in this case if someone subscribes to the event after it got pubblished (such as online) they will not get any starting value, so a IStatusService could be used at the start?
Using RegionContext and binding that to the Status property in the Shell? But this seems to defeat the purpose of Shell not knowing what the Modules use etc..
Or is there something that I have completely missed?
I have read the whole Prism(v4) documentation and just not 100% sure of which is the best way of implementing this.
I do not like ViewModels knowing about each other. I took the approach you listed in E.g #1 - that EventAggregator assists in moving information around for me. I do this currently with a database connection and settings, both used throughout viewmodels. I pass the model property around after it is created first time and when updated.
The ViewModel that creates the model subscribes to a Request and UpdateRequest, and publishes a Response. So a Request returns the current property, UpdateRequest will refresh or recreate as appropriate.
The Response is pushing to any listeners - such as a newly created ViewModel who has in its constructor a publish of the Request. This means that the constructor doesn't initialize everything, your going to have the Response listeners covering extra work, and sometimes you may need to wait for multiple responses to fully initialize.
You asked what is the best way... - I feel my answer is right, but do what is right for your project. I like strict MVVM, but it doesn't always have to be so rigid.

Managing prerequisites with MEF and Caliburn Micro

Lets say I have a component with a number of smaller components which check prerequisites before the first one will be initialized. They are not dependent on one another so I don't care about order and would like them to run simultaneously. I am using MEF and Caliburn.Micro for presentation.
I thought about this setup:
class Big
{
[ImportMany]
public IEnumerable<IBigPrerequisite> Prerequisites {get; set;}
public void Initialize(){...}
}
and
interface IBigPrerequisite
{
public bool IsBusy {...}
public bool Allow {...}
public void StartChecking();
}
Now what I would like to accomplish with this is that the classes implementing IBigPrerequisite can open up a window (for example "File X was not found - this could lead to errors. Continue?") - this should be possible.
But I would only want one window to be visible at a time. How would I accomplish that besides just going synchronously?
EDIT - since the question seemed too vague
I need these Actions to run specifically before Big will be activated. Let's say we switch up the activation logic to something like this:
Big big; //we got this through Importing somewhere in composition
var allow = true;
var count = 0;
if(!pre.Any()) //no prerequisites, show window immediately
windowManager.ShowWindow(big)
foreach(var pre in big.Prerequisities)
{
pre.PropertyChanged += (s, args) =>
{
if(args.PropertyName == "IsBusy" && !pre.IsBusy) // if a prerequisite finished it's check
{
allow = allow && pre.Allow; //if one prerequisite says nay we could just return, actually...
count++;
if(count == big.Prerequisites.Count() && allow)
windowManager.ShowWindow(big);
}
}
pre.StartChecking();
}
Now, I explicitly want the classes implementing IBigPrerequisite to be able to open a window, but in case all prerequisites are met (no user interaction required) no window should be showing. I do not wish to open up a window for every class here.
I am looking for a way to, say, give the IBigPrerequisite (which should probably be called IPrerequisiteViewModel anyways) a property like bool RequestsWindow {get;} and have the View only created when a) the viewmodel requests it and b) no other prerequisite window is open at the time.
Note: the code here is for illustration only as I am not sure how to implement this behaviour yet. I am not experienced with these frameworks (and concepts) so if this question seems silly please bear with me.
You are mixing concepts here.
Active view management in Caliburn.Micro is handled by the Conductor class. A Conductor-derived ViewModel can display a large number of Screen-derived ViewModels (or other Conductors). Available items are stored in the Items property.
You can find a much better description at "Screens, Conductors and Composition"
MEF has nothing to do with the Conductors and the composition mechanism, although it can be used to pass a list of items to a conductor. You can define an [ImportMany] constructor parameter or public property that receives the Screens to display during initializations and store them in the conductor's Items property.
Using a constructor parameter is more elegant, as you won't have to copy the items from your property's setter to the Items property.
Finally, you shouldn't display messages when creating your views and viewmodels. This is something that should be left for a later step, eg. during the Activate method. The Conductors and MEF get the parts together and build the UI. Executing actions and talking to the user should be done only after the composition step has finished.
I am going to answer this question myself detailing how I ended up solving this.
I made a LoaderViewModel : Conductor<PropertyChangedBase>.Collection.OneActive, IChild<Shell> and gave it a Queue<PropertyChangedBase>.
It has Show/HideWindow methods by traversing the Parent-Properties until it arrives at the Window-Level.
It has Queue and Dequeue methods. Queue is used when PropertyChanged is fired on a RequestsView-Property and calls Dequeue if there's either no ActiveItem or the ActiveItem is not marked as busy. Dequeue will activate a new item if there is one in the queue and then call ShowWindow, if there is no item it will call HideWindow instead.
The initial HideWindow is done in the ViewAttached-Event since if the window is hidden, CM seems to have some strange behaviour. Here, the parallel checking of the prerequisites is started and an event-handler registered similar to the one in the first post.
Sorry for being verbose, but the code has gotten a bit lengthy. If someone wants me to post it up write a comment.

Model view presenter, how to pass entities between view?

Edit : Accepted Chris Holmes response, but always ready to refactor if someone come up with a better way! Thanks!
Doing some winforms with MVP what is the best way to pass an entity to another view.
Let say I have a CustomerSearchView/Presenter, on doubleClick I want to show the CustomerEditView/Presenter. I don't want my view to know about the model, so I can't create a ctor that take an ICustomer in parameters.
my reflex would be,
CustomerSearchView create a new CustomerEditView, which create it's own presenter.
Then my CustomerSearchView would do something like :
var customerEditView = new CustomerEditView();
customerEditView.Presenter.Customer = this.Presenter.SelectedCustomer;
Other possible approach would be a CustomerDTO class, and make a CustomerEditView that accept one of those CustomerDTO, but I think it's a lot of work something simple.
Sorry for basic question but all example I can find never reach that point, and it's a brownfield project, and the approach used so far is giving me headache...
I don't know exactly how you are showing your views, so it's a bit difficult to give you specific advice here. This is how I've done this sort of thing before:
What we did was have the CustomerSearchViewPresenter fire an event like OpenCustomer(customerId). (That is assuming that your search view only has a few pieces of Customer data and the customerId would be one of them. If your search view has entire Customer objects listed then you could call OpenCustomer(customer). But I wouldn't build a search view and allow it to populate with entire objects... We keep our search views lightweight in terms of data.)
Somewhere else in the application is an event handler that listens for the OpenCustomer() event and performs the task of creating a new CustomerEditView w/ Presenter (and I'm going to defer to my IoC container do this stuff for me, so I don't have to use the "new" keyword anywhere). Once the view is created we can pass along the id (or customer object) to the new CustomerEditView and then show it.
This class that is responsible for listing the OpenCustomer() event and performs the creation of the CustomerEditView is typically some sort of Controller class in our app.
To further simplify this situation, I've done this another way: I create both the CustomerSearchView (& presenter) and CustomerEditView (& presenter) when the application or module starts up. When the CustomerSearchView needs to open a Customer for editing, the CustomerEditView becomes the responder to the OpenCustomer event and loads the data into itself, and knows how to show itself in whatever container it is supposed to do.
So there's multiple ways to do this.
How about:
//In CustomerSearchPresenter
var presenter = new CustomerEditPresenter();
var customerEditView = new CustomerEditView(presenter);
presenter.SetCustomer(customer);
//In CustomerEditPresenter
public void SetCustomer(customer)
{
View.Name = customer.Name;
View.Id = customer.Id;
...
}
In think your customer search view should just delegate to its presenter you need to have an action execute.
There are a couple of crucial insights to get a natural flow in any MVP code:
It's the presenter that drives the view, not the other way around.
Because of 1. the view need not know about the presenter's existence. Less dependencies usually means easier maintenance.
In C#, I find events being a great asset when decoupling presenters from views. More details in a previous answer: Model-View-Presenter in WinForms
I would look at MS Prism 4, and their nice Navigation interface. Also look at Silverlight and WCF Navigation. They are well done and handle things like prompting the user for confirmation from "dirty" forms, with cancellation.
I would look at the PageFunction() documentation in WCF as well, for how to "call" a page from another, and get back info.
Here's how it works (javascript, sorry):
User double-clicks customer on customer list:
CustomerList.onDblClick(customerId){
app.fireEvent('customerEditRequest', id)
}
...
app.onCustomerEditRequest(id){
this.mainRegion.requestNavigate('customers/edit', id);
}
If navigation to edit view was successful...
CustomerEditView.onNavigatedTo(context){
this.model.load(context.parameters.id));
}
CustomerEditView.onSaveButtonClick(){
this.model.save();
app.fireEvent('customerEdited', id);
}
...
app.onCustomerEdited(id){
app.mainRegion.requestNavigate('customerlist', id);
}
There are a few different ways you could do it:
send a callback function to the edit form, from the customer list. edit form will call it, and you do what you want when it's called.
have the edit form raise on "customerEdited" event that you listen to and react to (no app-wide bus)
use an application-wide Event Bus to manage the events centrally, shown.
I used to have my views communicate with their presenters, but have moved away from that. It doesn't conform to the original definition of a pattern (not a reason in itself for deviating just a contributing factor to exact those benefits). Views ideally should be kept as dumb and with as few dependencies as possible. View should communicate w/ Presenter (any "observers") via delegates/events/some "fire-and-forget" mechanism. As a matter of fact, I've introduced a controller into MVP specifically to intercept View events and either re-fire to presenter (rarely) to communite w/ Presenter, or to communicate with a system or Presenter-specific event bus - enabling me to change user action alerting mechanisms w/out touching the view. Have to be careful with an event bus though; pretty soon you start throwing all events in there, app gets chatty/bogged down in handling events, and events aren't the fastest things in .Net. Sunchronization is an added concern, esp if ur app need to have a more "conversational" interaction with your user.
Should bear in mind that although Presenter is usu view/process-specific, views (and view-models) can be reused; having the View in a containment/delegation relationship with the Presenter strongly couples View/limits its reuse. This could be reduced by some DI, but I find DI containers to be unnecessary complexity in most cases (since I have to know how to create objects anyway and how often do you change out an object for another semantically similar one after creating/testing it?). Concrete dependency goes nowhere except another layer/adds more obscurity/makes things more difficult to debug/trace. Been on a "simplicity" kick lately though, and mostly prefer to do my on Factory/object creations/ORM mappings for most apps, since there's usu a "1-to-1" btw db tables/entities and n need for the added complexity of a generic 3rd-party ORM tool that by taht generic context/needing to serve different apps has to make things harder than they need to be, even if u understand how they work (not the point).
Moreover, it's still quite feasible for View to observe Model in MVP (as in MVC), so I wouldn't be so quick to rule this out. I don't prefer to do this myself, but it' doesn't "break" the pattern. Matter of fact, I developed something similar to MVP about a decade ago because I didnt like the "circular loop" btw the MVC components (View knowing about Model); I preferred to have the cleaner separation btw View and Model that all these patterns (including MVC) professed, as well as a desire to keep View as dumb as possible (observing Model woujld mean View would need more intelligence to process Model changes). What I ended up doing was something like MVVM and strategy patter, where I used "substructures" of the model to pass in to the View, serving as "change notifiers". This kept everything view purpose-specific and flexible/reusable (tough combo).

Categories