using System; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Markup; using System.Windows.Media; using Caliburn.Controls.Transitions; namespace Caliburn.Controls { [ContentProperty("Content")] public class TransitionPresenter : FrameworkElement { public static readonly DependencyProperty ContentProperty = DependencyProperty.Register("Content", typeof(object), typeof(TransitionPresenter), new UIPropertyMetadata(null, OnContentChanged, CoerceContent)); public static readonly DependencyProperty ContentTemplateProperty = DependencyProperty.Register("ContentTemplate", typeof(DataTemplate), typeof(TransitionPresenter), new UIPropertyMetadata(null, OnContentTemplateChanged)); public static readonly DependencyProperty ContentTemplateSelectorProperty = DependencyProperty.Register("ContentTemplateSelector", typeof(DataTemplateSelector), typeof(TransitionPresenter), new UIPropertyMetadata(null, OnContentTemplateSelectorChanged)); private static readonly DependencyPropertyKey IsTransitioningPropertyKey = DependencyProperty.RegisterReadOnly("IsTransitioning", typeof(bool), typeof(TransitionPresenter), new UIPropertyMetadata(false)); public static readonly DependencyProperty IsTransitioningProperty = IsTransitioningPropertyKey.DependencyProperty; public static readonly DependencyProperty TransitionProperty = DependencyProperty.Register("Transition", typeof(TransitionBase), typeof(TransitionPresenter), new UIPropertyMetadata(null, null, CoerceTransition)); public static readonly DependencyProperty TransitionSelectorProperty = DependencyProperty.Register("TransitionSelector", typeof(TransitionSelector), typeof(TransitionPresenter), new UIPropertyMetadata(null)); private readonly UIElementCollection _children; private TransitionBase _activeTransition; private AdornerDecorator _currentHost; private AdornerDecorator _previousHost; static TransitionPresenter() { ClipToBoundsProperty.OverrideMetadata(typeof(TransitionPresenter), new FrameworkPropertyMetadata(null, CoerceClipToBounds)); } public TransitionPresenter() { _children = new UIElementCollection(this, null); ContentPresenter currentContent = new ContentPresenter(); _currentHost = new AdornerDecorator(); _currentHost.Child = currentContent; _children.Add(_currentHost); ContentPresenter previousContent = new ContentPresenter(); _previousHost = new AdornerDecorator(); _previousHost.Child = previousContent; } public object Content { get { return GetValue(ContentProperty); } set { SetValue(ContentProperty, value); } } public DataTemplate ContentTemplate { get { return (DataTemplate) GetValue(ContentTemplateProperty); } set { SetValue(ContentTemplateProperty, value); } } public DataTemplateSelector ContentTemplateSelector { get { return (DataTemplateSelector) GetValue(ContentTemplateSelectorProperty); } set { SetValue(ContentTemplateSelectorProperty, value); } } public bool IsTransitioning { get { return (bool) GetValue(IsTransitioningProperty); } private set { SetValue(IsTransitioningPropertyKey, value); } } public TransitionBase Transition { get { return (TransitionBase) GetValue(TransitionProperty); } set { SetValue(TransitionProperty, value); } } public TransitionSelector TransitionSelector { get { return (TransitionSelector) GetValue(TransitionSelectorProperty); } set { SetValue(TransitionSelectorProperty, value); } } protected override int VisualChildrenCount { get { return _children.Count; } } internal UIElementCollection Children { get { return _children; } } private ContentPresenter PreviousContentPresenter { get { return ((ContentPresenter) _previousHost.Child); } } protected ContentPresenter CurrentContentPresenter { get { return ((ContentPresenter) _currentHost.Child); } } private static object CoerceClipToBounds(object element, object value) { TransitionPresenter transitionElement = (TransitionPresenter) element; bool clip = (bool) value; if(!clip && transitionElement.IsTransitioning) { TransitionBase transition = transitionElement.Transition; if(transition.ClipToBounds) return true; } return value; } private static object CoerceContent(object element, object value) { TransitionPresenter te = (TransitionPresenter) element; if(te.IsTransitioning) return te.CurrentContentPresenter.Content; return value; } private static void OnContentChanged(object element, DependencyPropertyChangedEventArgs e) { TransitionPresenter te = (TransitionPresenter) element; te.BeginTransition(); } private static void OnContentTemplateChanged(object element, DependencyPropertyChangedEventArgs e) { TransitionPresenter te = (TransitionPresenter) element; te.CurrentContentPresenter.ContentTemplate = (DataTemplate) e.NewValue; } private static void OnContentTemplateSelectorChanged(object element, DependencyPropertyChangedEventArgs e) { TransitionPresenter te = (TransitionPresenter) element; te.CurrentContentPresenter.ContentTemplateSelector = (DataTemplateSelector) e.NewValue; } private static object CoerceTransition(object element, object value) { TransitionPresenter te = (TransitionPresenter) element; if(te.IsTransitioning) return te._activeTransition; return value; } protected virtual void BeginTransition() { TransitionSelector selector = TransitionSelector; TransitionBase transition = selector != null ? selector.SelectTransition(CurrentContentPresenter.Content, Content) : Transition; if(transition != null) { // Swap content presenters AdornerDecorator temp = _previousHost; _previousHost = _currentHost; _currentHost = temp; } ContentPresenter currentContent = CurrentContentPresenter; // Set the current content currentContent.Content = Content; currentContent.ContentTemplate = ContentTemplate; currentContent.ContentTemplateSelector = ContentTemplateSelector; if(transition != null) { ContentPresenter previousContent = PreviousContentPresenter; if(transition.IsNewContentTopmost) Children.Add(_currentHost); else Children.Insert(0, _currentHost); IsTransitioning = true; _activeTransition = transition; CoerceValue(TransitionProperty); CoerceValue(ClipToBoundsProperty); transition.BeginTransition(this, previousContent, currentContent); } } // Clean up after the transition is complete internal void OnTransitionCompleted(TransitionBase transition) { _children.Clear(); _children.Add(_currentHost); _currentHost.Visibility = Visibility.Visible; _previousHost.Visibility = Visibility.Visible; ((ContentPresenter) _previousHost.Child).Content = null; IsTransitioning = false; _activeTransition = null; CoerceValue(TransitionProperty); CoerceValue(ClipToBoundsProperty); CoerceValue(ContentProperty); } protected override Size MeasureOverride(Size availableSize) { _currentHost.Measure(availableSize); return _currentHost.DesiredSize; } protected override Size ArrangeOverride(Size finalSize) { foreach(UIElement uie in _children) uie.Arrange(new Rect(finalSize)); return finalSize; } protected override Visual GetVisualChild(int index) { if(index < 0 || index >= _children.Count) throw new ArgumentException("index"); return _children[index]; } } }