Hello everyone,
We were having issues with the 'ScrollItemIntoViewAsync' method.
When virtualization is turned on, none of the proposed solutions found on the internet worked (or not atleast a 100%).
E.g. when I'd randomly select an item from my entire collection (hierarchy depth could easily go down 30 levels or more) it wouldn't always correctly scroll to that row.
However, we managed to come up with a pretty straight forward solution which could be implemented by pretty much anyone. (And hopefully, internally by Telerik inside the grid controls as well)
The idea is that the root items in a hierarchical grid are always present, and we found that ScrollIntoViewAsync always worked when trying to scroll to root items.
So we'd first scroll to the root item, then to the next item inside the root item's collection and so on and so on untill we reached the actual item we were supposed to scroll to.
Prerequistes:
1.) The row item's view model must implement a specific interface (we called it IScrollableGridItem) that contains a "GetScrollList()" function which returns a list of objects that contains all the parents of that item and itself, in reversed order (eg the root item will be the first in the list and the actual item is the last)
2.) The row item's view model must have a reference to its parent item (to basically make the "GetScrollList()" work)
3.) Row height should be set on the grid (it doesn't seem to always work with no specified row height)
We made a custom behavior that allowed us to have a SelectedItem at any given time, regardless of the grid SelectionMode & SelectionUnit.
Inside this behavior, when the SelectedItem was being set via the ViewModel we added the following code:
And for the sake of completeness, here's the code for the "GetParents()" implementation:
We were having issues with the 'ScrollItemIntoViewAsync' method.
When virtualization is turned on, none of the proposed solutions found on the internet worked (or not atleast a 100%).
E.g. when I'd randomly select an item from my entire collection (hierarchy depth could easily go down 30 levels or more) it wouldn't always correctly scroll to that row.
However, we managed to come up with a pretty straight forward solution which could be implemented by pretty much anyone. (And hopefully, internally by Telerik inside the grid controls as well)
The idea is that the root items in a hierarchical grid are always present, and we found that ScrollIntoViewAsync always worked when trying to scroll to root items.
So we'd first scroll to the root item, then to the next item inside the root item's collection and so on and so on untill we reached the actual item we were supposed to scroll to.
Prerequistes:
1.) The row item's view model must implement a specific interface (we called it IScrollableGridItem) that contains a "GetScrollList()" function which returns a list of objects that contains all the parents of that item and itself, in reversed order (eg the root item will be the first in the list and the actual item is the last)
2.) The row item's view model must have a reference to its parent item (to basically make the "GetScrollList()" work)
3.) Row height should be set on the grid (it doesn't seem to always work with no specified row height)
We made a custom behavior that allowed us to have a SelectedItem at any given time, regardless of the grid SelectionMode & SelectionUnit.
Inside this behavior, when the SelectedItem was being set via the ViewModel we added the following code:
001.
private
object
actualScrollToItem =
null
;
002.
private
object
currentScrollToItem =
null
;
003.
private
List<
object
> scrollItems =
null
;
004.
private
int
scrollToItemAttemptCount = 0;
005.
006.
private
void
SelectedItemTargetUpdated(
object
sender, DataTransferEventArgs e)
007.
{
008.
var item = GetValue(e.Property);
009.
010.
/* SNIP IRRELEVANT CODE TO SCROLLING */
011.
012.
actualScrollToItem = item;
013.
014.
if
(actualScrollToItem
is
IScrollableGridItem)
015.
{
016.
Console.WriteLine(
"-- BEGIN SCROLLING TO {0} --"
, actualScrollToItem.GetHashCode());
017.
018.
var tmp = item
as
IScrollableGridItem;
019.
020.
// Get a list of items to scroll to
021.
scrollItems = tmp.GetScrollList();
022.
023.
// Start scrolling to the first item
024.
currentScrollToItem = scrollItems.First();
025.
026.
GridControl.ScrollIntoViewAsync(currentScrollToItem, Scroll, ScrollFailed);
027.
028.
}
029.
else
030.
{
031.
// Attempt is not IScrollable, attempt to scroll anyway
032.
currentScrollToItem = actualScrollToItem;
033.
Console.WriteLine(
"Item is not IScrollable"
);
034.
GridControl.ScrollIntoViewAsync(actualScrollToItem, Scroll, ScrollFailed);
035.
}
036.
}
037.
038.
private
void
Scroll(FrameworkElement arg)
039.
{
040.
Console.WriteLine(
"Succesfully scrolled to {0}"
, currentScrollToItem.GetHashCode());
041.
042.
scrollToItemAttemptCount = 0;
043.
044.
var row = arg
as
GridViewRow;
045.
046.
if
(actualScrollToItem == currentScrollToItem)
047.
{
048.
Console.WriteLine(
"Reached actual scroll item"
);
049.
Console.WriteLine(
"-- END SCROLLING TO {0} --"
, actualScrollToItem.GetHashCode());
050.
GridControl.ClearSelection();
051.
GridControl.SelectedItems.Add(currentScrollToItem);
052.
053.
actualScrollToItem =
null
;
054.
currentScrollToItem =
null
;
055.
scrollItems =
null
;
056.
057.
if
(row !=
null
)
058.
{
059.
row.IsCurrent =
true
;
060.
row.Focus();
061.
row.BringIntoView();
062.
}
063.
}
064.
else
065.
{
066.
if
(row !=
null
&& row.IsExpandable)
067.
{
068.
row.IsExpanded =
true
;
069.
}
070.
071.
var idx = scrollItems.IndexOf(currentScrollToItem);
072.
currentScrollToItem = scrollItems[idx + 1];
073.
074.
GridControl.ScrollIntoViewAsync(currentScrollToItem, Scroll, ScrollFailed);
075.
}
076.
077.
}
078.
079.
private
void
ScrollFailed()
080.
{
081.
scrollToItemAttemptCount++;
082.
083.
Console.WriteLine(
"Failed to scroll to {0}. Already tried {1} times"
, currentScrollToItem.GetHashCode(), scrollToItemAttemptCount);
084.
085.
if
(currentScrollToItem ==
null
)
086.
{
087.
Console.WriteLine(
"Stopped attempting to scroll because currentScrollToItem is NULL"
);
088.
scrollToItemAttemptCount = 0;
089.
return
;
090.
}
091.
092.
if
(scrollToItemAttemptCount < 5)
093.
{
094.
GridControl.ScrollIntoViewAsync(currentScrollToItem, Scroll, ScrollFailed);
095.
}
096.
else
097.
{
098.
currentScrollToItem =
null
;
099.
actualScrollToItem =
null
;
100.
scrollToItemAttemptCount = 0;
101.
}
102.
103.
}
And for the sake of completeness, here's the code for the "GetParents()" implementation:
01.
public
List<
object
> GetParents()
02.
{
03.
List<
object
> result =
new
List<
object
>();
04.
05.
var tmp = Parent;
06.
07.
// Get all my parents
08.
while
(tmp !=
null
)
09.
{
10.
result.Add(tmp);
11.
12.
tmp = tmp.Parent;
13.
}
14.
15.
// Add myself to the list
16.
result.Add(
this
);
17.
18.
// We want the root item to be first in the list and myself last
19.
result.Reverse();
20.
21.
return
result;
22.
}