Click here to Skip to main content
15,884,628 members
Articles / Desktop Programming / WPF

Custom Sized ScatterViewItems

Rate me:
Please Sign up or sign in to vote.
4.17/5 (6 votes)
9 Dec 2010CPOL8 min read 28.5K   831   10  
Demonstrates how to set the initial size of ScatterViewItems based on the content's requested size.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Surface;
using Microsoft.Surface.Presentation;
using Microsoft.Surface.Presentation.Controls;
using System.Windows.Media.Animation;

namespace ScatterViewSizingSample
{
	/// <summary>
	/// Interaction logic for PopupWindow.xaml
	/// </summary>
	/// <remarks>
	/// The popup control is used as a child of the main scatterview and will display content in a window like UI.
	/// It's possible for rendered content to request an initial size by setting the InitialSizeRequest attached property (PopupWindow.InitialSizeRequest="www,hhh").
	/// 
	/// Written by: Isak Savo, isak.savo@gmail.com and included in a CodeProject Article
	/// </remarks>
	public partial class PopupWindow : UserControl
	{
		#region Attached Property InitialSizeRequest
		public static Size GetInitialSizeRequest(DependencyObject obj)
		{
			return (Size)obj.GetValue(InitialSizeRequestProperty);
		}

		public static void SetInitialSizeRequest(DependencyObject obj, Size value)
		{
			obj.SetValue(InitialSizeRequestProperty, value);
		}

		// Using a DependencyProperty as the backing store for InitialSizeRequest.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty InitialSizeRequestProperty =
			DependencyProperty.RegisterAttached("InitialSizeRequest", typeof(Size), typeof(PopupWindow), new UIPropertyMetadata(Size.Empty));

		#endregion

		public PopupWindow()
		{
			InitializeComponent();
			if (!System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
				c_contentHolder.Loaded += new RoutedEventHandler(c_contentHolder_Loaded);
		}

		public PopupWindow(object content)
		{
			InitializeComponent();
			if (!System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
				c_contentHolder.Loaded += new RoutedEventHandler(c_contentHolder_Loaded);
			c_contentHolder.Content = content;
		}

		private static Size DefaultPopupSize = new Size(300, 200);
		/// <summary>
		/// Gets the size that the parent container should have to fully accomodate the PopupWindow and its child content
		/// based on the child's InitialSizeRequest.
		/// </summary>
		/// <returns>The size, which should be set to the parent container</returns>
		private Size CalculateScatterViewItemSize()
		{
			var presenter = GuiHelpers.GetChildObject<ContentPresenter>(c_contentHolder);
			if (presenter == null)
				return DefaultPopupSize;
			// It seems it's safe to assume the ContentPresenter will always only have one child and that child is the visual representation
			// of the content of c_contentHolder.
			var child = VisualTreeHelper.GetChild(presenter, 0);
			if (child == null)
				return DefaultPopupSize;
			var requestedSize = PopupWindow.GetInitialSizeRequest(child);
			if (!requestedSize.IsEmpty
				&& requestedSize.Width != 0
				&& requestedSize.Height != 0)
			{
				var borderHeight = this.ActualHeight - c_contentHolder.ActualHeight;
				var borderWidth = this.ActualWidth - c_contentHolder.ActualWidth;
				return new Size(requestedSize.Width + borderWidth, requestedSize.Height + borderHeight);
			}
			else
				// No requested size set, or the requested size was invalid
				return DefaultPopupSize;
		}

		void c_contentHolder_Loaded(object sender, RoutedEventArgs e)
		{
			// By now we've got the visual tree for the content of the popup, check to see if it has a request for the size of the popup
			var newSize = CalculateScatterViewItemSize();
			
			AnimateEntry(newSize);
		}

		private void AnimateEntry(Size targetSize)
		{
			var svi = GuiHelpers.GetParentObject<ScatterViewItem>(this, false);
			if (svi != null)
			{
				// Easing function provide a more natural animation
				IEasingFunction ease = new BackEase { EasingMode = EasingMode.EaseOut, Amplitude = 0.3 };
				var duration = new Duration(TimeSpan.FromMilliseconds(500));
				var w = new DoubleAnimation(0.0, targetSize.Width, duration) { EasingFunction = ease };
				var h = new DoubleAnimation(0.0, targetSize.Height, duration) { EasingFunction = ease };
				var o = new DoubleAnimation(0.0, 1.0, duration);
				
				// Remove the animation after it has completed so that its possible to manually resize the scatterviewitem
				w.Completed += (s, e) => svi.BeginAnimation(ScatterViewItem.WidthProperty, null);
				h.Completed += (s, e) => svi.BeginAnimation(ScatterViewItem.HeightProperty, null);
				// Set the size manually, otherwise once the animation is removed the size will revert back to the minimum size
				// Since animation has higher priority for DP's, this setting won't have effect until the animation is removed
				svi.Width = targetSize.Width;
				svi.Height = targetSize.Height;

				svi.BeginAnimation(ScatterViewItem.WidthProperty, w);
				svi.BeginAnimation(ScatterViewItem.HeightProperty, h);
				svi.BeginAnimation(ScatterViewItem.OpacityProperty, o);
			}
		}

		private void AnimateExit()
		{
			var svi = GuiHelpers.GetParentObject<ScatterViewItem>(this, false);
			if (svi != null)
			{
				IEasingFunction ease = new BackEase { EasingMode = EasingMode.EaseOut, Amplitude = 0.3 };
				var duration = new Duration(TimeSpan.FromMilliseconds(500));
				var w = new DoubleAnimation(0.0, duration) { EasingFunction = ease };
				var h = new DoubleAnimation(0.0, duration) { EasingFunction = ease };
				var o = new DoubleAnimation(0.0, duration);
				svi.BeginAnimation(ScatterViewItem.WidthProperty, w);
				svi.BeginAnimation(ScatterViewItem.HeightProperty, h);
				svi.BeginAnimation(ScatterViewItem.OpacityProperty, o);
			}
		}
		
		private void btnClose_Click(object sender, RoutedEventArgs e)
		{
			var sv = GuiHelpers.GetParentObject<ScatterView>(this);
			if (sv != null)
				sv.Items.Remove(this);
		}
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer ABB
Sweden Sweden
My name is Isak Savo and I work as a Research Engineer at ABB Corporate Research in Västerås, Sweden. My work is focused around user experience which includes a lot of prototyping of new solutions for user interfaces and user interaction.

While I have a background in C programming in a Linux environment, my daily work is mostly spent in Windows using C# and WPF.

Comments and Discussions