Silverlight TreeView - Load data when node is expanded - silverlight

Is there a way to only load child nodes when the parent node is expanded? The problem that I’m running into is that the “expand” icon doesn’t show up if a node doesn’t have any children. Since I don’t want to load the children until the icon is clicked, I’m left with a bit of a catch 22.

First, read this post:
http://bea.stollnitz.com/blog/?p=55
Second, inherit TreeViewItem and TreeView:
public class TreeViewItemEx : TreeViewItem {
protected override DependencyObject GetContainerForItemOverride() {
TreeViewItemEx tvi = new TreeViewItemEx();
Binding expandedBinding = new Binding("IsExpanded");
expandedBinding.Mode = BindingMode.TwoWay;
tvi.SetBinding(TreeViewItemEx.IsExpandedProperty, expandedBinding);
return tvi;
}
}
public class TreeViewEx : TreeView {
protected override DependencyObject GetContainerForItemOverride() {
TreeViewItemEx tvi = new TreeViewItemEx();
Binding expandedBinding = new Binding("IsExpanded");
expandedBinding.Mode = BindingMode.TwoWay;
tvi.SetBinding(TreeViewItemEx.IsExpandedProperty, expandedBinding);
return tvi;
}
}
Third, binding your Model's property to "IsExpanded".

With tree views you usually have to load the children of each displayed node.
So if you only display the root you need to load the roots children too. Once you expand the root you need to load the children of each child if you want the expand stuff for those children.

It is perfectly possible to have tree controls load the child nodes on demand, and you can do this with the Silverlight TreeView. When you populate the root nodes if the data for this is coming from a database for example then for each node also return whether it has children or not, if it does then add one dummy child, this will make the control put a + next to it. Handle the expanded event and in this see if the child is the dummy node, if it is remove it, get the children from the database and add them.

i was also looking at this. I think you need to write your own subclass of the TreeNode that loads the child nodes on demand.
One approach I used in a windows forms TreeView was to add an empty child node to each node and then remove this when the node was expanded and the real child nodes were needed. The problem with this approach is that you get false expandable nodes, but if you can live with it then its a simple solution.

Related

Is it possible to create a control once and have it generated everytime it is needed?

Say for example you have a stackpanel that you would like to programmatically add buttons to.
The code behind for the button generation and addition to the stackpanel is:
Button button = new Button();
button.Content = "Button";
button.HorizontalAlignment = HorizontalAlignment.Left;
button.Name = "Button" + i
stackPanel1.Children.Add(button);
My question is - Is it possible to generate the button once and have it as some kind of template that can be added to the stackpanel whenever it is needed without going through the generation code again?
In WPF each UIElement can only be the logical child of one control at any given time, see WPF Error: Specified element is already the logical child of another element. Disconnect it first, so no you can't use the same button and add it to another control later on, unless you're sure you've gotten rid of that stackpanel
But, you can do recycling. See Optimizing Performance: Controls. especially if you are willing to override MeasureOverride and ArrangeOverride of you stackpanel
I've actually written such a recycler because I had grids with many controls, and I wanted to implement some kind of virtualizing grid. These are the main methods of my class:
internal class Recycler
{
private UIElementCollection _children;
public Recycler(UIElementCollection children)
{
_children = children;
//You need this because you're not going to remove any
//UIElement from your control without calling this class
}
public void Recycle(UIElement uie)
{
//Keep this element for recycling, remove it from Children
}
public UIElement GiveMeAnElement(Type type)
{
//Return one of the elements you kept of this type, or
//if none remain create one using the default constructor
}
}

Execute custom code after command was executed

I have a TreeView control, and after a new node is added, I need to execute some custom code (ex expand the node, make it visible, and fire begind edit method). Since I want this to be available in every instance of TreeView control, I thought of subclassing the TreeView control.
Now, in order for treeview to know when the new node is added, it would either have an event that is fired when item is added (which it doesn't), or to have a reference to command that was executed to add a new item.
So two questions:
1) Is there a way to add an event in TreeView that would be fired whenever a treenode is added (I am always adding nodes through source collection from ViewModel) - I could not find any way to do this
2) I could add an AddCommand property to TreeList, that would be bound to ViewModel's AddCommand, and then have some button, or ContextMenu item that would bind to TreeList.AddCommand, instead of view model. This way TreeView would hold reference to AddCommand, but the drawback would be that actual usage would be kind of wierd. Question: How can I know when an TreeView's AddCommand (or any command, to that matter) is executed, so I can fire some custom code after it? It seems that CommandManager.AddExecutedHandler is a solution, but I am unable to execute it.
Is this any help?
public class CustomTreeControl : TreeView
{
...
// WPF only
protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
{
base.OnItemsSourceChanged(oldValue, newValue);
Debug.WriteLine("OnItemsSourceChanged");
}
// WPF + Silverlight
protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
{
base.OnItemsChanged(e);
Debug.WriteLine("OnItemsChanged: {0}", e.Action);
}
}

Unable to find container from item in Treeview

