using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Forms.Integration;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Threading;
using Tango.SharedUI.Helpers;
namespace Tango.SharedUI.Controls
{
[ContentProperty(nameof(Elements))]
public class NavigationControl : UserControl
{
private Action _onCompleted;
#region Transition Types
public enum TransitionTypes
{
Fade,
Zoom,
Slide,
}
#endregion
#region Navigation Element
private class NavigationElement : ContentControl
{
public FrameworkElement Element
{
get { return (FrameworkElement)GetValue(ElementProperty); }
set { SetValue(ElementProperty, value); }
}
public static readonly DependencyProperty ElementProperty =
DependencyProperty.Register("Element", typeof(FrameworkElement), typeof(NavigationControl), new PropertyMetadata(null));
public ScaleTransform Scale
{
get { return (ScaleTransform)GetValue(ScaleProperty); }
set { SetValue(ScaleProperty, value); }
}
public static readonly DependencyProperty ScaleProperty =
DependencyProperty.Register("Scale", typeof(ScaleTransform), typeof(NavigationControl), new PropertyMetadata(null));
public RotateTransform Rotate
{
get { return (RotateTransform)GetValue(RotateProperty); }
set { SetValue(RotateProperty, value); }
}
public static readonly DependencyProperty RotateProperty =
DependencyProperty.Register("Rotate", typeof(RotateTransform), typeof(NavigationControl), new PropertyMetadata(null));
public TranslateTransform Translate
{
get { return (TranslateTransform)GetValue(TranslateProperty); }
set { SetValue(TranslateProperty, value); }
}
public static readonly DependencyProperty TranslateProperty =
DependencyProperty.Register("Translate", typeof(TranslateTransform), typeof(NavigationControl), new PropertyMetadata(null));
public NavigationElement()
{
Visibility = Visibility.Hidden;
Scale = new ScaleTransform(1, 1);
Rotate = new RotateTransform(0);
Translate = new TranslateTransform(0, 0);
TransformGroup group = new TransformGroup();
group.Children.Add(Scale);
group.Children.Add(Rotate);
group.Children.Add(Translate);
RenderTransformOrigin = new Point(0.5, 0.5);
RenderTransform = group;
}
public NavigationElement(FrameworkElement element) : this()
{
Element = element;
}
public void AnimateScale(DoubleAnimation animation)
{
Scale.BeginAnimation(ScaleTransform.ScaleXProperty, animation);
Scale.BeginAnimation(ScaleTransform.ScaleYProperty, animation);
}
public void AnimateTranslateX(DoubleAnimation animation)
{
Translate.BeginAnimation(TranslateTransform.XProperty, animation);
}
public void AnimateOpacity(DoubleAnimation animation)
{
BeginAnimation(OpacityProperty, animation);
}
public void Reset()
{
Scale.BeginAnimation(ScaleTransform.ScaleXProperty, null);
Scale.BeginAnimation(ScaleTransform.ScaleYProperty, null);
Rotate.BeginAnimation(RotateTransform.AngleProperty, null);
Translate.BeginAnimation(TranslateTransform.XProperty, null);
Translate.BeginAnimation(TranslateTransform.YProperty, null);
Opacity = 1;
}
public void Activate()
{
if (Content != Element)
{
Content = Element;
}
Visibility = Visibility.Visible;
}
public void Deactivate(bool detach)
{
if (detach)
{
Content = null;
}
Visibility = Visibility.Hidden;
}
}
#endregion
#region INavigationView
public interface INavigationView
{
void OnNavigatedTo();
void OnNavigatedFrom();
}
#endregion
#region INavigationViewModel
///
/// Represents a view data context which can be notified when its view is being navigated in or out.
///
public interface INavigationViewModel
{
///
/// Called when the navigation system has navigated to this VM view.
///
void OnNavigatedTo();
///
/// Called when the navigation system has navigated from this VM view.
///
void OnNavigatedFrom();
}
#endregion
private Grid _grid;
private bool _loaded;
#region Properties
public ObservableCollection Elements
{
get { return (ObservableCollection)GetValue(ElementsProperty); }
set { SetValue(ElementsProperty, value); }
}
public static readonly DependencyProperty ElementsProperty =
DependencyProperty.Register("Elements", typeof(ObservableCollection), typeof(NavigationControl), new PropertyMetadata(null, (d, e) => (d as NavigationControl).OnElementsChanged()));
public FrameworkElement SelectedElement
{
get { return (FrameworkElement)GetValue(SelectedElementProperty); }
set { SetValue(SelectedElementProperty, value); }
}
public static readonly DependencyProperty SelectedElementProperty =
DependencyProperty.Register("SelectedElement", typeof(FrameworkElement), typeof(NavigationControl), new PropertyMetadata(null, (d, e) => (d as NavigationControl).OnSelectedElementChanged(e.OldValue as FrameworkElement, e.NewValue as FrameworkElement)));
public TransitionTypes TransitionType
{
get { return (TransitionTypes)GetValue(TransitionTypeProperty); }
set { SetValue(TransitionTypeProperty, value); }
}
public static readonly DependencyProperty TransitionTypeProperty =
DependencyProperty.Register("TransitionType", typeof(TransitionTypes), typeof(NavigationControl), new PropertyMetadata(TransitionTypes.Zoom));
public bool TransitionAlwaysFades
{
get { return (bool)GetValue(TransitionAlwaysFadesProperty); }
set { SetValue(TransitionAlwaysFadesProperty, value); }
}
public static readonly DependencyProperty TransitionAlwaysFadesProperty =
DependencyProperty.Register("TransitionAlwaysFades", typeof(bool), typeof(NavigationControl), new PropertyMetadata(false));
public Duration TransitionDuration
{
get { return (Duration)GetValue(TransitionDurationProperty); }
set { SetValue(TransitionDurationProperty, value); }
}
public static readonly DependencyProperty TransitionDurationProperty =
DependencyProperty.Register("TransitionDuration", typeof(Duration), typeof(NavigationControl), new PropertyMetadata(new Duration(TimeSpan.FromSeconds(0.5))));
public bool KeepElementsAttached
{
get { return (bool)GetValue(KeepElementsAttachedProperty); }
set { SetValue(KeepElementsAttachedProperty, value); }
}
public static readonly DependencyProperty KeepElementsAttachedProperty =
DependencyProperty.Register("KeepElementsAttached", typeof(bool), typeof(NavigationControl), new PropertyMetadata(false));
public int SelectedIndex
{
get { return (int)GetValue(SelectedIndexProperty); }
set { SetValue(SelectedIndexProperty, value); }
}
// Using a DependencyProperty as the backing store for SelectedIndex. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SelectedIndexProperty =
DependencyProperty.Register("SelectedIndex", typeof(int), typeof(NavigationControl), new PropertyMetadata(0, (d, e) => (d as NavigationControl).OnSelectedIndexChanged()));
public bool UseDefferedRendering
{
get { return (bool)GetValue(UseDefferedRenderingProperty); }
set { SetValue(UseDefferedRenderingProperty, value); }
}
public static readonly DependencyProperty UseDefferedRenderingProperty =
DependencyProperty.Register("UseDefferedRendering", typeof(bool), typeof(NavigationControl), new PropertyMetadata(false));
private void OnSelectedIndexChanged()
{
SelectedElement = Elements[SelectedIndex > Elements.Count - 1 ? 0 : SelectedIndex];
}
#endregion
#region Attached Properties
#region NavigationName
///
/// Determines the element navigation name.
///
public static readonly DependencyProperty NavigationName =
DependencyProperty.RegisterAttached("NavigationName",
typeof(String), typeof(NavigationControl),
new FrameworkPropertyMetadata(""));
///
/// Sets the name of the navigation.
///
/// The element.
/// The value.
public static void SetNavigationName(FrameworkElement element, String value)
{
element.SetValue(NavigationName, value);
}
///
/// Gets the name of the navigation.
///
/// The element.
///
public static String GetNavigationName(FrameworkElement element)
{
return element.GetValue(NavigationName).ToStringSafe();
}
#endregion
#endregion
#region Constructors
public NavigationControl()
{
ClipToBounds = true;
_grid = new Grid();
Content = _grid;
Elements = new ObservableCollection();
Loaded += NavigationControl_Loaded;
}
#endregion
#region Event Handlers
private void NavigationControl_Loaded(object sender, RoutedEventArgs e)
{
if (!_loaded)
{
_loaded = true;
if (Elements != null)
{
//foreach (var element in Elements.Where(x => x != SelectedElement))
//{
// LoadVisual(element);
//}
SelectedElement = Elements.FirstOrDefault();
}
}
}
private void Elements_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
SyncElements();
}
#endregion
#region Private Methods
private void OnElementsChanged()
{
if (Elements != null)
{
Elements.CollectionChanged -= Elements_CollectionChanged;
Elements.CollectionChanged += Elements_CollectionChanged;
}
SyncElements();
}
private void SyncElements()
{
if (Elements == null)
{
_grid.Children.Clear();
}
else
{
var toRemove = _grid.Children.OfType().ToList().Where(x => !Elements.Contains(x.Element)).ToList();
var toAdd = Elements.Where(x => !_grid.Children.OfType().Select(y => y.Element).ToList().Contains(x)).ToList();
toRemove.ForEach(x => _grid.Children.Remove(x));
if (UseDefferedRendering)
{
foreach (var x in toAdd)
{
var navigationElement = new NavigationElement()
{
RenderTransformOrigin = new Point(0.5, 0.5),
Element = x,
Content = x,
};
_grid.Children.Add(navigationElement);
if (!KeepElementsAttached)
{
bool loaded = false;
navigationElement.Loaded += (_, __) =>
{
if (!loaded)
{
if (x != SelectedElement)
{
loaded = true;
navigationElement.Content = null;
}
}
};
}
}
}
else
{
toAdd.ForEach(x => _grid.Children.Add(
new NavigationElement()
{
RenderTransformOrigin = new Point(0.5, 0.5),
Element = x,
Content = KeepElementsAttached ? x : null,
}));
}
}
if (!Elements.Contains(SelectedElement))
{
SelectedElement = null;
}
}
private NavigationElement GetElementContainer(FrameworkElement element)
{
return _grid.Children.OfType().ToList().SingleOrDefault(x => x.Element == element);
}
private void Navigate(NavigationElement fromElement, NavigationElement toElement)
{
if (toElement == null || toElement == fromElement)
{
_onCompleted?.Invoke();
}
if (fromElement != null)
{
DoubleAnimation toAnimation = new DoubleAnimation();
toAnimation.Duration = TransitionDuration;
DoubleAnimation fromAnimation = new DoubleAnimation();
fromAnimation.Duration = TransitionDuration;
int fromIndex = Elements.IndexOf(fromElement.Element);
int toIndex = Elements.IndexOf(toElement.Element);
fromElement.Reset();
toElement.Reset();
fromAnimation.Completed += (_, __) =>
{
fromElement.Deactivate(!KeepElementsAttached);
};
bool completed = false;
toAnimation.Completed += (_, __) =>
{
if (!completed)
{
completed = true;
INavigationView fromNavigationView = fromElement.Element as INavigationView;
INavigationView toNavigationView = toElement.Element as INavigationView;
if (fromNavigationView != null)
{
fromNavigationView.OnNavigatedFrom();
}
if (toNavigationView != null)
{
toNavigationView.OnNavigatedTo();
}
INavigationViewModel fromVM = fromElement.Element.DataContext as INavigationViewModel;
INavigationViewModel toVM = toElement.Element.DataContext as INavigationViewModel;
if (fromVM != null && fromVM != toVM) fromVM.OnNavigatedFrom();
if (toVM != null) toVM.OnNavigatedTo();
_onCompleted?.Invoke();
}
};
switch (TransitionType)
{
case TransitionTypes.Fade:
fromAnimation.From = 1;
fromAnimation.To = 0;
toAnimation.From = 0;
toAnimation.To = 1;
fromElement.AnimateOpacity(fromAnimation);
toElement.AnimateOpacity(toAnimation);
break;
case TransitionTypes.Zoom:
fromAnimation.From = 1;
fromAnimation.To = 0;
toAnimation.From = 0;
toAnimation.To = 1;
fromElement.AnimateScale(fromAnimation);
toElement.AnimateScale(toAnimation);
break;
case TransitionTypes.Slide:
if (toIndex > fromIndex)
{
fromAnimation.From = 0;
fromAnimation.To = -ActualWidth;
toAnimation.From = ActualWidth;
toAnimation.To = 0;
}
else
{
fromAnimation.From = 0;
fromAnimation.To = ActualWidth;
toAnimation.From = -ActualWidth;
toAnimation.To = 0;
}
fromElement.AnimateTranslateX(fromAnimation);
toElement.AnimateTranslateX(toAnimation);
break;
}
if (TransitionAlwaysFades && TransitionType != TransitionTypes.Fade)
{
DoubleAnimation fromFadeAnimation = new DoubleAnimation();
fromFadeAnimation.From = 1;
fromFadeAnimation.To = 0;
fromFadeAnimation.Duration = TransitionDuration;
DoubleAnimation toFadeAnimation = new DoubleAnimation();
toFadeAnimation.From = 0;
toFadeAnimation.To = 1;
toFadeAnimation.Duration = TransitionDuration;
fromElement.AnimateOpacity(fromFadeAnimation);
toElement.AnimateOpacity(toFadeAnimation);
}
fromElement.Activate();
toElement.Activate();
}
else
{
toElement.Activate();
toElement.Reset();
INavigationViewModel toVM = toElement.Element.DataContext as INavigationViewModel;
if (toVM != null) toVM.OnNavigatedTo();
}
}
#endregion
#region Properties Changes
protected virtual void OnSelectedElementChanged(FrameworkElement fromElement, FrameworkElement toElement)
{
Navigate(GetElementContainer(fromElement), GetElementContainer(toElement));
}
#endregion
#region Public Methods
public void NavigateTo(FrameworkElement element)
{
var e = Elements.SingleOrDefault(x => x == element);
if (e != null)
{
SelectedElement = element;
}
}
public FrameworkElement NavigateTo(String navigationName)
{
return NavigateTo(navigationName, null);
}
public FrameworkElement NavigateTo(String navigationName, Action onCompleted = null)
{
_onCompleted = onCompleted;
var element = Elements.SingleOrDefault(x => GetNavigationName(x) == navigationName || x.GetType().Name == navigationName);
if (element != null)
{
if (SelectedElement == element)
{
_onCompleted?.Invoke();
}
else
{
SelectedElement = element;
}
}
return element;
}
public FrameworkElement GetElement(String navigationName)
{
return Elements.SingleOrDefault(x => GetNavigationName(x) == navigationName || x.GetType().Name == navigationName);
}
public FrameworkElement GetAndDetach(String navigationName)
{
var element = Elements.SingleOrDefault(x => GetNavigationName(x) == navigationName);
GetElementContainer(element).Deactivate(!KeepElementsAttached);
return element;
}
///
/// This method needs to be called in order for
// the element to print visibly at the correct size.
///
private static void LoadVisual(FrameworkElement element)
{
if (element.Parent == null)
{
try
{
System.Windows.Forms.UserControl controlContainer = new System.Windows.Forms.UserControl();
controlContainer.Width = 100;
controlContainer.Height = 100;
var host = new ElementHost() { Child = element, Dock = System.Windows.Forms.DockStyle.Fill };
controlContainer.Controls.Add(host);
IntPtr handle = controlContainer.Handle;
host.Child = null;
controlContainer.Dispose();
}
catch { }
}
}
#endregion
}
}