SL3 TreeView Not Programmatically Scrolling - silverlight

I have a treeview in Silverlight 3.
The treeview is bound to a observable collection - which contains a list of hierarchical data.
When the page loads initially, all nodes, by default, in the treeview, are collapsed.
I have functionality that allows a certain item in the treeview to be selected programmatically.
The problem that I am running into is when an item is selected that isn't immediately visible (i.e. one or more parent nodes are collapsed). I programmatically expand them, but, when I try to programmatically scroll into view, so the user can see the selected item, it doesn't work.
I looked into this further, and I believe that it has to do with the calculated viewport height for the scroll viewer. It almost seems like a timing issue, as, if the item's parent node is expanded, and then the item is programmatically selected, the code that scrolls the treeview into view for that selected treeview item works perfectly.
Please refer to the extension method below that I am using to scroll the treeview into view. Any help or suggestions on how to correct this would be greatly appreciated.
Thanks.
public static void BringIntoViewForScrollViewer(this FrameworkElement frameworkElement, ScrollViewer scrollViewer)
{
var transform = frameworkElement.TransformToVisual(scrollViewer);
var positionInScrollViewer = transform.Transform(new Point(0, 0));
if (positionInScrollViewer.Y < 0 || positionInScrollViewer.Y > scrollViewer.ViewportHeight)
scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset + positionInScrollViewer.Y - ScrollPadding);
}

Call UpdateLayout on the TreeView or ScrollViewer in between expanding the nodes and calling your extension method to ensure the VerticalOffset and ViewportHeight properties are up to date.
(answer copied from comment by Dan Auclair that led to problem resolution)

Related

How to force ItemContainerGenerator to generate container for the item or How to scroll TreeView to expanded node when UI virtualization is enabled?

