Hi,
I'm trying to replicate the way Windows Explorer works when you expand a node.
If you expand a node with child items, and those childred expand beyond the view, the view scrolls so that as many of the child nodes as possible are displayed.
Currently I have this code, which works the first time a node is expanded, but stops to work after contracting and expanding.
Is there a better way to implement this behavior?
I'm trying to replicate the way Windows Explorer works when you expand a node.
If you expand a node with child items, and those childred expand beyond the view, the view scrolls so that as many of the child nodes as possible are displayed.
Currently I have this code, which works the first time a node is expanded, but stops to work after contracting and expanding.
private void Tree_OnExpanded(object sender, RadRoutedEventArgs e)
{
var originalSource = e.OriginalSource
as
RadTreeViewItem;
new
Thread(() =>
{
while
(originalSource.ItemContainerGenerator.ContainerFromIndex(0) ==
null
)
Thread.Sleep(100);
Dispatcher.BeginInvoke(
new
Action(() =>
{
tree.BringIntoViewMode = BringIntoViewMode.HeaderAndItems;
originalSource.BringIntoView();
tree.BringIntoViewMode = BringIntoViewMode.Header;
}));
}).Start();
Is there a better way to implement this behavior?
4 Answers, 1 is accepted
0
Hello Aksel,
Please accept my apology for the delayed response.
In order to bring the expanded item to the top of the RadTreeView and display as many as possible of its children as possible, you can get the coordinates of the expanded item and scroll the RadTreeView.ScrollViewer to display this coordinates at the top. In order to do so, you'll need to transform the coordinates of the expanded RadTreeViewItem into the RadTreeView coordinate system:
and scroll to it.
I attached a sample solution demonstrating this approach. However, please note that I have disabled the RadTreeView animations as they delay the RadTreeView.ScrollViewer calculations. When you turn them on, the ScrollToVerticalOffset() logic will be executed before the ScrollViewer can fully calculate the real Height of the RadTreeView as the animations delay the realization of the RadTreeViewItem containers.
This is why if you have to turn the animations on, it's best to create custom animations and attach a behavior that will fire the tree.ScrollViewer.ScrollToVerticalOffset() logic after the animations are over. And in this case you should consider applying an animation for the scrolling logic as well to make it more fluent.
All the best,
Tina Stancheva
the Telerik team
Please accept my apology for the delayed response.
In order to bring the expanded item to the top of the RadTreeView and display as many as possible of its children as possible, you can get the coordinates of the expanded item and scroll the RadTreeView.ScrollViewer to display this coordinates at the top. In order to do so, you'll need to transform the coordinates of the expanded RadTreeViewItem into the RadTreeView coordinate system:
Point p = item.TransformToVisual(tree).Transform(
new
Point(0, 0));
tree.ScrollViewer.ScrollToVerticalOffset(tree.ScrollViewer.VerticalOffset + p.Y);
I attached a sample solution demonstrating this approach. However, please note that I have disabled the RadTreeView animations as they delay the RadTreeView.ScrollViewer calculations. When you turn them on, the ScrollToVerticalOffset() logic will be executed before the ScrollViewer can fully calculate the real Height of the RadTreeView as the animations delay the realization of the RadTreeViewItem containers.
This is why if you have to turn the animations on, it's best to create custom animations and attach a behavior that will fire the tree.ScrollViewer.ScrollToVerticalOffset() logic after the animations are over. And in this case you should consider applying an animation for the scrolling logic as well to make it more fluent.
All the best,
Tina Stancheva
the Telerik team
Explore the entire Telerik portfolio by downloading Telerik DevCraft Ultimate.
0
Aksel
Top achievements
Rank 1
answered on 30 Jan 2013, 03:43 PM
I don't want the item to go to the top if it has room to expand.
Basically, I want the following to happen;
- If the last child item is visible, don't do anything.
- If the last child item is hidden, scroll down until the item is visible or until the parent is in the top position.
See the attached images
scroll1: We want to expand Item 5.
scroll2: It only has 5 children, so the last item is visible. No scroll is done.
scroll3: Now we expand Item 5.0, which has 20 children. It scrolls so far down it can, but stops when the parent is in the top position.
scroll4: Now if Item 5.0 only had 14 items, it would only scroll so far down that item 14 is visible, no further.
How should I implement this?
Basically, I want the following to happen;
- If the last child item is visible, don't do anything.
- If the last child item is hidden, scroll down until the item is visible or until the parent is in the top position.
See the attached images
scroll1: We want to expand Item 5.
scroll2: It only has 5 children, so the last item is visible. No scroll is done.
scroll3: Now we expand Item 5.0, which has 20 children. It scrolls so far down it can, but stops when the parent is in the top position.
scroll4: Now if Item 5.0 only had 14 items, it would only scroll so far down that item 14 is visible, no further.
How should I implement this?
0
Aksel
Top achievements
Rank 1
answered on 31 Jan 2013, 10:00 AM
I have solved it for now:
This code waits untill the ItemContainerGenerator is done generating the child items, before displaying the items. For some reason, it did not work to have the BringIntoView() in the StatusChanged event. I had to split it into a separate thread.
private
void
Tree_OnExpanded(
object
sender, RadRoutedEventArgs e)
{
var item = e.OriginalSource
as
RadTreeViewItem;
if
(item ==
null
)
return
;
var itenContainerWaiter =
new
ManualResetEvent(
false
);
EventHandler handler =
null
;
handler = (o, args) =>
{
var itemContainerGenerator = o
as
ItemContainerGenerator;
if
(itemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
return
;
itemContainerGenerator.StatusChanged -= handler;
itenContainerWaiter.Set();
};
item.ItemContainerGenerator.StatusChanged += handler;
new
Thread(() =>
{
itenContainerWaiter.WaitOne();
Dispatcher.BeginInvoke(
new
Action(() =>
{
tree.BringIntoViewMode = BringIntoViewMode.HeaderAndItems;
item.BringIntoView();
tree.BringIntoViewMode = BringIntoViewMode.Header;
}));
}).Start();
}
This code waits untill the ItemContainerGenerator is done generating the child items, before displaying the items. For some reason, it did not work to have the BringIntoView() in the StatusChanged event. I had to split it into a separate thread.
0
Hi Aksel,
The ItemContainerGenerator.StatusChanged event is fired multiple times with a ContainerGenerator status and if you try too early to bring the item into view, it might not work as expected. I believe this is the reason why in your case moving the logic in another thread that is executed later works better.
Another approach that you can try is to use the RadTreeView.ItemPrepared event. It is fired for each RadTreeViewItem container after it is fully generated and initialized. In the handler of the event you can bring the item into view:
All the best,
Tina Stancheva
the Telerik team
The ItemContainerGenerator.StatusChanged event is fired multiple times with a ContainerGenerator status and if you try too early to bring the item into view, it might not work as expected. I believe this is the reason why in your case moving the logic in another thread that is executed later works better.
Another approach that you can try is to use the RadTreeView.ItemPrepared event. It is fired for each RadTreeViewItem container after it is fully generated and initialized. In the handler of the event you can bring the item into view:
private
void
RadTreeView_ItemPrepared(
object
sender, RadTreeViewItemPreparedEventArgs e)
{
if
(e.PreparedItem.ParentItem!=
null
&& e.PreparedItem.ParentItem.IsExpanded)
{
(sender
as
RadTreeView).BringIntoViewMode = BringIntoViewMode.HeaderAndItems;
e.PreparedItem.BringIntoView();
(sender
as
RadTreeView).BringIntoViewMode = BringIntoViewMode.Header;
expandStarted =
false
;
}
}
All the best,
Tina Stancheva
the Telerik team
Explore the entire Telerik portfolio by downloading Telerik DevCraft Ultimate.