There seems to be a lot of "behind the scenes magic" involved in TreeViewDragDropTarget.
All the posts I've seen so far, stop at the presentation of the treeview without explaining what's going on with collection of data the TreeView is bound to.
How does the TreeViewDragDropTargt communicate back to the ViewModel property that my TreeView is bound to? Are there any MVVM oriented walkthroughs or tutorials about TreeView Drag and Drop ala MVVM?
UPDATE:
"If an item is dropped onto the drag drop target, it is added to the nested control if the nested control is bound to an ObservableCollection (or any collection that implements INotifyCollectionChanged and contains the same type of items as the item that was dropped)."
And what that means is that I can hook into the CollectionChanged event of the ObservableCollection my TreeView is bound to keep my model in sync.
Is there a better option?
I haven't seen a better option so far. Remember that the whole point behind the MVC/MVP/MVVM patterns is to put as much code as possible under tests.
For Drag/Drop, I've used the Presentation -> Code Behind -> Messenger Raise message "Items Added" ... and catch the event from the ViewModel.
If there's custom logic in the Code Behind, for example, validate that the "source" is of a valid status for example, or of the right type etc, you could delegate this checking in a "service" (Dependency Injected) that would be specialized in handling these odd cases. The Code Behind becomes miminal, and the "drag service" becomes testable.
Do you need really need a "Drag Service", and if so, should you have one "Drag Service" per ViewModel, or one per application ? I'll answer with your classic answer "It Depends".
Related
I am going crazy trying to figure out how to manipulate telerik RadGridView for WPF's column filtering from my ViewModel. I thought I might be able to bind the value of a FilterDescriptor to a ViewModel property, but I get
Cannot find governing FrameworkElement or FrameworkContentElement for target element
<telerik:CompositeFilterDescriptor>
<telerik:FilterDescriptor Member="Foo.SomeProperty" Operator="IsContainedIn" Value="{Binding SelectedThings}" />
</telerik:CompositeFilterDescriptor>
where Foo.SomeProperty is a VM property bound to a grid column and SelectedThings is a VM property containing a stringified array of unique values to filter against.
GridView is bound to a QueryableCollectionView.
I want the filtering to work at runtime, based upon some custom logic in the VM - ex. user clicks some button and the VM restricts distinct values of one of the columns.
This shouldn't be so hard. I must be approaching this wrong. I've been pooring over other stackoverflow questions but haven't found a solution that works yet. Any suggestions would be appreciated.
UPDATE
I would still like to achieve this with a completely MVVM approach, but for the time being I've decided to stop going nuts over it and resolve the issue in the following way.
ViewModel's ObservableCollection which is bound to a list of checkboxes representing some filtering criteria are updated by the user through the GUI.
Upon that property change the ViewModel sends a message via Messenger (MVVMLIGHT) which in the codebehind has registered to receive. The message includes the filtering criteria information.
The codebehind receives it and applies it directly to the GridView.
What's nice about this is it avoids having to wire up any DependencyProperties or other event handlers and allows the VM to remain ignorant of the View implementation. It's simple and it works.
Yes, there's a little code behind now, but until I can find some better info on how to solve this problem purely through MVVM binding, this is a workable solution.
Wish I'd done this at 9 AM yesterday instead of banging my head against the wall all day long reading Telerik's crappy documentation and hunting for examples of how to do it "the right way". Meh.
Working on custom WPF control which is called "MultiSelectTreeView," which inherits from System.Windows.Controls.TreeView. Its purpose is to allow for multi-selecting and dragging and dropping.
The xaml for the view which contains this MultiSelectTreeView control binds an ObservableCollection that is exposed by the underlying view model.
A drag-and-drop operation might possibly involve many remove/add (or move) operations on the ObservableCollection, however I need to encapsulate all of the operations for a single drag-and-drop operation into a command object to support the undo/redoing of the drag-and-drop as a single atomic operation.
When I hook into the ViewModel.ObservableCollection's CollectionChanged event multiple events fire and from the perspective of the ViewModel, there is no way to know if any particular add/remove/move event will exist in isolation or whether it will be a part of a series of events, all related to a single user drag-and-drop.
I can imagine all sorts of wonky solutions, such as giving the MultiSelectTreeView control all sorts of in-depth knowledge of its underlying ViewModel's possible structure (to momentarily unhook the ObservableCollection's CollectionChanged event), but that doesn't feel right at all.
Perhaps I should create my own descendant of ObservableCollection, which supports a .MoveRange() method that only fires one event, or something along those lines.
I'm sure someone with more than just a few weeks of WPF experience could probably suggest a profoundly better solution than these.
I'm not sure if this will help you, but I just ran across it today.
Batch Updates with INotifyCollectionChanged
I am having difficulties in making TabControl run flawlessly in MVVM architecture. Currently what I am doing is having TabControl's ItemsSource property bound to ObservableCollection Screens property. Each time I want to add new tab, I create adequate ViewModel, add it to Screens, and throuh data templates adequate View will be shown.
Problems:
1) it seems that desctructor for my ViewModels are not fired until complete application closes. I am not sure if Data templates are the cause of this. When I remove ViewModel from Screens collection, it should be available for GC, since all I did was added it to Screens collection, which showed the View, and then when command to close the view was issued, I removed from collection. After that I tried to force Gc.Collect, but still dctor fires only on application close. I am not sure why is this happening...
1) in some data structures it is not possible to bind to ViewModel's property through ElementName, so one way around is to use RelativeSource binding. However, this creates lots of binding exceptions when element (ex View) is being closed. Similar problem to mine is described here:
How to avoid Binding Error when parent is removed
In my case TargetNullValue and FallbackValue do not help, and the only way around I have found is to have ViewModel as StaticResource. Problem with this approach is that when using Screens collection and data templates to connect Views and ViewModels, you cannot create viewmodels the usual way:
<UserControl.Resources>
<vm:SomeViewModel x:Key="someViewModel" />
</UserCpntrol.Resources>
So, is there an alternative approach to using TabControl in MVVM scenario, or I am doing something wrong here?
Regarding the destructor part, should not use the destructor. It is the recommended approach to implement the IDisposable interface instead. This will help you to automate the cleanupd of your objects and lets the GC do the dirty work for you:
Use this method to close or release unmanaged resources such as files,
streams, and handles held by an instance of the class that implements
this interface. By convention, this method is used for all tasks
associated with freeing resources held by an object, or preparing an
object for reuse.
My favorite MVVM tutorial uses a Tabcontrol as a central UI control: WPF Apps With The Model-View-ViewModel Design Pattern. This may give you hints to a nice and working approach.
I'm implementing the MVVM pattern in a Silverlight Application that utilizes 3rd party software (ESRI's Silverlight API). This 3rd party control only exists in the XAML:
<esri:Map x:Name="map" ... />
The ViewModel has an ObservableCollection of object which are bound to a ListBox. When a user double clicks on a specific item, the Model (which is bound to a user control inside that ListBox) fires an event which the "Main" view model is subscribed to.
My question is, in the "Main" ViewModel where the esri:Map resides, how do I call functions from this 3rd party control with data from the event (ex. map.ZoomTo( result ); )? The only solution I have so far is to move the event code (in the ListBox user controls) from the ViewModel into the Code-Behind and then subscribe to those events in the "MainPage" xaml and fire the code in the code behind.
Is this the best option utilizing the MVVM pattern??
Sounds like you want either a Blend Trigger or Behavior, depending upon the exact scenario. Here's an introduction to them.
That's a perfectly valid approach for "ViewModel First" MVVM. Another approach is to have the Code-Behind be ViewModel Aware, and directly call methods in the View Model from the event handlers - which is suitable for a "View First" approach to MVVM.
I am a huge fan of separating the whole thing, and using a good MVVM framework like Caliburn.Micro. With a framework like such, you could do something more OOPish, very SOLID, still MVVM, by utilizing a MEF:
Your View would simply trigger events in an Event Aggregator, with a message payload with information to whatever receiver handles it. The View doesn't care who handles the event, all it cares about is to raise the event to the aggregator.
Your View Models would handle whatever events are appropriate for each one. They don't care who raises the event, only that they are in charge of handling events from the Aggregator.
You will find that direct ViewModel first and View first implementations, through event handlers, of MVVM begin to grow cumbersome as your enterprise solition grows: it's scalability is limited.
In the MVVM (Model-View-ViewModel) pattern should the ViewModel reference the view. I would think that it should not. But how should the following scenario be handeled? I have a view that has a tab control as the main container, the viewmodel for this view implements a command to add a new tab to the tab control. The easy way would be to allow the viewmodel to reference the view and then in the command implementation to just programmatically add the new tab to the tabcontrol in the view. This just seems wrong. Should I somehow bind the tabcontrol to the viewmodel and then implement a data/control-template to add the new tabs. I hope this makes some kind of sense to somebody :)
In "pure" MVVM, the ViewModel shouldn't really reference the View. It's often convenient, however, to provide some form of interface in the View whereby the ViewModel can interact with it.
However, I've found that I almost never do that anymore. The alternative approach is to use some form of attached property or blend behavior within your View, and bind it to your ViewModel properties. This allows you to keep the View logic 100% within the View. In addition, by creating a behavior for this, you create a reusable type that can be used to handle this in every ViewModel->View interaction. I strongly prefer this approach over having any View logic within the ViewModel.
In order to demonstrate this technique, I wrote a sample for the Expression Code Gallery called WindowCloseBehavior. It demonstrates how you can use a Behavior within the View bound to properties in the ViewModel to handle controlling a Window's life-cycle, including preventing it from being closed, etc.
Reed and Dan covered the general approach but in reference to your specific case, TabControl is an ItemsControl and so can bind its ItemsSource to a data collection in your ViewModel representing the set of tabs to display. The UI for each type of tab can then be represented by a DataTemplate specific to the data type of an item (either using DataType or a DataTemplateSelector). You can then add or remove data items as needed from your VM and have the tabs update automatically without the VM knowing anything about the TabControl.
I find that it's often a helpful compromise to expose an interface on the View that handles View-specific functionality. This is a good way to handle things that are awkward to accomplish with pure binding, such as instructing the form to close, opening a file dialog (though this often gets put in its own service interface) or interacting with controls not designed well for data binding (such as the example you provided.)
Using an interface still keeps the View and ViewModel largely decoupled and enables you to mock the specific IView during testing.
One of us is missing something obvious. Your tab control is an ItemsControl. You should bind the ItemsSource of your tab control to an ovservable collection in your view model. When you handle the command in your view model to add a tab, you simply add a new element to this collection and, voila, you've added a new tab to the control.