I have a Silverlight TreeView control that I'm dynamically populating when a specific node is clicked. For example, I load the first generation (level) of nodes, then when the user clicks on one of the nodes, I use the Selected event to populate that node's children, etc, etc. I'm sending back from my database a bool value with each node value that indicates whether or not the new nodes have children nodes.
I am wanting to set the expand arrow to show in front of new nodes that have a child, but the catch is that its children will not be populated yet. I thought maybe setting the HasChild to my bool value, but HasChild is read-only. Any suggestions would be very much appreciated.
Just a add blank treeviewitem when it's collapsed just to show the arrow.
But when expanded by the user, clear that blank child, and do an async call to get the real children.
Set the UserState to the parent treeviewitem or id when doing the GetChildrenAsync call.
When they arrive, the UserState should be that parent treeviewitem or id, when you find that parent which you just set it's itemsource to the e.Result.
If you're familiar with dependency properties, you could create a dependency property that would be settable by you in code and gettable in the xaml so it could be bound to.
This would allow you to use a ValueConverter to render the visibility of the arrow.
Related
I am new to MVVM and WPF treeview. I have done some research and have read the Josh Smith article on MVVM, and this, and this.
I think I have no problem creating the treeview in WPF. The thing is on my app, the left panel is the tree view, the right panel will display some properties of the selected tree view node, which user can click a button to edit the properties and save it to the data source (and potentially will affect the treeview item). In addition, user would be able to add/delete child node/grandchild node.
I can't seem to find any article/example to implement this using MVVM.
I am currently thinking that in the view models for the child node and grandchild node, I shall add a public property which points to a Usercontrol. The right panel will bind to the treeview's selected item's Usercontrol. The thing is, when user adds a child node/grand child node, the right panel will be used to let user fill in information and save. I am not sure whether it will affect the binding.
And other issues like editing the properties of a tree node will mean to copy all the child node info of the node to the new node and delete the old node from the tree and add the new node to the tree?
Can someone point me to any good articles on a similar implementation or give a rough idea on the issue that I should take note etc?
Thank you very much.
Angela
A lot depends on your setup but here is a way I have used before.
Note that you might need a ChildPropertyChanged event type of thing (I made that name up) to bubble up changes in the tree to the root of the tree.
To add a node
I created a ViewModel that contains:
a property for the tree data collection (probably a reference to the root node)
a property called NewNode.
a property called CurrentNode
a command that adds the NewNode to the CurrentNode: AddCommand
In the View:
Bind the TreeView to the tree data collection
Bind the SelectedItem of the TreeView to the CurrentNode
Bind the controls with the data for the new node to the NewNode properties
Bind a button to the AddCommand
In the AddCommand:
Add the NewNode to the CurrentNode and re-initialize the NewNode property.
To edit a node
In the ViewModel
add a command: UpdateCommand
add a command: EditCommand
In the View
bind some edit controls to the CurrentNode's properties (OneWay binding)
bind a button to the EditCommand
bind a button to the UpdateCommand
In the EditCommand:
make the edit controls and update button visible (I use a State property to bind to, see Extra below)
In the UpdateCommand:
write the values of the edit controls to the SelectedNode
hide the edit controls (I use a State property to bind to, see Extra below)
To delete a node
In the Viewmodel
add a DeleteCommand
In the DeleteCommand:
remove the CurrentNode from the collection
Extra
I have found it very useful to implement IEditableObject on the ViewModel of a Node.
Using the methods of this interface you can add a cancel button to reverse the EditCommand. By adding a State property to the ViewModel of a Node (New, Modified, Deleted) you can track changes, know what updates to send to the model/database and you can bind the View to it to show/hide elements.
Problem:
I'm having trouble managing focus and item selection in a WPF ListBox with an embedded ListBox and some custom selection management in the code behind.
Background:
The "parent" ListBox is assigned a DataTemplate containing a "child" ListBox. I have added code to navigate using the up and down arrow keys from the parent ListBoxItem to the child items and from the child items back to the parent items.
When navigating back to a parent item which is not the first item in the first level list box the focus is always set to the first item and it is selected even though it is set in the program to a non-first item (e.g. parent item #2). I have traced through the code and the focus and selection is set to a non-first item but then another event to select and set the focus to the first item is always received.
There was another case on stackoverflow which was similar to what I am experiencing. The case is wpf listview lost the focus I tried all of the answers and responses in this case and none of them worked.
Does anyone have insight into why I can't programmatically select a non-first element in the parent ListBox?
Try using ListBox.IsSynchronizedWithCurrentItem = true then working with the list's collection view (if you're not explicitly creating one, you can use CollectionViewSource.GetDefaultView) to set the current item (ICollectionView.MoveCurrentTo()).
Selectors have a much more preferable method of dealing with selected items when working with the collection view, rather than SelectedItem.
I have been struggling with the Focus in ListBoxes too. Perhaps the answers to these two questions will help you further. Especially the trick with setting the focus on a Background thread is very very useful.
Deselect item in ListBox
Delay change of focus
Is there any way to tell when the containers are finished being made for a ListView?
A detailed explanation of what I've done so far
I have a ListView control that has a DataTemplate in one of its columns that contains a CheckBox Control.. I've figured out how to access the CheckBox dynamically using the object that the ListView is bound to.
ListViewItem lItem = (ListViewItem)ListView.ItemContainerGenerator.ContainerFromItem(trackToHandle);
CheckBox checkBoxToHandle = FindChild<CheckBox>(lItem, "CheckBox");
The problem is that the CheckBoxes "reset" (become unchecked) whenever I scroll too far or whenever I sort the columns.
I figured out this was because the VirtualizingStackPanel was only spitting out containers for those ListViewItems that were visible (or almost visible)..
And because the CheckBox is inside a DataTemplate that is defined in the XAML it gets thrown away everytime it goes out of view or when the list is sorted.
I got around this by creating a separate list of CheckBoxes and using the actual CheckBoxes "click" event to change the state of the corresponding CheckBox in my list.. then made a little method to go change the state of all the visible CheckBoxes whenever the user scrolls... as a result it appears like it should have in the first place.
Except when I sort the columns.
I tried making it re-do the CheckBoxes (like before) right after it'd sorted a column but it didn't work.
My best guess is that it doesn't immediately make the containers after I sort..
Is there any way to tell when the containers are finished being made for a ListView?
If you bind your checkboxes IsChecked property to a boolean property on your data context, then you will not have this issue.
The whole purpose of the VirtualizingStackPanel is reduce memory usage by not creating ListItem's unless needed.
In effect, you need to move the data side of the checkbox away from the control.
I have got an ObservableCollection<Button> buttonList; (this can be any other control: TextBox, TextBlock or even a UserControl).
I want to bind this List via DataBinding to a parent control (lets say a Grid or a WrapPanel,...)
Is this somehow possible? Children property is read only.
I do not want to do this in the program code -> Dont iterate through the list and add every item to the children property.
I want to BIND the List to a parent control -> the View is automatically updated, every time I add a new Item to the List.
Any Ideas?
Use ItemsControl, and bind your collection to ItemsSource.
And don't forget to read Dr. WPF's articles: ItemsControl: A to Z. This is second time I'm mentioning this blog today here, but he really-really deserves to be mentioned in every post related to collection of items and bindings :).
Cheers, Anvaka.
I wrote a custom Silverlight 3 control that uses a class as its data context (MVVM pattern). I want to place this control on another control (form) through XAML. The child control exposes a Dependency Property that when set through XAML, will make it show detailed info.
So an example is that the child control shows order details data, and I want to place it on a form that show user orders. When you select an order, the selected item value on the parent control (orders list), is data bound to the child control, to show details.
The problem is that the child control’s dependency property's OnChanged handler never gets called. If I do not set a data context on the child (so it uses the parent's data context) all works fine, but when I set a different data context, it breaks down.
Ideally, your ViewModel would be for the outer UserControl and a property on the ViewModel will be the DataContext of the Inner/Child userControl
Its true that when the Parent control's DataContext is set, it is propogated down to all its child controls. But the child control has an option of overriding this behavior by setting its own DataContext (which you seem to be doing in your example). Hence by the rule of preferences, the child control's DataContext is given more preference and thus it overrides the parent's one. Also since the child's DataContext never changes after it's initially set, the DP never gets invoked.
So I thought about this some more, and I understand what is happening, but I think its very confusing, and is not done right. If I am doing data binding on a control in the main page, it should use the context of that page to do the binding. And binding I do inside the control should use the control's context.
The way it works now uses the control's context no matter where I put the binding expression (unless I am doing E2E binding, then its using the main page's context). That is silly to me. But at least I understand it now.
I solved the problem using Element to Element binding, and got it to work. I hope the SL team would change this behavior.