I got an ItemsControl (a Treeview or a TreevieItem) wich Item is filled with my own Model.
I wan't to programatically unfold the TreeView. So I try this:
var model = itemsControl_.Items.Select(i => i as MyModel).Where(ftn => ftn != null && ftn.key = searchedKey));
now that i have founded the model a want to unfold. I search for the container to unfold it:
var tvi = itemsControl_.ItemContainerGenerator.ContainerFromItem(model) as TreeViewItem;
if(tvi!=null)
{
if (!tvi.IsExpanded)
{
tvi.IsExpanded = true;
}
}
And sometimes tvi is null !?!
Can someone explain me how that can be possible ?
Based on your description, it seems you want to expand TreeViewItem.
I think this is most likely caused by the Virtualizating. The Virtualizating Attached Property is on by default for TreeView, which means if an item is not in the viewport, it probably didn't get an container.
If you call GetContainerFromItem on a TreeView, the ItemContainerGenerator searches only the direct child objects of the TreeView. So you need to recursively traverse the TreeView and child TreeViewItem objects. A further complication is that if the TreeView virtualizes its items (you enable virtualization by setting the VirtualizingStackPanel.IsVirtualizing property to true), the child items need to be created before you can check its data object.
Hope this will help.
Pls refer to http://blogs.msdn.com/b/wpfsdk/archive/2010/02/23/finding-an-object-treeviewitem.aspx
Regards
Dipak

Define Background Color of Alternating TreeView Rows Based on Visibility

Is there a way in WPF to define the background of alternating visible rows?
I've tried setting the AlternationCount property, but that restarts for every child node which gives a weird look.
Ideally what I would like is to know what the visual index of a given node is. Only expanded nodes are counted.
There was no easy way to do this as WPF creates nested containers for the tree nodes. So as Rachel mentioned, looping through the items seemed to be the way to go. But I didn't want to depart too much from the built in ItemsControl.AlternationIndex attached property as that is the one people would expect. Because it is readonly I had to access it via reflection, but after that things fell into place.
First off, make sure you handle the Loaded, Expanded and Collapsed events of your TreeViewItem. In the event handler find the owning TreeView and do a recursive alternation count set of all visible nodes. I created an extension method to handle it:
public static class AlternationExtensions
{
private static readonly MethodInfo SetAlternationIndexMethod;
static AlternationExtensions()
{
SetAlternationIndexMethod = typeof(ItemsControl).GetMethod(
"SetAlternationIndex", BindingFlags.Static | BindingFlags.NonPublic);
}
public static int SetAlternationIndexRecursively(this ItemsControl control, int firstAlternationIndex)
{
var alternationCount = control.AlternationCount;
if (alternationCount == 0)
{
return 0;
}
foreach (var item in control.Items)
{
var container = control.ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem;
if (container != null)
{
var nextAlternation = firstAlternationIndex++ % alternationCount;
SetAlternationIndexMethod.Invoke(null, new object[] { container, nextAlternation });
if (container.IsExpanded)
{
firstAlternationIndex = SetAlternationIndexRecursively(container, firstAlternationIndex);
}
}
}
return firstAlternationIndex;
}
}
As you can see it runs through each node and sets the custom alternation index. It checks if the node is expanded and if so continues the count on the child nodes.
Above I mentioned that you have to handle the Loaded event for the TreeViewItem. If you only handle the expanded and collapsed events you won't get the new containers that are created when a node is first opened. So you have to do a new pass when the child node has been created and added to the visual tree.
Something I've done with javascript is create an OnLoaded event for a table which loops through the table rows and if the row is visible, it sets the background color to a nextColor variable, and changes the nextColor variable to the opposite color. That might work here.

C# WPF - Adding a child node to a selected node in treeview

In WPF treeview control, I need to add a child node to a parent node i select using mousedoubleclick event.
http://msdn.microsoft.com/en-us/library/system.windows.controls.treeview.selecteditem.aspx
I followed the step in the MSDN, but i get invalidCastException when i do this.
TreeViewItem newChild =
(TreeViewItem)treeView1.SelectedItem;
How can i solve this?
Thanks
SelectedItem returns the selected data item, not the visual representing it.
If you need to access the selected TreeViewItem, use the ItemContainerGenerator :
TreeViewItem item = treeView1.ItemContainerGenerator.ContainerFromItem(treeView1.SelectedItem) as TreeViewItem;
Not sure it works for nested items though... you might have to use the ItemContainerGenerator of the parent TreeViewItem, which wouldn't be very convenient
EDIT: just tested, indeed it only works for root nodes...
Anyway, the best way to add a node is to use bindings and HierarchicalDataTemplates. You just need to add the object to the data source, and the corresponding TreeViewItem will be added automatically (provided the containing collection implements INotifyCollectionChanged...)
What type of Items do you Add() to the Tree? The same type will be returned.
If it is mixed, use
TreeViewItem newChild = treeView1.SelectedItem as TreeViewItem;
if (newChild != null) { ... }

Resources