/************************************************************************************* Extended WPF Toolkit Copyright (C) 2007-2013 Xceed Software Inc. This program is provided to you under the terms of the Microsoft Public License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license For more features, controls, and fast professional support, pick up the Plus Edition at http://xceed.com/wpf_toolkit Stay informed: follow @datagrid on Twitter or Like http://facebook.com/datagrids ***********************************************************************************/ using System; using System.Collections.Generic; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Media; using System.Windows.Threading; using Xceed.Wpf.Toolkit.Core.Utilities; namespace Xceed.Wpf.Toolkit.Panels { public class SwitchPresenter : FrameworkElement { #region Constructors public SwitchPresenter() : base() { this.AddVisualChild( _contentPresenter ); this.Loaded += new RoutedEventHandler( this.SwitchPresenter_Loaded ); this.Unloaded += new RoutedEventHandler( this.SwitchPresenter_Unloaded ); } #endregion #region DelaySwitch Property // Using a DependencyProperty as the backing store for DelaySwitch. This enables animation, styling, binding, etc... public static readonly DependencyProperty DelaySwitchProperty = DependencyProperty.Register( "DelaySwitch", typeof( bool ), typeof( SwitchPresenter ), new UIPropertyMetadata( false ) ); public bool DelaySwitch { get { return ( bool )this.GetValue( SwitchPresenter.DelaySwitchProperty ); } set { this.SetValue( SwitchPresenter.DelaySwitchProperty, value ); } } #endregion #region DelayPriority Property // Using a DependencyProperty as the backing store for DelayPriority. This enables animation, styling, binding, etc... public static readonly DependencyProperty DelayPriorityProperty = DependencyProperty.Register( "DelayPriority", typeof( DispatcherPriority ), typeof( SwitchPresenter ), new UIPropertyMetadata( DispatcherPriority.Background ) ); public DispatcherPriority DelayPriority { get { return ( DispatcherPriority )this.GetValue( SwitchPresenter.DelayPriorityProperty ); } set { this.SetValue( SwitchPresenter.DelayPriorityProperty, value ); } } #endregion #region SwitchParent Internal Property internal static readonly DependencyProperty SwitchParentProperty = DependencyProperty.Register( "SwitchParent", typeof( SwitchPanel ), typeof( SwitchPresenter ), new FrameworkPropertyMetadata( null, new PropertyChangedCallback( SwitchPresenter.OnSwitchParentChanged ) ) ); internal SwitchPanel SwitchParent { get { return ( SwitchPanel )this.GetValue( SwitchPresenter.SwitchParentProperty ); } set { this.SetValue( SwitchPresenter.SwitchParentProperty, value ); } } private static void OnSwitchParentChanged( DependencyObject d, DependencyPropertyChangedEventArgs e ) { ( ( SwitchPresenter )d ).OnSwitchParentChanged( e ); } protected virtual void OnSwitchParentChanged( DependencyPropertyChangedEventArgs e ) { if( e.OldValue != null ) { ( e.OldValue as SwitchPanel ).UnregisterPresenter( this, _switchRoot ); _switchRoot = null; BindingOperations.ClearAllBindings( _contentPresenter ); } if( e.NewValue != null ) { _contentPresenter.SetBinding( ContentPresenter.ContentProperty, new Binding() ); _switchRoot = ( e.NewValue as SwitchPanel ).RegisterPresenter( this ); } } private static void OnLoaded( object sender, RoutedEventArgs e ) { SwitchPresenter sp = sender as SwitchPresenter; if( sp._switchRoot == null ) { sp.SwitchParent = VisualTreeHelperEx.FindAncestorByType( sp, typeof( SwitchPanel ), false ) as SwitchPanel; } } private static void OnUnloaded( object sender, RoutedEventArgs e ) { ( sender as SwitchPresenter ).SwitchParent = null; } #endregion #region VisualChildrenCount Protected Property protected override int VisualChildrenCount { get { return 1; } } #endregion internal void RegisterID( string id, FrameworkElement element ) { if( element == null ) return; _knownIDs[ id ] = element; } internal void SwapTheTemplate( DataTemplate template, bool beginAnimation ) { if( this.DelaySwitch ) { _currentTemplate = template; this.Dispatcher.BeginInvoke( new Action( this.OnSwapTemplate ), this.DelayPriority, new DelaySwitchParams() { Template = template, BeginAnimation = beginAnimation } ); } else { this.DoSwapTemplate( template, beginAnimation ); } } protected override Size MeasureOverride( Size constraint ) { // if first pass, resolve SwitchParent if( !_isMeasured && _switchRoot == null ) { SwitchPresenter.OnLoaded( this, null ); _isMeasured = true; } _contentPresenter.Measure( constraint ); return _contentPresenter.DesiredSize; } protected override Size ArrangeOverride( Size arrangeBounds ) { _contentPresenter.Arrange( new Rect( arrangeBounds ) ); return arrangeBounds; } protected override Visual GetVisualChild( int index ) { if( index != 0 ) throw new ArgumentOutOfRangeException( "index", index, "" ); return _contentPresenter; } private void OnSwapTemplate( DelaySwitchParams data ) { // If we are switching the templates fast the invokes will lag. So ignore old invokes. if( data.Template == _currentTemplate ) { this.DoSwapTemplate( data.Template, data.BeginAnimation ); _currentTemplate = null; } } private void DoSwapTemplate( DataTemplate template, bool beginAnimation ) { // cache transforms for known ID'd elements in the current template Dictionary knownLocations = null; if( beginAnimation && _knownIDs.Count > 0 ) { knownLocations = new Dictionary(); foreach( KeyValuePair entry in _knownIDs ) { Size size = entry.Value.RenderSize; Matrix m = ( entry.Value.TransformToAncestor( SwitchParent ) as MatrixTransform ).Matrix; Point[] points = { new Point(), new Point( size.Width, size.Height ) }; m.Transform( points ); knownLocations[ entry.Key ] = new Rect( points[ 0 ], points[ 1 ] ); } } // clear the known IDs because the new template will have all new IDs _knownIDs.Clear(); // set and apply the new template _contentPresenter.ContentTemplate = template; if( template != null ) { _contentPresenter.ApplyTemplate(); } // determine locations of ID'd elements in new template // and begin animation to new location if( knownLocations != null && _knownIDs.Count > 0 ) { Dictionary newLocations = null; RoutedEventHandler onLoaded = null; onLoaded = delegate( object sender, RoutedEventArgs e ) { FrameworkElement element = sender as FrameworkElement; element.Loaded -= onLoaded; string id = SwitchTemplate.GetID( element ); if( knownLocations.ContainsKey( id ) ) { // ensure that the new locations have been resolved if( newLocations == null ) { newLocations = this.SwitchParent.ActiveLayout.GetNewLocationsBasedOnTargetPlacement( this, _switchRoot ); } UIElement parent = VisualTreeHelper.GetParent( element ) as UIElement; if( parent != null ) { Rect previousLocation = knownLocations[ id ]; Point[] points = { previousLocation.TopLeft, previousLocation.BottomRight }; Matrix m = ( SwitchParent.TransformToDescendant( parent ) as MatrixTransform ).Matrix; m.Transform( points ); Rect oldLocation = new Rect( points[ 0 ], points[ 1 ] ); Rect newLocation = newLocations[ id ]; this.SwitchParent.ActiveLayout.BeginGrandchildAnimation( element, oldLocation, newLocation ); } } }; foreach( KeyValuePair entry in _knownIDs ) { entry.Value.Loaded += onLoaded; } } } private void SwitchPresenter_Unloaded( object sender, RoutedEventArgs e ) { this.SwitchParent = null; } private void SwitchPresenter_Loaded( object sender, RoutedEventArgs e ) { if( _switchRoot == null ) { this.SwitchParent = VisualTreeHelperEx.FindAncestorByType( this, typeof( SwitchPanel ), false ) as SwitchPanel; } } #region Private Fields // track our topmost ancestor that is the direct child of the SwitchPanel internal UIElement _switchRoot = null; internal Dictionary _knownIDs = new Dictionary(); private ContentPresenter _contentPresenter = new ContentPresenter(); private bool _isMeasured = false; private DataTemplate _currentTemplate; #endregion #region DelaySwitchParams Nested Type private struct DelaySwitchParams { public DataTemplate Template; public bool BeginAnimation; } #endregion } }