Reputation: 6365
<GridView
Name="Slider"
ItemsSource="{Binding Node.Contents}"
Loaded="SliderLoaded"
.../>
The ViewModel
initialization is async and that´s where I set the Node
property referenced in the binding.
This is the style for the items panel:
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<ItemsStackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
On code behind:
private async void SliderLoaded(object sender, RoutedEventArgs e)
{
// I get a null reference exception trying to access the GridView scrollViewer here
await Task.Delay(150); // [hack] wait a bit for the view tree and binding to be ready
// the scrollViewer is accessible after the delay
}
And this how I access the ScrollViewer:
public static ScrollViewer GetScrollViewer(this DependencyObject element)
{
if (element is ScrollViewer)
{
return (ScrollViewer)element;
}
for (var i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++)
{
var child = VisualTreeHelper.GetChild(element, i);
var result = GetScrollViewer(child);
if (result != null)
return result;
}
return null;
}
Reading docs and other SO answers, the "loaded" event handler seams like the place where all sub-views and bindings are created and available, but as you can see in the comments, it´s not working.
I also tried DataContextChanged
event handler with the same result.
How or when can I be 100% sure the grid scrollViewer and items are in place?
Upvotes: 0
Views: 755
Reputation: 6365
Sorry for the confusion. My code was working and I didn´t realize. Thing is that I had a function getting the ScrollView and other stuff to find items and do an animated scrolling. The ScrollView was there but other parts in the code wrapped in a TaskCompletionSource
were failing silently.
Anyway, the whole point of all of this was to programmatically scroll the GridView, and all the problems were gone when I replaced the ItemsStackPanel
with a StackPanel
in the ItemsPanelTemplate
.
Sorry to bother and thank you all.
Upvotes: 0
Reputation: 34286
You didn't specify how you are getting the GridView's ScrollViewer. I'm assuming you're using VisualTreeHelper to do so?
This works for me:
<GridView Loaded="GridView_Loaded"/>
private void GridView_Loaded(object sender, RoutedEventArgs e)
{
var scrollViewer = ((UIElement)sender)
.ChildrenBreadthFirst()
.OfType<ScrollViewer>()
.First();
}
public static class Extensions
{
public static IEnumerable<UIElement> ChildrenBreadthFirst(this UIElement element)
{
var queue = new Queue<UIElement>();
queue.Enqueue(element);
while (queue.Count > 0)
{
element = queue.Dequeue();
var count = VisualTreeHelper.GetChildrenCount(element);
for (var i = 0; i < count; i++)
{
var child = (UIElement)VisualTreeHelper.GetChild(element, i);
yield return child;
queue.Enqueue(child);
}
}
}
}
Upvotes: 1