Home > English > Automatically scrolling to a selected item in a ScrollViewer in Silverlight

Automatically scrolling to a selected item in a ScrollViewer in Silverlight

Sometimes it is necessary to have some custom user interactions as you are building business applications. It became necessary during a design session that the client needed the ability to design their own bill layouts. They needed the ability to basically drag and drop controls on the screen to build an invoice to send to their customers. With the power of Silverlight, I wanted to present them with a grid or canvas and allow them to create their own layouts. The problem with this is that they started making really complex layouts that made perfect sense to them but required scrolling during the design phase of the bill. One issue that came up is that when they wanted to navigate to an input control, they had to scroll and search for the control. I am using a ValidationSummary control to provide validation errors but since I was out of the context of a DataForm, I couldn’t just have my custom controls get highlighted. So I found two different implementations that provided a good starting ground but I wanted to have both vertical and horizontal navigation. I created a Behavior to support this and now when a user clicks on an item in the ValidationSummary control, they are navigated to the corresponding control.

The following is a sample screen shot of one of the bill design that was created:

Bill Designer

The following is a screen shot when the user click on the item in error:

Bill Designer - Auto Scroll

Here is the implementation for the Behavior:

 public class ScrollToSelectedItemBahavior : Behavior<ScrollViewer>
{
	private const int SCROLL_PADDING = 10;

	#region Overrides
	protected override void OnAttached()
	{
		base.OnAttached();

		this.AssociatedObject.Loaded += OnLoaded;
	}

	protected override void OnDetaching()
	{
		base.OnDetaching();

		this.AssociatedObject.Loaded -= OnLoaded;
		var controls = this.AssociatedObject.GetVisualDescendants();
		foreach (FrameworkElement control in controls)
		{
			control.GotFocus -= Control_GotFocus;
		}
	}
	#endregion

	#region Events

	private void OnLoaded(object sender, RoutedEventArgs e)
	{
		var controls = AssociatedObject.GetVisualDescendants();
		foreach (FrameworkElement control in controls)
		{
			control.GotFocus += Control_GotFocus;
		}
	}

	private void Control_GotFocus(object sender, RoutedEventArgs e)
	{
		FrameworkElement element = e.OriginalSource as FrameworkElement;
		var transform = element.TransformToVisual(AssociatedObject);
		var positionInScrollViewer = transform.Transform(new Point(0, 0));

		if (positionInScrollViewer.Y < 0 || 
			positionInScrollViewer.Y > AssociatedObject.ViewportHeight)
		{
			AssociatedObject.ScrollToVerticalOffset(AssociatedObject.VerticalOffset + 
				positionInScrollViewer.Y - SCROLL_PADDING);
		}

		if (positionInScrollViewer.X < 0 || 
			positionInScrollViewer.X > AssociatedObject.ViewportWidth)
		{
			AssociatedObject.ScrollToHorizontalOffset(AssociatedObject.HorizontalOffset + 
				positionInScrollViewer.X - SCROLL_PADDING);
		}
	}

	#endregion

};

As with any Behavior you need to add a using statement for System.Windows.Interactivity. In this example, I am also using some helper functions from the Silverlight Toolkit and I also have a using statement for System.Windows.Controls.Primitives.

Basically, when we attach the Behavior to the underlying control, we wire up the Loaded event and in this event we cycle through all the descendants and wire up the GotFocus event. It is the method call GetVisualDescendants that is the helper method from the Silverlight Toolkit. Conversely, when we are detaching we unwire all of the GotFocus events.

In the Control_GotFocus method we simply create a transform and then scroll vertically, horizontally, or both depending on where the control is located.

That is basically all there is to coding this Behavior.

Now, let’s look at the Xaml required to get this to work:

<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
	<i:Interaction.Behaviors>
		<core:ScrollToSelectedItemBahavior/>
	</i:Interaction.Behaviors>
	<Grid 
		x:Name="grdDesigner"
		Margin="10,10,10,10" 
		ShowGridLines="True"
		radDrag:RadDragAndDropManager.AllowDrop="True" 
		Background="#FF2D2D2D">
		<i:Interaction.Triggers>
			<i:EventTrigger EventName="MouseLeftButtonDown">
				<core:ExecuteDataMethod Method="Designer_MouseLeftButtonDown" />
			</i:EventTrigger>
		</i:Interaction.Triggers>

	</Grid>
</ScrollViewer>

The following are the links that got me started and show a slightly different implementation:
Silverlight 4 ScrollViewer & Keeping items in view
Silverlight BringIntoView() extension method (with OnGotFocus behavior)

Hope this helps…

Advertisements
Categories: English Tags: , ,
  1. No comments yet.
  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: