Как заставить прокрутку Silverlight ScrollViewer показывать дочерний элемент управления с фокусом?
У меня есть ScrollViewer, который содержит Grid с несколькими элементами управления в нем. Пользователь может выполнить вкладку с помощью элементов управления, но в конце концов они переходят к элементу управления, который не отображается, поэтому они должны выполнить manully прокрутку, чтобы снова получить контроль.
Можно ли прокручивать прокрутку ScrollViewer автоматически, чтобы фокусное управление всегда было видимым. В противном случае я могу сделать эту работу, не слушая событие GotFocus на каждом элементе управления, а затем прокручивая ScrollViewer, чтобы сделать видимым элемент управления?
В настоящее время я использую Silverlight 2.
Ответы
Ответ 1
Я тестировал это с помощью Silverlight 3. Я не уверен в SL2.
Это мой XAML:
<ScrollViewer Height="200" Width="200" KeyUp="ScrollViewer_KeyUp">
<StackPanel>
<Button Content="1" Height="20" />
<Button Content="2" Height="20" />
<Button Content="3" Height="20" />
<Button Content="4" Height="20" />
<Button Content="5" Height="20" />
<Button Content="6" Height="20" />
<Button Content="7" Height="20" />
<Button Content="8" Height="20" />
<Button Content="9" Height="20" />
<Button Content="10" Height="20" />
<Button Content="11" Height="20" />
<Button Content="12" Height="20" />
<Button Content="13" Height="20" />
<Button Content="14" Height="20" />
<Button Content="15" Height="20" />
<Button Content="16" Height="20" />
<Button Content="17" Height="20" />
<Button Content="18" Height="20" />
<Button Content="19" Height="20" />
<Button Content="20" Height="20" />
</StackPanel>
</ScrollViewer>
И это код:
private void ScrollViewer_KeyUp(object sender, KeyEventArgs e)
{
ScrollViewer scrollViewer = sender as ScrollViewer;
FrameworkElement focusedElement = FocusManager.GetFocusedElement() as FrameworkElement;
GeneralTransform focusedVisualTransform = focusedElement.TransformToVisual(scrollViewer);
Rect rectangle = focusedVisualTransform.TransformBounds(new Rect(new Point(focusedElement.Margin.Left, focusedElement.Margin.Top), focusedElement.RenderSize));
double newOffset = scrollViewer.VerticalOffset + (rectangle.Bottom - scrollViewer.ViewportHeight);
scrollViewer.ScrollToVerticalOffset(newOffset);
}
То, что я сделал, это нажать кнопку "# 1" и вкладку, пока я не получу кнопку №20. Это сработало для меня. Попробуйте и дайте мне знать, как это работает для вас.
Ответ 2
Набор инструментов Silverlight содержит метод "ScrollIntoView".
Добавьте ссылку на System.Windows.Controls.Toolkit.dll и вы сможете использовать код ниже.
scrollViewer.ScrollIntoView(control);
Ответ 3
Просто небольшое улучшение. Еще нужно сделать это для Silverlight 4.
Вместо GotFocus для каждого элемента управления вы можете обрабатывать GotFocus самого scrollviewer и реализовывать его только один раз.
private void _ScrollViewer_GotFocus(object sender, RoutedEventArgs e)
{
FrameworkElement element = e.OriginalSource as FrameworkElement;
if (element != null)
{
ScrollViewer scrollViewer = sender as ScrollViewer;
scrollViewer.ScrollToVerticalOffset(GetVerticalOffset(element, scrollViewer));
}
}
private double GetVerticalOffset(FrameworkElement child, ScrollViewer scrollViewer)
{
// Ensure the control is scrolled into view in the ScrollViewer.
GeneralTransform focusedVisualTransform = child.TransformToVisual(scrollViewer);
Point topLeft = focusedVisualTransform.Transform(new Point(child.Margin.Left, child.Margin.Top));
Rect rectangle = new Rect(topLeft, child.RenderSize);
double newOffset = scrollViewer.VerticalOffset + (rectangle.Bottom - scrollViewer.ViewportHeight);
return newOffset < 0 ? 0 : newOffset; // no use returning negative offset
}
Ответ 4
Я получил это, чтобы работать, с помощью ответа Кирилла выше. Общий контекст этого заключается в том, что у меня есть пользовательские формы в моем приложении, и этот код используется для визуализации элементов управления в форме.
Моя общая стратегия заключалась в том, чтобы добавить элементы управления в Grid, а затем найти все дочерние элементы ScrollViewer с помощью VisualTreeHelper и добавить обработчик событий GotFocus для каждого элемента управления.
Когда элемент управления получает фокус, снова используя VisualTreeHelper, я просматриваю визуальное дерево, чтобы найти элемент управления, родитель которого является сеткой, которая прокручивается ScrollViewer. Затем я прокручиваю ScrollViewer, чтобы сделать видимым элемент управления.
Здесь код (gridRender - это сетка, в которую добавлены элементы управления):
private void AfterFormRendered()
{
var controls = VisualTreeHelperUtil.FindChildren<Control>(gridRender);
foreach (var ctrl in controls)
{
ctrl.GotFocus += CtrlGotFocus;
}
}
private void CtrlGotFocus(object sender, RoutedEventArgs e)
{
var ctrl = sender as Control;
var gridChildControl = VisualTreeHelperUtil.FindParentWithParent(ctrl, gridRender) as FrameworkElement;
if (gridChildControl != null)
{
// Ensure the control is scrolled into view in the ScrollViewer.
GeneralTransform focusedVisualTransform = gridChildControl.TransformToVisual(scrollViewer);
Point topLeft = focusedVisualTransform.Transform(new Point(gridChildControl.Margin.Left, gridChildControl.Margin.Top));
Rect rectangle = new Rect(topLeft, gridChildControl.RenderSize);
double newOffset = scrollViewer.VerticalOffset + (rectangle.Bottom - scrollViewer.ViewportHeight);
scrollViewer.ScrollToVerticalOffset(newOffset);
}
}
Примечание. Класс VisualTreeHelperUtil - это мой собственный класс, который добавляет некоторые полезные функции поиска в класс VisualTreeHelper.