I am currently working on my first backbone.js app. The concept of events is quite familiar to me but I am in question if I should use a central event dispatcher or not. Generally I see these two approaches:
Directly wire event publishers and event receivers together. (I started with this approach.)
Use an event bus and wire publishers and receivers to this bus.
Is it favorable to use an event bus in terms of e. g. long time maintainability of the app and traceability of events?
Whether to use a central event bus or to directly wire events together should be assessed on a case by case basis. Depending on the circumstance, there are times where you will favour one over the other.
I will use a central event bus whenever I can due to the fact that the publisher and receiver are not tightly coupled. In my opinion, this makes your code more maintainable and flexible.
I find central event buses very useful when you have:
A single event publishing instance and numerous event receivers.
Many event publishing instances and a single event receiver
The benefits of the central event bus in the above cases becomes even more apparent when the event receiver instances or publisher instances are dynamic and thus being created and destroyed over the life of the Application. As an example, consider the following single page app:
The single page app must stretch to fit the browser window width.
The single page app is primarily a tabbed set of views, however the number of tabs is dynamic as they are created and destroyed by users
The contents of the tabs are all different with the exception that they have a main area of content that must be stretched to fit the available width after accounting for the width of other sibling elements
Resizing needs to occur at many different points due to the contents of tabs continually changing.
In the above case, regardless of the specific scenario, the central bus model works really well. A code example would be:
Application Level
var App = {
init: function () {
// Event to resize width of element to remaining width
App.Events.on("fitRemainingWidth:app", this.fitRemainingWidth);
},
// event pub sub for the app.
Events: _.extend({}, Backbone.Events),
// function that expands an element, or elements to the remaining width in the window.
fitRemainingWidth: function(targetEls) {
var els = $(targetEls);
_.each(els, function (e) {
var el = $(e);
var cont = el.parent();
newWidth = cont.innerWidth();
otherElements = cont.children().not(el);
otherElementsWidth = 0;
otherElements.each(function () {
otherElementsWidth += $(this).outerWidth();
});
el.width(newWidth - otherElementsWidth);
});
}
}
In your views
Whenever a something needs to be resized, you trigger the event, for example:
App.Events.trigger("fitRemainingWidth:app", this.$(".main-content"), this);
As you can see, this is really useful, because the fitRemainingWidth function can apply to anything at all and you never know which views may want to use it in the future or when it will need to be called.
So I guess I should move onto when I find not using a central event bus preferable. I am sure there are other cases, however the main one for me is when the receiver needs to be wired to a specific instance of the publisher. For example, if we continue on with the tabbed application example, lets say that the contents of each tab is a split view of a particular person's mailbox. In each split view is a list pane showing a collection of emails and a reading pane showing the content of the currently selected message in the list.
In this case we want to trigger a "selected:email" event when an email is clicked and have the appropriate reading pane bound to it so it can display the email's contents.
Say we have ten mailbox tabs open, each with their own email list pane and reading pane. That makes ten email lists and ten reading panes. If I was to use a central event bus for this scenario when I triggered a "selected:email" from one of the lists, the reading panes that are receiving these events from the event bus would need to have their own smarts to try and work out whether the selected email was something they need to display or not. It's not really desirable for each reading pane to try and work this out and there is uneccesary logic involved. Its far better for a reading pane to just receive a "selected:email" event with the email object as it's payload and just display it.
So in this scenario, its better to to make each mailbox tab view responsible for wiring the events of it's specific child view instances and their models and collections. In our example this means a specific instance of an email list view with it's collection and a specific associated instance of a reading pane view.
In summary, I am suggesting there are use cases for each and that you should try to be judicious on choosing the right approach for each circumstance that you come across. Use both and learn to see what fits in which circumstances.
Lastly, in regards to the traceability of events, you can always write a wrapper for the On and Off functions in your event bus that both calls the normal On and Off functions but also adds/removes information to a register containing which objects are bound to which events. You could use this for debugging and write to the console information about these objects so you can have a clear picture of the state of your event bus and its listeners at any point in time. I've not ever thought about doing this, but there is no reason why you couldn't ;)
Post Edit: The comment by tsiki regarding using Local Event buses is a good one. In the above example I made with many tabbed mailboxes, you could use a local event bus for each user mailbox tab view. You might want to do this if each mailbox tab view became quite complicated in its own right containing many nested child views and large amounts of events to be generated/handled.
Related
Further to a post (CefSharp load a page with browser login).
I implemented the IRequestHandler interface and the different methods, particularly the GetAuthCredentials where I show a dialog and recover user and password and passing it to the event handler.
Now I want to access to the dom where I get several frameset with differents frames and I'm interested in one frame which I know the name Atribute.
Inside this frame I need to get list of different type of input , select etc...
In my app I have a button which I use to set values of the different elements depending if they are present on the displayed page.
PROBLEM is I don't see any way of getting the document, the frames collection etc....
CefSharp doesn't expose the underlying DOM, and is unlikely to see http://magpcss.org/ceforum/viewtopic.php?f=6&t=10652&p=19533#p16750
Your best bet is to use EvaluateScriptAsync and a combination of Javascript Binding
https://github.com/cefsharp/CefSharp/wiki/Frequently-asked-questions#2-how-do-you-call-a-javascript-method-that-return-a-result
https://github.com/cefsharp/CefSharp/wiki/Frequently-asked-questions#3-how-do-you-expose-a-net-class-to-javascript
If you absolutely must have DOM access and cannot invent your way to a solution then CefGlue might be a better choice for you. (I should point out that the DOM can only be accesses in the Render process, and as such calls needed to be passed to the Browser process though IPC, so it isn't a trivial task).
I'm using Caliburn Micro to create a Visual Studio style interface for my application.
The user could have multiple errors in multiple windows which are all shown on an 'errors' view which is docked at the bottom of my application. I've managed to implement a nice notification mechanism so that when changes are made to any document, the errors VM is notified and queries the source object.
I'm having trouble, however, in figuring out how to get a double-click to navigate to the source of the error.
Imagine I have bound 'description' to a textbox on my document view and this provides a 'description is required' validation message. Double clicking the message should navigate the user to the document in question and focus on the textbox.
The validation objects that provide the messages already contain a reference to the viewmodel (as an IViewAware interface) which allows me to call GetView() to get a reference to the view. I've tried using this approach to enumerate the bindings on my view and find the controls that are bound to a particular property, though this is very slow and more of a brute force - pot luck approach
Does anyone have any recommendations on approaches to this problem? My only other thought is to pass responsibility of the 'focus' action to the view itself and have that decide how to handle a request for navigation to a certain property - maybe using attached properties to identify particular controls by a unique property identifier. The application could cache this list in a dictionary to make things quicker (all the processing done up front as the app is loading)
I assume Visual Studio implements this quite easily for the standard code editor window, since it just needs a line number to jump to the right place..
Ok so after a couple of hours messing about with this I've come to a solution which seems to work ok. I've implemented an interface class for my views which allows them to navigate.
The basic setup is:
ErrorsViewModel - receives notifications that a business object needs requerying for errors and manages the list of errors and when it needs to query (so that any work can be done on a background thread to prevent locking the UI)
WorkspaceViewModel - the main workspace VM that hosts the tool panes and documents
ICanNavigate - interface that the views will impelement to decide how to handle navigation arguments
NavigationEventMessage - the message that is sent to the event aggregator to provide a navigation notification and arguments
The ErrorsViewModel receives an event message from another VM via the event aggregator and eventually queries the object (when it can). Once the errors are gathered, it shows the items in the view (in a grid control at the moment).
When the user double clicks on a row a NavigationEventMessage is dispatched which contains some arguments such as the view that dispatched it and the target business object etc. It is handled by..
1) The WorkspaceViewModels view. This allows it to bring the correct document into activation by looking at the DataContext of each object and checking if it matches the view passed in the message
2) The target documents view, the view decides what to highlight/show/seek to based on the arguments sent in the message
This seemed to be the most logical way to separate the VM from the View as the ViewModels still have no coupling with the views. The view just implements the interface, and the VM calls it if it's there and passes in the args.
I'm writing an application using C and ncurses library. My program is intensively exchanging data with some sensors (sends and receives some data). There are many sensors (up to 500). Now I want to display information about each of the sensor in the 'user-friendly' way: it has to be kinda of scrollable list with items, each of which the user can choose. My best idea now is to use ncurses 'Menus library'. The steps are following:
Create list of items (actually some array of strings, where each string contains sensor number, type and other information.
Create menu and add these items to menu
Display menu
It might sound ok, but the problem is that information (actually state) of each sensor is dynamically updated. Ok, I can implement some 'hot-button' for updating information, but it might be very very time consuming: rebuilding and redisplaying all items is not a good idea.
So how would you do this?
You basically have a view of a data source and the data source is being updated and modified and you need to signal to the viewer that a particular data item has been modified so that the viewer can update its view.
Typically what I do in a situation like this is to have an event pathway so that when the data source that is shared between the viewer, the consumer of the data, and the producer, your sensor threads, the shared data is updated and then an event message is sent to the viewer.
The event messages are usually sent using some kind of a FIFO queuing mechanism so that the viewer will receive the events in the order in which they are sent by the producer.
With an event driven windowing system such as Windows, there is usually a PostMessage() function that allows you to post a message to a particular window or to a particular thread.
So the basic window design is to accept the messages which have a message identifier indicating that the source data has been updated along with some identifier or index to indicate which of the items was updated. The Viewer would then access the storage area to pull that particular data item from the area and then use that data to update the specific item text in the view.
This is pretty much standard MVC pattern. The nasty bit is how you communicate the source data update event to the viewer.
I have a Region which can have only one active view at a time. I want to add new view to the region on user action and remove the existing view from the same region. I also want to maintain the cache of few views. If the no of view is more than the specified limit then I will remove the oldest view. Is there any direct support for it or I have to implement the Region adapter for it. Is there any other better approach for the same?
Well, let me answer your two questions.
First, if you want a Region to only show one view (like say you have a region defined as a ContentControl), that is possible. You can add many views to that region and only the active one will be shown. To show a different view in that region that has already been added, you would simply Activate that view:
var region = regionManager.Regions["TabRegion"];
region.Add(view1);
region.Add(view2);
region.Activate(view2);
In this way, you can have many instantiated views ready to go, but only one visible.
Second, with the expirations. I'd say a region adapter would be the cleanest and most correct way, but you could just create an expiring cache for these and when they expire, you can remove them from the region if they aren't active:
var region = regionManager.Regions["TabRegion"];
region.Add(view1);
regionTracker.Add(view1, region, TimeSpan.FromMinutes(10));
region.Add(view2);
regionTracker.Add(view2, region, TimeSpan.FromMinutes(10));
region.Activate(view2);
And then the implementation of your expiration for your regionTracker could just:
if(!region.ActiveViews.Contains(ViewThatJustExpired))
{
region.Remove(ViewThatJustExpired);
}
It's a bit half-baked, but hopefully this will give you some idea of where to go.
Take a look at my blog post about dynamic module loading in PRISM with Navigation. In that post you'll see how I use a multiple-view container, then swap views into and out of focus. It involves having an interface for navigation, then raising events that swap the view state using the visual state manager.
Click Here to View
Jeremy
I hope this makes sense.
I have created several WPF User Controls. The lowest level item is 'PostItNote.xaml'. Next, I have a 'NotesGroup.xaml' file that has an ItemsControl bound to a List of PostItNotes. Above that, I have a 'ProgrammerControl.xaml' file. Each ProgrammerControl has a grid with four different NotesGroup user controls on it (and each NotesGroup contains 0-many PostItNotes.
Then, I have my main window. It also has an ItemsControl, bound to a list of Programmers.
So, you end up with a high level visual view of a list of programmers, each programmer has four groups of tickets, each group of tickets has many PostItNotes.
The trouble I'm having, is that I want to respond to a mouse click event in my mainWindow's code behind file.
I can add a MouseClick event into my PostItNote.xaml.vb file and that is getting called when the user clicks a PostItNote, and I can re-raise the event; but I can't seem to get the NotesGroup to listen for that event. I'm not sure if that's even the correct approach.
When the user clicks the PostItNote, I'm going to do a bunch of business-logic type stuff that the PostItNote control doesn't have a reference to/doesn't know about it.
Can anyone point me in the right direction?
You have a couple choices:
Use the PreviewXXX events which are fired during the "tunneling" phase of WPF event routing. The parent controls can always preview the events going down through them to children.
Use the more advanced approach to hooking up events leveraging the AddHandler method to which you can pass a parameter called "handledEventsToo" which basically means you want to know when the event happened "within" you even if some descendent element handled the event itself.
I am going to take a flyer here. You probably don't want to be handling the event that high up; not really anyway. You are catching the event at the lower levels, which is unavoidable. Consider invoking a routed command from the PostItNote click event handler.
The routed commands bubble up and tunnel down through the tree. You can have an architecture where a high-level handler can listen to a logical event (Opening a postit note perhaps?). The handler for this doesn't need to care where the command originates from. It might be from you clicking something, it might be from clicking on a toolbar button. Both are valid scenarios.
It sounds like you are creating some kind of custom UI, am I right? You want the application to respond to the users interactions. That is what the RoutedCommands are for.