First, name your scroll viewer ScrollViewer
. After that add the GotFocus
and LostFocus
event handlers for each text box control on the page and write the following code inside:
private void txt_LostFocus(object sender, RoutedEventArgs routedEventArgs)
{
ScrollViewer.Height = _oldHeight;
}
void txt_GotFocus(object sender, RoutedEventArgs e)
{
var transform = ((Application.Current).RootVisual).RenderTransform as TransformGroup;
if (transform != null)
{
_translateTransform = transform.Children.OfType<TranslateTransform>().FirstOrDefault();
if (_translateTransform != null)
{
var binding = new Binding("Y")
{
Source = _translateTransform
};
BindingOperations.SetBinding(this, TranslateYProperty, binding);
}
}
var clipboardVisible = false;
try
{
clipboardVisible = Clipboard.ContainsText();
}
// ReSharper disable once EmptyGeneralCatchClause
catch
{
}
ScrollViewer.Height = _oldHeight - (clipboardVisible ? 407 : 338);
}
You will need to add the following dependency property to the page:
#region TranslateY dependency property
public static readonly DependencyProperty TranslateYProperty = DependencyProperty.Register(
"TranslateYProperty", typeof(double), typeof(OrderContactPage), new PropertyMetadata(default(double), PropertyChangedCallback));
private static void PropertyChangedCallback(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
((OrderContactPage)o)._translateTransform.Y = 0;
}
public double TranslateY
{
get { return (double)GetValue(TranslateYProperty); }
set { SetValue(TranslateYProperty, value); }
}
#endregion
And also helper fields:
private double _oldHeight;
private TranslateTransform _translateTransform;
You also need to handle some events for your scroll viewer, add this to the page's constructor:
ScrollViewer.Loaded += ScrollViewerOnLoaded;
ScrollViewer.SizeChanged += ScrollViewer_OnSizeChanged;
Implement those event handlers:
private void ScrollViewerOnLoaded(object sender, RoutedEventArgs routedEventArgs)
{
ScrollViewer.Loaded -= ScrollViewerOnLoaded;
_oldHeight = ScrollViewer.ActualHeight;
}
private async void ScrollViewer_OnSizeChanged(object sender, SizeChangedEventArgs e)
{
await ScrollToFocusedElement();
}
private async Task ScrollToFocusedElement()
{
await Task.Yield();
var focusedElement = FocusManager.GetFocusedElement() as PhoneTextBox;
if (focusedElement != null)
{
// http://stackoverflow.com/questions/1225318/how-can-i-make-the-silverlight-scrollviewer-scroll-to-show-a-child-control-with
var focusedVisualTransform = focusedElement.TransformToVisual(ScrollViewer);
var rectangle =
focusedVisualTransform.TransformBounds(
new Rect(new Point(focusedElement.Margin.Left, focusedElement.Margin.Top), focusedElement.RenderSize));
var offset = ScrollViewer.VerticalOffset + (rectangle.Bottom - ScrollViewer.ViewportHeight);
ScrollViewer.ScrollToVerticalOffset(offset);
}
}
Wow, there is a lot of code there. I am working on creating something reusable, but I am not there yet. Once I do it, I will publish it on NuGet.
Note that this only works in Portrait
mode and will not work perfectly if you have the auto suggestion bar open. I didn't need to handle that in my app so I skipped it :)
Enjoy.