I have a WPF TreeView which varies in depth. My application also has the ability to search inside the tree. This is accomplished by using the Filter property of the ICollectionView interface. Searching and filtering all works fine, but the problem lies when I try to remove the filter.
I have the following scenario; I enter search criteria and the treeview is filtered and the result is shown. Now when I press my button to clear the filter, the filter is removed but the problem is that it shows only the child nodes of what I previously searched for and does not show the child nodes of the other nodes, these nodes are also not expandable anymore.
My search function recursively walks down the tree and tests each node for the entered search text.
Do I have to apply the view.Filter = null; statement for each node and their childnodes?
Thanks,
Grant
Yes!
Every hierarchy level has it's own ICollectionView and each uses it's own filtering. So yes, yu have to clear the filter on every (parent-)node (depending on your exact implementation).
Related
I'm having real problems with TreeViews BringIntoView implementation when trying to select things in the tree that are bound via ItemSource and ItemTemplate. It just doesn't work, I've tried pretty much all the fancy ways i could find via google and most don't work and if they do the wrong thing is always selected.
It would be be easier for me if i could just walk the tree one level at a time via treeview items instead of the the things i'd bound in.
Is there a way of "mapping" my bound hierarchical data template items back as treeviewitems?
NOTE: it would appear part of the problem with bring into view is that the items im loading into the tree is done in a "lazy" manner where the children are created until they shown - i have to do it this way as the dataset is huge.
To summarize: I can quickly search through my dataset and ascertain a "nodes" full hierarchical path quite quickly i.e. i can get "root\p1\p2\p3\data" easily enough, but I cant get a treeview thats bound to the dataset to display it using bringintoview. If I could walk the tree using treevitems i could find the node myself and call bringintoview on that. thats the plan anyway.
mucho tacks.
EDIT:
After some more searching I have found this http://blogs.msdn.com/b/wpfsdk/archive/2010/02/23/finding-an-object-treeviewitem.aspx. Which works buts also has problems - I can't initially expand the root object, and I can't work out how to seperate the ItemsPanel and ItemContainerStyle into toplevel styles/resources so i can share them about my other treeviews easily.
I have used the demo here. All works well.
http://code.msdn.microsoft.com/Word-2010-Using-the-Drag-81bb5bff
However, when I try and swap out the listbox for a treeview, it just doenst want to play.
5 minutes to get the demo working and over 2 hours now trying to use the tree view instead. IT shouldnt be that hard surely?
Relating specifcally to the demo mentiond above and my modfiication from a listbox to a tree view:
There is a test on "e.Data.GetDataPresent" which determines the "effect" of the cursor/pointer as it is moving across the GUI - from the tree view in the task pane to over the document (With the effect being set in a ItemDrag event in this instance).
A "DragEnter" event is rasied when the cursor is then moved over the document (well a form overlaid on the document), If data is not found, the effect is set to "DragDropEffects.None".
This will mean when dropping the item on the document (via the invisibile overlaid form) the "DragDrop" event will not be raisd as "DropEffects.None" stops this.
The problem was surrounding my test of data bing present in the DragEventArgs.Data property.
The interesting thing, though, is the affect of "DragDropEffects" and its side effect on other events.
I appologize for the novel but I wanted to explain as much as I have done thus far.
Within my current project I have an application that consumes a service that provides a collection as a <List>. Due to how I am using this data in the application I have had to convert this data to an observable collection. This was done so that as the data was selected and moved about the application UI updates would be refreshed using INotifyPropertyChanged and INotifyCollectionChanged.
Where I am having a challenge now is I have a listbox that is bound to the observable collection within the listbox I have a datatemplate that renders out the items of the collection. This data template contains a button which needs to allow the user to click the button for each item to remove them from the collection.
The use case for this is a listbox that stores selected name as chosen from a gridview. Once the user has selected names from the gridview they are stored ( within the observable collection as a queue) and rendered out in the UI in a listbox control which shows all selected names. I need to provide the user with the ability to remove these names in any order selected.
From what I have been reading there is no means to enumerate / index an observable collection. For situations such as this you should use List or an Array. However in order for the items to refresh in the list view they need to be in an Observable Collection.
From what I have read it appears that when the event is triggered I need to convert the observable collection to an Array and then evaluate the array to determine the index and then remove the record accordingly?
I think I may be off base on this as it seems like I am over engineering this problem? The above scenario does not seem correct is because I fell as if I am doing a lot of converting to and from the collections to just remove a record?
Does anyone know of an efficient means to remove records from a collection ( in any order selected) when the collection is rendered out as an items control within a listbox?
I’ve been successful in removing the last record added to the collection using RemoveAt() however I have not had any success in randomly removing records.
Afterthought: Part of this issue could be related to the fact that I have a button inserted within the datatemplate (control item) and as a result the item is not actually being selected before the event is fired on the button event?
Sorry for the rambling on this but I have had my head in this for hours and made minor progress. Any tips or ideas would be appreciated!
ObservableCollection<T> inherits from Collection<T> which implements IList<T>, so you can certainly index and enumerate it. It has a Remove method that takes the object to remove and removes the first occurrence in the collection and a RemoveAt method that takes an index and removes the item at that index.
Based on your afterthought, it sounds like you have a WPF ListBox with an ItemTemplate that creates a Button. ListBox will set the DataContext of each instantiated template to the item in the list being bound to, so you can get a reference to the item that created a Button from the DataContext property on the Button or by using a Binding.
I am working on an MVVM-based WPF app that uses the DataGrid. The grid is data-bound to an ObservableCollection<Widget> (the Widget list). The user needs to be able to insert a Widget into the list below the currently-selected Widget, to delete a selected Widget, and to reorder the Widgets on the grid. I'll implement the reordering through dragging and dropping.
Before I dig into this, I want to get a reality check on my approach. Here is what I am planning to do: The Widget class will have an Index property that will be used to order the Widget list. Additions, deletions, and reordering will be done by manipulating this Index property, and sorting will be done on the Widget list, rather than through a CollectionView.
Before the DataContext is set, the Widget list will be sorted on the Index property. When a user adds a Widget, the Index property will be incremented by 1 for each Widget below the selected item on the grid, and the new Widget will be given the index number opened up by the renumbering. The Widget list will resorted, and the bindings will be refreshed.
When a user deletes a Widget, the Widget will be removed from the list, and the Index property of items on the grid below the deleted item will be decremented by 1. The Widget list will be resorted, and the bindings refreshed, as above. Reordering will be done as a combination delete-and-insert.
Here is my question: Is this a reasonable strategy for implementing inserting, deleting, and reordering? Is there a better way to go about it? Any good articles or blog posts on the subject? I've looked, but I'm not coming up with anything on-point.
Thanks for your help.
I've got this problem figured out. I do need the Index property, but I don't need to sort the ObservableCollection. I create an interface, IIndexedObject, with one property, Index. I implement that interface on any object that needs to be indexed, and in my database table, I add an Index column.
When I load into my object model, I sort the database table on the Index property. That ensures that the list is loaded in the same order as in the last run. I add objects using the blank row provided at the bottom of the DataGrid. It gives them an index of 0. I delete objects using a Delete button bound to an ICommand in the view model.
My view model subscribes to the CollectionChanged property of any ObservableCollection that contains IIndexedObjects. When the event fires, the view model passes the collection to a ReIndexCollection service method, which takes an IIndexedObject. The method re-indexes the collection, based on its current order, by simply iterating the collection, assigning an incremental integer value to each element's Index property. When I persist the collection back to the database, the Index value gets saved, to ensure that the collection is loaded in the same order on the next run.
Since the only sorting that is needed is done at the database load, there is no need to sort the ObservableCollection. When I move items on the DataGrid, it will take care of re-ordering the collection at that time. All I have to do is re-index the collection, so that its order will be persisted back to the database.
All-in-all, it's much easier than I had expected. One of the reasons I like WPF and MVVM.
Improving sorting performance:
http://blogs.msdn.com/jgoldb/archive/2008/10/30/improving-microsoft-datagrid-sorting-performance-part-3.aspx
I am developing a wpf desktop app with strict MVVM pattern.
Current my app is doing following things:
Showing a Treeview with HierarchicalDataTemplate.
User can expand or collapse Nodes.
User can add add new Nodes(Drag n Drop + double click).
Everytime a new Node is added Model is updated and Treeview is recreated based on Model.
Because Treeview is recreated, all nodes are shown as expanded after adding nodes.
I want to show nodes with their previous expanded condition. Is there any way to do this using MVVM ? What I have thought so far is
Model should not contain any data related to how to draw UI ??
VM should just get data from Model and put it in UI(and pass date from UI to Model) ??
Thanks for your thoughts. I may be way far out form rail. But just want to have some wisdom from you guys.
Thanks
PAIJA
If you haven't already, read this great article by Josh Smith: Simplifying the WPF TreeView by Using the ViewModel Pattern
Basically what he suggests there is to include a property called IsExpanded in your VM and bind the TreeView to it correctly so that the expanded/collapsed status is entirely controlled by the programmer.
One solution what I think could be is to stop the recreation of the tree, just update the model and only add nodeitems to the current node where you are dropping them. Just refresh the collections in model and dont refresh the tree. Let us know if this does't suits your architect.
Thanks,
Jagdev Josan
The view model can contain view related information, that is what it is for. It is a bridge between pure business and pure view. My view models usually expose a few business properties of an object and add a few related view properties. If all you need is business properties, then bind straight to the business layer. Its only when you need to do something like your situation here that you need a view model.
If you want to completely recreate the tree (which sounds crazy) you can store the expanded state of the nodes in your view model and bind them to the tree view items using an ItemsContainerStyle. that way when you recreate your tree view your previously expanded nodes will still be expanded.
So your wrapped business objects will contain an extra property IsExpanded that you can use to restore your tree view state.
P.s. did i mention its a bit over the top to recreate the tree view?