Cool question! I actually did the spell quite recently.
ideally you need something like
<Grid x:Name="LayoutRoot"> <Grid x:Name="Overlay" Panel.ZIndex="1000" Visibility="Collapsed"> </Grid> <ContentControl x:Name="MainContent" /> </Grid>
Now, instead of compressing the contents, the Overlay grid will be on top of it, looking like a charm panel! all with xaml
If you have more questions about this, give me a scream!
Change my implementation of Charm - feel free to use it for exaggeration!
public class SlidePanel : ContentControl { static SlidePanel() { DefaultStyleKeyProperty.OverrideMetadata(typeof(SlidePanel), new FrameworkPropertyMetadata(typeof(SlidePanel))); } public SlidePanel() { EventManager.RegisterClassHandler(typeof(SlidePanel), SlidePanel.MouseEnterEvent, new RoutedEventHandler(OnLocalMouseEnter)); EventManager.RegisterClassHandler(typeof(SlidePanel), SlidePanel.MouseLeaveEvent, new RoutedEventHandler(OnLocalMouseLeave)); } #region Mouse Handlers private static void OnLocalMouseEnter(object sender, RoutedEventArgs e) { SetExpanded(sender, true); } private static void OnLocalMouseLeave(object sender, RoutedEventArgs e) { SetExpanded(sender, false); } private static void SetExpanded(object sender, bool expanded) { SlidePanel panel = sender as SlidePanel; if (panel != null) { panel.IsExpanded = expanded; } } #endregion Mouse Handlers #region Panel Width public double PanelWidth { get { return (double)GetValue(PanelWidthProperty); } set { SetValue(PanelWidthProperty, value); } } // Using a DependencyProperty as the backing store for PanelWidth. This enables animation, styling, binding, etc... public static readonly DependencyProperty PanelWidthProperty = DependencyProperty.Register("PanelWidth", typeof(double), typeof(SlidePanel), new UIPropertyMetadata(5.0)); #endregion Panel Width #region Closed Width public double ClosedWidth { get { return (double)GetValue(ClosedWidthProperty); } set { SetValue(ClosedWidthProperty, value); } } // Using a DependencyProperty as the backing store for ClosedWidth. This enables animation, styling, binding, etc... public static readonly DependencyProperty ClosedWidthProperty = DependencyProperty.Register("ClosedWidth", typeof(double), typeof(SlidePanel), new UIPropertyMetadata(5.0, new PropertyChangedCallback(OnClosedWidthChange))); #endregion Closed Width #region Expanded Property public bool IsExpanded { get { return (bool)GetValue(IsExpandedProperty); } set { SetValue(IsExpandedProperty, value); } } // Using a DependencyProperty as the backing store for IsExpanded. This enables animation, styling, binding, etc... public static readonly DependencyProperty IsExpandedProperty = DependencyProperty.Register("IsExpanded", typeof(bool), typeof(SlidePanel), new UIPropertyMetadata(false, new PropertyChangedCallback(OnExpandedChanged))); #endregion Expanded Property #region Property Changes private static void OnExpandedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (e.NewValue == e.OldValue) return; SlidePanel panel = d as SlidePanel; if (panel == null) return; bool newVal = (bool)e.NewValue; panel.IsExpanded = newVal; bool expanded = (bool)panel.GetValue(IsExpandedProperty); Storyboard widthAnimation = AnimationHelper.CreateDoubleAnimation<SlidePanel>(panel, expanded, (p, a) => { a.From = (double)p.GetValue(SlidePanel.ClosedWidthProperty); a.To = (double)p.GetValue(SlidePanel.PanelWidthProperty); }, (p, a) => { a.From = (double)p.GetValue(SlidePanel.WidthProperty); a.To = (double)p.GetValue(SlidePanel.ClosedWidthProperty); }, new TimeSpan(0, 0, 0, 0, 300), WidthProperty); Timeline opacity = AnimationHelper.DoubleAnimation(0.0, 1.0, expanded, new TimeSpan(0, 0, 0, 0, 300), OpacityProperty); Storyboard.SetTargetName(opacity, panel.Name); Storyboard.SetTargetProperty(opacity, new PropertyPath(OpacityProperty)); widthAnimation.Children.Add(opacity); widthAnimation.Begin(panel); } private static void OnClosedWidthChange(DependencyObject d, DependencyPropertyChangedEventArgs e) { SlidePanel panel = d as SlidePanel; if (panel != null) panel.Width = (double)e.NewValue; } #endregion Property Changes }
The little trick I found was to set the opacity to 0 when it was not expanded, but set the width to 10, this allows the user to place the mouse on the side of the screen and then it will appear after the second or so .. amuses.
Change - as requested .. AnimationHelper.
public class AnimationHelper { public static Timeline DoubleAnimation(double from, double to, bool modifier, TimeSpan duration, DependencyProperty property) { DoubleAnimation animation = new DoubleAnimation(); if (modifier) { animation.From = from; animation.To = to; } else { animation.To = from; animation.From = to; } animation.Duration = new Duration(duration); return animation; } public static Storyboard CreateDoubleAnimation<T>(T control, bool modifier, double from, double to, TimeSpan duration, DependencyProperty property) where T : Control { return AnimationHelper.CreateDoubleAnimation<T>(control, modifier, (p, a) => { a.From = from; a.To = to; }, (p, a) => { a.From = to; a.To = from; }, duration, property); } public static Storyboard CreateDoubleAnimation<T>(T control, bool modifier, Action<T, DoubleAnimation> onTrue, Action<T, DoubleAnimation> onFalse, TimeSpan duration, DependencyProperty property) where T : Control { if (control == null) throw new ArgumentNullException("control"); DoubleAnimation panelAnimation = new DoubleAnimation(); if (modifier) { if (onTrue != null) onTrue.Invoke(control, panelAnimation); } else { if (onFalse != null) onFalse.Invoke(control, panelAnimation); } panelAnimation.Duration = new Duration(duration); Storyboard sb = new Storyboard(); Storyboard.SetTargetName(panelAnimation, control.Name); Storyboard.SetTargetProperty(panelAnimation, new PropertyPath(property)); sb.Children.Add(panelAnimation); return sb; } }