using System; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Ink; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using System.Windows.Controls.Primitives; using System.Windows.Media.Imaging; namespace Tweened.Extensions { public static class GridExtension { #region Grid Properties - IsMaximizable, Transition, CurrentScreenShot /// /// Defines if the Grid is Maximizable /// /// /// public static bool GetIsMaximizable(DependencyObject obj) { return (bool)obj.GetValue(IsMaximizableProperty); } public static void SetIsMaximizable(DependencyObject obj, bool value) { obj.SetValue(IsMaximizableProperty, value); } // Using a DependencyProperty as the backing store for IsMaximizable. This enables animation, styling, binding, etc... public static readonly DependencyProperty IsMaximizableProperty = DependencyProperty.RegisterAttached("IsMaximizable", typeof(bool), typeof(Grid), new PropertyMetadata(false, null)); /// /// Contains the current bitmap ScreenShot /// /// /// internal static ImageBrush GetCurrentScreenShot(DependencyObject obj) { return (ImageBrush)obj.GetValue(CurrentScreenShotProperty); } internal static void SetCurrentScreenShot(DependencyObject obj, ImageBrush value) { obj.SetValue(CurrentScreenShotProperty, value); } // Using a DependencyProperty as the backing store for CurrentScreenShot. This enables animation, styling, binding, etc... internal static readonly DependencyProperty CurrentScreenShotProperty = DependencyProperty.RegisterAttached("CurrentScreenShot", typeof(ImageBrush), typeof(Grid), new PropertyMetadata(null, null)); public static Brush GetSavedGridBackground(DependencyObject obj) { return (Brush)obj.GetValue(SavedGridBackgroundProperty); } public static void SetSavedGridBackground(DependencyObject obj, Brush value) { obj.SetValue(SavedGridBackgroundProperty, value); } // Using a DependencyProperty as the backing store for SavedGridBackground. This enables animation, styling, binding, etc... public static readonly DependencyProperty SavedGridBackgroundProperty = DependencyProperty.RegisterAttached("SavedGridBackground", typeof(Brush), typeof(Grid), null); /// /// Contains current transition /// /// /// internal static Storyboard GetTransition(DependencyObject obj) { return (Storyboard)obj.GetValue(TransitionProperty); } internal static void SetTransition(DependencyObject obj, Storyboard value) { obj.SetValue(TransitionProperty, value); } // Using a DependencyProperty as the backing store for Transition. This enables animation, styling, binding, etc... internal static readonly DependencyProperty TransitionProperty = DependencyProperty.RegisterAttached("Transition", typeof(Storyboard), typeof(Grid), null); private static void storyboard_Completed(object sender, EventArgs e) { Storyboard s = sender as Storyboard; s.Completed -= storyboard_Completed; FrameworkElement f = s.GetValue(GridExtension.TweenedObjectProperty) as FrameworkElement; Grid parentGrid = f.Parent as Grid; parentGrid.Background = GridExtension.GetSavedGridBackground(parentGrid); parentGrid.IsHitTestVisible = true; foreach (UIElement u in parentGrid.Children) { u.Opacity = 1; if (!Object.ReferenceEquals(u, f)) { u.IsHitTestVisible = true; } else { //we save current object coordinates GridCoordinates oldCoordinates = (GridCoordinates)f.GetValue(GridExtension.ObjectCoordinatesProperty); //we set new row and column coordinates Grid.SetRow(f, oldCoordinates.RowIndex); Grid.SetColumn(f, oldCoordinates.ColumnIndex); Grid.SetColumnSpan(f, oldCoordinates.ColumnSpan); Grid.SetRowSpan(f, oldCoordinates.RowSpan); } } } /// /// Defines duration for current transition /// /// /// public static double GetTransitionDuration(DependencyObject obj) { return (double)obj.GetValue(TransitionDurationProperty); } public static void SetTransitionDuration(DependencyObject obj, double value) { obj.SetValue(TransitionDurationProperty, value); } // Using a DependencyProperty as the backing store for TransitionDuration. This enables animation, styling, binding, etc... public static readonly DependencyProperty TransitionDurationProperty = DependencyProperty.RegisterAttached("TransitionDuration", typeof(double), typeof(Grid), new PropertyMetadata(200d)); /// /// Save current tweened object /// /// /// internal static FrameworkElement GetTweenedObject(DependencyObject obj) { return (FrameworkElement)obj.GetValue(TweenedObjectProperty); } internal static void SetTweenedObject(DependencyObject obj, FrameworkElement value) { obj.SetValue(TweenedObjectProperty, value); } // Using a DependencyProperty as the backing store for TweenedObject. This enables animation, styling, binding, etc... internal static readonly DependencyProperty TweenedObjectProperty = DependencyProperty.RegisterAttached("TweenedObject", typeof(FrameworkElement), typeof(Storyboard), null); #endregion #region object attach properties /// /// Contains Object Row et Column index /// /// /// public static GridCoordinates GetObjectCoordinates(DependencyObject obj) { return (GridCoordinates)obj.GetValue(ObjectCoordinatesProperty); } public static void SetObjectCoordinates(DependencyObject obj, GridCoordinates value) { obj.SetValue(ObjectCoordinatesProperty, value); } // Using a DependencyProperty as the backing store for ObjectCoordinates. This enables animation, styling, binding, etc... public static readonly DependencyProperty ObjectCoordinatesProperty = DependencyProperty.RegisterAttached("ObjectCoordinates", typeof(GridCoordinates), typeof(FrameworkElement), new PropertyMetadata(new GridCoordinates() { ColumnIndex = 0, RowIndex = 0 }, null)); /// /// Defines if current object isMaximized /// /// /// public static bool GetIsMaximized(DependencyObject obj) { return (bool)obj.GetValue(IsMaximizedProperty); } public static void SetIsMaximized(DependencyObject obj, bool value) { obj.SetValue(IsMaximizedProperty, value); } // Using a DependencyProperty as the backing store for IsMaximized. This enables animation, styling, binding, etc... public static readonly DependencyProperty IsMaximizedProperty = DependencyProperty.RegisterAttached("IsMaximized", typeof(bool), typeof(FrameworkElement), new PropertyMetadata(false, new PropertyChangedCallback(OnMaximizedChanged))); private static void OnMaximizedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { //1 - On récupère la grille parente FrameworkElement f = d as FrameworkElement; if (f == null) return; Grid parentGrid = f.Parent as Grid; if (parentGrid == null) return; //on vérifie qu'elle autorise le maximize de ses enfants if (!Convert.ToBoolean(GetIsMaximizable(parentGrid))) return; //2 - On cache tous les enfants mis à part celui qui voit sa propriété passée à true; bool isMaximized = Convert.ToBoolean(e.NewValue); if (isMaximized) { Maximize(parentGrid, f); } else { Minimize(parentGrid, f); } } /// /// Minimize object /// /// parent grid of the object /// object to minimize private static void Minimize(Grid parentGrid, FrameworkElement f) { parentGrid.IsHitTestVisible = false; Storyboard s = parentGrid.GetValue(GridExtension.TransitionProperty) as Storyboard; DoubleAnimation daObj = s.Children[0] as DoubleAnimation; DoubleAnimation daBrush = s.Children[1] as DoubleAnimation; daBrush.To = 1; daObj.To = 0; daObj.BeginTime = TimeSpan.FromMilliseconds(0); s.Begin(); s.Completed += storyboard_Completed; } /// /// maximize Object /// /// parent grid of the object /// object to maximize private static void Maximize(Grid parentGrid, FrameworkElement f) { parentGrid.IsHitTestVisible = false; //3 - we save current screenshot WriteableBitmap wb = new WriteableBitmap(parentGrid, null); parentGrid.SetValue(GridExtension.CurrentScreenShotProperty, new ImageBrush() { ImageSource = wb, Stretch = Stretch.None }); GridExtension.SetSavedGridBackground(parentGrid, parentGrid.Background); parentGrid.Background = parentGrid.GetValue(GridExtension.CurrentScreenShotProperty) as ImageBrush; Brush b = parentGrid.Background; foreach (UIElement u in parentGrid.Children) { u.Opacity = 0; if (!Object.ReferenceEquals(u, f)) { u.IsHitTestVisible = false; } else { //we save current object coordinates f.SetValue(GridExtension.ObjectCoordinatesProperty, new GridCoordinates() { RowIndex = (int)Grid.GetRow(f), ColumnIndex = (int)Grid.GetColumn(f), RowSpan = (int)Grid.GetRowSpan(f), ColumnSpan = (int)Grid.GetColumnSpan(f) }); //we set new row and column coordinates Grid.SetRow(f, 0); Grid.SetColumn(f, 0); Grid.SetColumnSpan(f, parentGrid.ColumnDefinitions.Count); Grid.SetRowSpan(f, parentGrid.RowDefinitions.Count); } } double duration = (double)parentGrid.GetValue(GridExtension.TransitionDurationProperty); Storyboard sb = new Storyboard(); DoubleAnimation daObj = new DoubleAnimation(); daObj.Duration = TimeSpan.FromMilliseconds(duration); daObj.BeginTime = TimeSpan.FromMilliseconds(duration / 2); daObj.EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut }; daObj.To = 1; Storyboard.SetTarget(daObj, f); Storyboard.SetTargetProperty(daObj, new PropertyPath(FrameworkElement.OpacityProperty)); DoubleAnimation daBrush = new DoubleAnimation(); daBrush.Duration = TimeSpan.FromMilliseconds(duration); daBrush.EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut }; daBrush.To = .3; Storyboard.SetTarget(daBrush, b); Storyboard.SetTargetProperty(daBrush, new PropertyPath(Brush.OpacityProperty)); sb.Children.Add(daObj); sb.Children.Add(daBrush); parentGrid.SetValue(GridExtension.TransitionProperty, sb); sb.SetValue(GridExtension.TweenedObjectProperty, f); sb.Begin(); sb.Completed += (s, e) => { parentGrid.IsHitTestVisible = true; }; } #endregion #region Child object properties /// /// Allow to define the object to maximize /// /// /// public static FrameworkElement GetObjectToMaximize(DependencyObject obj) { return (FrameworkElement)obj.GetValue(ObjectToMaximizeProperty); } public static void SetObjectToMaximize(DependencyObject obj, FrameworkElement value) { obj.SetValue(ObjectToMaximizeProperty, value); } // Using a DependencyProperty as the backing store for IsMaximizedSource. This enables animation, styling, binding, etc... public static readonly DependencyProperty ObjectToMaximizeProperty = DependencyProperty.RegisterAttached("ObjectToMaximize", typeof(FrameworkElement), typeof(FrameworkElement), new PropertyMetadata(null, IsMaximizedSource_DependencyPropertyChangedCallback)); static void IsMaximizedSource_DependencyPropertyChangedCallback(DependencyObject dobj, DependencyPropertyChangedEventArgs ea) { var fe = dobj as FrameworkElement; if (fe == null) return; if (dobj is ButtonBase) { var btn = fe as ButtonBase; btn.Click += btn_Click; } else { fe.MouseLeftButtonDown += fe_MouseLeftButtonDown; } fe.Unloaded += fe_Unloaded; } static void btn_Click(object sender, RoutedEventArgs e) { var dobj = sender as DependencyObject; var objToMaximize = dobj.GetValue(GridExtension.ObjectToMaximizeProperty) as DependencyObject; ToggleIsMaximized(objToMaximize); } static void fe_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { var dobj = sender as DependencyObject; var objToMaximize = dobj.GetValue(GridExtension.ObjectToMaximizeProperty) as DependencyObject; ToggleIsMaximized(objToMaximize); } private static void ToggleIsMaximized(DependencyObject dobj) { var newIsMaxValue = (bool)dobj.GetValue(GridExtension.IsMaximizedProperty); dobj.SetValue(GridExtension.IsMaximizedProperty, !newIsMaxValue); } static void fe_Unloaded(object sender, RoutedEventArgs e) { var fe = sender as FrameworkElement; if (sender is ButtonBase) { var btn = fe as ButtonBase; btn.Click -= btn_Click; } else { fe.MouseLeftButtonDown -= fe_MouseLeftButtonDown; } fe.Unloaded -= fe_Unloaded; } #endregion } public struct GridCoordinates { internal int RowIndex; internal int ColumnIndex; internal int RowSpan; internal int ColumnSpan; } }