TreeView doesn't have ScrollIntoView() method
The only way is to call TreeVewItem.BringIntoView() for the corresponding data item container.
But if node is invisible and no container is generated yet, ItemsControl.ItemContainerGenerator.ContainerFromItem() will return null.
So there should be some way to force ItemContainerGenerator to create container for the item.
The reasonable question is: How can node be expanded and stay invisible?!
Easy! IsExpanded is bound to VM's property. And UI virtualization works as expected:
Event hanlder for TreeViewItem.Expanded was called ony when manual scrolling to the item was done.
I can't guarantee this issue is similar enough to help, but I thought since I couldn't find a good answer for my own issue I would post here as this is similar and I figured out how to work around my issue.
I am working with a Canvas control and have complex UI elements that are placed on that canvas and have ItemsControl controls as a part of their XAML UI definitions. The ItemsControl's have defined DataTemplates set in their ItemTemplates.
Because of this, certain aspects of my objects will only fully generate once the Visual Tree has been updated. This isn't an issue with dragging and dropping and working with the items as the canvas is being worked on because the ItemContainerGenerator has already generated the items when I need them. But it is an issue when trying to regenerate the items from the database at Canvas load time before the visual tree has drawn itself.
I found the only real way to get around this problem was to only start working on placing "secondary objects" on the canvas (objects that tie into objects that the ItemContainerGenerators make) once the LayoutUpdated event for the canvas has been fired.
public class DesignerCanvas : Canvas
{
public void LoadCanvasFromDB()
{
...
[loading items from the database, part one]
LayoutUpdated += DesignerCanvas_LayoutUpdated;
}
void DesignerCanvas_LayoutUpdated(object sender, EventArgs e)
{
LayoutUpdated -= DesignerCanvas_LayoutUpdated;
[loading items from the database which tie to UI elements from part one]

Scroll Treeview to Bottom automatically in WPF

I got a treeview with a bunch of nodes where more gets added over time. When I add a new node to the treeview, I need to make sure that the very bottom most node is visible.
I tried using the ItemContainerGenerator to select the last item and bring it into view. But it doesn't work for me.
How can I make my treeview scroll to the very last item?
If you are working with TreeViewItem in code, I think you can use treeViewItem.BringIntoView() which should cause the scrollable area the item is in to scroll so that it is visible.
Edits
Examples
view.SchemaUpdateGrid.ScrollIntoView(e.UserState);
The above like is from a BackgroundWorker.ProgressChanged event handler. e.UserState contains the currently working object (custom class), not a grid row. The grid automatically translates the object into the item.

Preserving a bound ListBox's scroll position when the underlying collection changes

I have a ScrollViewer and a ListBox inside it which is bound to an ObservableCollection in the view model. The ScrollViewer is maximized to take up all available space of the parent container. I'm finding that when the collection is modified and ends up producing more ListBoxItems than can fit in the viewable area of the ScrollViewer, the ScrollViewer scrolls down to show the last item in the ListBox. How do I prevent the ScrollViewer from scrolling when the child ListBox's items are updated?
I would like the scroll position to stay intact whenever the collection in the view model is updated.
Thanks in advance!
You are going to have to manage this yourself. The ListBox has a ScrollIntoView method that allows you to scroll to a specific location:
http://msdn.microsoft.com/en-us/library/system.windows.controls.listbox.scrollintoview(v=VS.95).aspx
Determining the items that are currently visible, if you need this, is not so easy. See the ItemsControlExtensions that I wrote as part of the WP7Contrib project:
http://wp7contrib.codeplex.com/SourceControl/changeset/view/67473#1475881
This has a GetItemsInView extensions method that will provide the list of visible items.

How to invalidate layout of listbox from custom children

I have a custom panel for a listbox
<ItemsPanelTemplate x:Key="FloatPanelTemplate">
<Controls:FloatPanel x:Name="CardPanel" />
</ItemsPanelTemplate>
The panel lays out its children using its X and Y dependency properties.
This all works nicely when the FloatPanel is used by itself - I'm using FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure on the dependency properties of the child items to tell the FloatPanel to redraw its layout.
When I use it in a Listbox (code above) then it draws fine the first time, but when I drag the children (which modifies the item's X and Y) it is not notifying the Listbox that it needs to redraw the FloatPanel's children. I think the issue is related to the fact that each item in the bound collection is wrapped with a ListBoxItem.
Hopefully I've described what i'm doing well enough that someone can tell me how to make the panel (or its children) tell it needs to do the Layout routines again. As I said it works once (initial draw) but then dragging items doesn't work (Listbox isnt aware that its children have changed and needs to relayout.) If I drag an item and then resize the window, the listbox does a layout and the items are drawn in their new locations.
How do I notify the ListBox (or more importantly the FloatPanel in the ItemsPanelTemplate) that it needs to do a Layout pass?
Instead try FrameworkPropertyMetadataOptions.AffectsParentMeasure and FrameworkPropertyMetadataOptions.AffectsParentArrange.
Those names... Thank God for intellisense, huh?
As you've noted, since ListBoxItem is your element's immediate layout parent, changes to the dependency properties that affect the parent's layout will not be "seen" by the panel which is further up the visual tree.
So instead what you may need to do unfortunately is to traverse the visual tree until you find an element deriving from Panel and call its InvalidateArrange method.
DependencyObject obj=this;
while ( (obj=VisualTreeHelper.GetParent(obj)) != null) {
Panel p = obj as Panel;
if (p != null) {
p.InvalidateArrange();
break;
}
}
It's ugly, but maybe a WPF guru will have a better suggestion.
Are you sure that your ListBox or ListView panel isn't seeming to present an incorrect size because it has...
ScrollViewer.CanContentScroll="True"
...which is the default value?
When list control is in this mode, and when it is scrolled to the bottom, there may be extra whitespace at the bottom of its panel in order to make sure that an integral item is lined up at the top of its internal ScrollViewer, and this might seem like the ScrollViewer--and hence the list control--is not resizing itself.
To prevent whitespace at the bottom of the panel, you must enable pixel-wise scrolling on the ListBox or ListView control:
ScrollViewer.CanContentScroll="False"
or in code:
ScrollViewer.SetCanContentScroll(list_ctrl, false);
See also: WPF ListView non integral scrolling

Sliding effect when adding new items to a WPF ListBox

I have a WPF ListBox control which displays items of an RSS feed. I occasionally check the source of the RSS feed for new items. Once I detect a new item I add it to the observable collection which immediately adds the new item to the ListBox display.
Is there a way to 'slide in' the new item from the top, pushing down the existing items? How would I achieve such an effect? Can it be done with a ListBox, or do I need to resort to my own container, such as a StackPanel and animate for example the Height of newly added controls programmatically?
I just posted an answer to this question which is very similar to yours.
WPF how to animate a list of components
It can be done with a ListBox. Use the ItemContainerStyle to style the ListBoxItems that the binding creates for you: this style can include animations, e.g. by adding an EventTrigger for the Loaded event to the Style.Triggers, and transforms. For example, in your trigger action you could animate the Height so the item expands into place, or if the height is unknown you could have your style set a ScaleTransform and in your trigger action animate the ScaleY of that transform from 0 to 1.

Resources