We have written a base Backbone view class which abstracts the common methods and properties. All our view class in a module extends this base class. Also this classes use same model.
The model contains a collection. I am listening to change event of collection. Whenever there change in collection i want to listen for change event to the corresponding view. Instead this event is getting triggered for all the model instances created.
Please share your inputs to resolve this issue.
in your base view, you can listen to model change events like this
intialize: function(options){
....... init code
this.model.on('change',this.modelChanged, this);
}
then in the modelChanged handler you can trigger a custom event on the view
modelChanged: function(){
this.trigger('modelChanged', [your args]);
}
then you can listen to the custom view event with 'on' function like we did with the model
Related
What is the best way for the view model to "ask" the view to do something, (e.g. manipulate on of the controls)? I don't think that the view model sending events violates MVVM as after all the INotifyPropertyChanged interface is an example of view models sending events to views.
I realize that having code behind in the view is not considered ideal from a MVVM perspective, so I could use an attached behavior to manipulate a particular control for example but the problem remains - how do I ask the attached behavior to do something from my view model?
I could have Boolean dependency property on my view with property changed event handler which runs the code I need to run. I could then bind this property to a property on my view model and then toggle the value of this property whenever I want to trigger the code to run. However this seems like a hack.
I could have an event on the view model and then explicitly attach this event to an event handler in the view in code but this seems anti MVVM - I should be able to achieve the connection using binding.
This must be a fairly common scenario, are there any standard solutions?
You could for example use an event aggregator or a messenger to send an event or message from the view model that the view handles in a loosely coupled way. Both the view and the view model know only about the event aggregator but they don't know anything about each other. Please refer to this blog post for more information about the concept.
Another common approach is to implement an interface in the view and inject the view model with this interface, e.g.:
public interface IDoSomething
{
void DoSomething();
}
public partial class Window1 : Window, IDoSomething
{
public Window1()
{
InitializeComponent();
DataContext = new ViewModel(this);
}
public void DoSomething()
{
//do something...
}
}
public class ViewModel
{
public ViewModel(IDoSomething doSomething)
{
//...
}
}
This doesn't break the MVVM pattern as the view model knows about and is dependant only on an interface.
I have a nested composite view that checks its model for a collection, and if so sets that collection to itself as its collection.
onRender: ->
if model.attributes.has_collection
#collection = model.get 'myCollection'
This works quite well when the model has a collection, and the item view(s) all display, and when I add a new model to the collection, a new view appears.
However when there is no collection, and the button is clicked to create a new model, I need to create the model, set the collection (a collection of one) to the view's model, and get the view to display the model and any further models that are added. I have tried various things, right now I set the collection on the model and then run this function :
class List.myCompositeView
childView: myView
// *** //
setChildren: ->
#collection = #model.get 'myCollection'
#render()
The first model appears as it should, but further models that are created do not display. To repeat, they do appear when the collection is set in the onRender function. I realize there is lots of code that I did not add here, so if there is something (potentially) relevant to the problem that you need to know, let me know. Could it be that the view is not binding to the collection's events properly? Thanks!
A marionette collectionView has a private method called _initialEvents. When your collectionView is constructed it sets up this method to be called the first time the view is rendered. If your first render is when a collection view is not set then this would make sense that the events would not get wired up correctly. You should be able to call this private method after you set the collection and everything should work: #_initialEvents()
If it is helpful, this is the implementation of that method:
if (#collection) {
#listenTo(this.collection, 'add', #_onCollectionAdd);
#listenTo(this.collection, 'remove', #_onCollectionRemove);
#listenTo(this.collection, 'reset', #render);
if (#getOption('sort')) {
#listenTo(#collection, 'sort', #_sortViews);
}
}
I have a collection of models in Backbone. One model is triggering a custom event, that I can catch, listening to the collection. What is the best way to find out, which model triggered the event?
You can add the model ID with your custom event like this:
yourModel.trigger('customEvent', yourModel.get('id'))
and on the callback that catch the custom event you'll get the id as a parameter:
yourCallback: function(id) {
...
}
I am working on WPF application using MVVM and PRISM and stuck in one issue.
I have two different views(View1 and View2) with their respective view-models.
View1 is main View having a list of domain objects and View2 is used to display the properties of domain object. Now I need to pass the object to View2 every time the selection is changed.
I know that we can do it IEventTrigger but a view model can listen the event only when it is residing in memory.
So here my problem arise. Since the first there is not selected item. The View2 is not rendered. I dont know how to pass the object to the View2 first time via Event.
What can be the possible solution?
Since you said in a comment that you don't want one ViewModel to refer to the other, you can use PRISM's EventAggregator for this instead
Whenever the selection changes, broadcast a SelectionChangedMessage from ViewModel1, and have ViewModel2 subscribe to receive those messages.
If you also need to know the selected item when ViewModel2 is first created, have it broadcast something like a GetCurrentItemMessage, which ViewModel1 can subscribe to receive and would make it re-broadcast the SelectionChangedMessage
Also if you're new to PRISM's EventAggregator, I have a static class on my blog that can be used to simplify how the EventAggregator is used, as I find the default syntax very confusing and hard to understand at first. I use it for most small applications.
If your View1 contains a List which has a SelectedItem property, you could create a SelectedItem-Property in your ViewModel1. The you create a ViewModel2-Property in your ViewModel1.
You bind to it like:
<ListView SelectedItem="{Binding Path=SelectedItem}">
.
.
</ListView>
<my:view2 DataContext="{Binding Path=ViewModel2}"/>
Finally you pass the SelectedItem in the setter of your SelectedItem-Property:
public object SelectedItem
{
get { return _seledtedItem; }
set { _selectedItem = value; ViewModel2.SomeProperty = _selectedItem; OnPropertyChanged("SelectedItem"); }
}
Basically, I added a custom event to my TreeView derivative. It is meant to replace the AfterSelected Event, which I disable, because I require a different Event handling mechanism for when people click on tree items.
The event handling itself works well. But I want my new event to be the one wired when a custom control is doubled-clicked in the design view.
The custom class is called CustomTreeView, and this is how it declares its new event.
public event EventHandler CustomSelect;
So basically, when double clicked in the design view, I want this to appear in the MyForm.Designer.cs
this.MyCustomTreeView.CustomSelect += new System.Windows.Forms.TreeViewEventHandler(this.MyCustomTreeView_CustomSelect);
And obviously, the MyCustomTreeView_CustomSelect() function should be added to MyForm.cs
so how do I set this up in my custom control?
You're looking for the DefaultEventAttribute
[DefaultEvent("CustomSelect")]
public class MyCustomTreeView : TreeView
{
//...
}