// ****************************************************************** /// <summary> /// This method is asynchronous. /// Expand all items and subs recursively if any. Does support virtualization (item recycling). /// But honestly, make you a favor, make your life easier en create a model view around your hierarchy with /// a IsExpanded property for each node level and bind it to each TreeView node level. /// </summary> /// <param name="treeView"></param> /// <param name="actionItemExpanded"></param> /// <param name="actionAllItemExpanded"></param> public static void ExpandAll(this TreeView treeView, Action <TreeViewItem, object> actionItemExpanded = null, Action actionAllItemExpanded = null) { var referenceCounterTracker = new ReferenceCounterTracker(actionAllItemExpanded); referenceCounterTracker.AddRef(); treeView.Dispatcher.BeginInvoke(new Action(() => ExpandSubContainers(treeView, actionItemExpanded, referenceCounterTracker)), DispatcherPriority.Background); referenceCounterTracker.ReleaseRef(); }
// ****************************************************************** /// <summary> /// Expand any ItemsControl (TreeView, TreeViewItem, ListBox, ComboBox, ...) and their childs if any (TreeView) /// </summary> /// <param name="ic"></param> /// <param name="actionItemExpanded"></param> /// <param name="referenceCounterTracker"></param> public static void ExpandSubContainers(ItemsControl ic, Action <TreeViewItem, object> actionItemExpanded, ReferenceCounterTracker referenceCounterTracker) { ItemContainerGenerator icg = ic.ItemContainerGenerator; { if (icg.Status == GeneratorStatus.ContainersGenerated) { ExpandSubWithContainersGenerated(ic, actionItemExpanded, referenceCounterTracker); } else if (icg.Status == GeneratorStatus.NotStarted) { ActionHolder actionHolder = new ActionHolder(); EventHandler itemCreated = delegate(object sender, EventArgs eventArgs) { var icgSender = sender as ItemContainerGenerator; if (icgSender.Status == GeneratorStatus.ContainersGenerated) { ExpandSubWithContainersGenerated(ic, actionItemExpanded, referenceCounterTracker); // Never use the following method in BeginInvoke due to ICG recycling. The same icg could be // used and will keep more than one subscribers which is far from being intended // ic.Dispatcher.BeginInvoke(actionHolder.Action, DispatcherPriority.Background); // Very important to unsubscribe as soon we've done due to ICG recycling. actionHolder.Execute(); referenceCounterTracker.ReleaseRef(); } }; referenceCounterTracker.AddRef(); actionHolder.Action = new Action(() => icg.StatusChanged -= itemCreated); icg.StatusChanged += itemCreated; // Next block is only intended to protect against any race condition (I don't know if it is possible ? How Microsoft implemented it) // I mean the status changed before I subscribe to StatusChanged but after I made the check about its state. if (icg.Status == GeneratorStatus.ContainersGenerated) { ExpandSubWithContainersGenerated(ic, actionItemExpanded, referenceCounterTracker); } } } }