如何在WPF中创建类似Windows 8样式的应用程序工具栏?

4
我原打算开发一款Windows 8风格的应用程序(Metro),但是后来发现不支持使用双屏,而这正是我的应用程序所需之处。现在我正在将我的应用程序重设计为WPF桌面应用程序,但我仍希望模仿Windows 8应用程序中的一些精美设计特征。其中一个设计特征是飞出栏,通常用于Windows 8风格的应用程序:底部应用栏用于命令、顶部导航栏、右侧Charm在所有应用程序中均可使用。它们共同拥有的设计是临时飞出面板,覆盖在当前窗口布局之上。我的问题是:在WPF中如何创建类似的效果?我没有问题来创建一个主网格与隐藏的底部行,使其可见以显示一些常见的命令按钮。但是,最好的方法是将其飞出并覆盖在标准布局之上,而不是挤压它。我知道可以在当前窗口上打开新窗口,但这会创建糟糕的代码设计,并难以获得漂亮的外观。我更愿意在同一窗口中完成它。
1个回答

11
很棒的问题!我最近实际上已经完成了魅力栏..

理想情况下,您需要像这样的东西

<Grid x:Name="LayoutRoot">

 <Grid x:Name="Overlay" Panel.ZIndex="1000" Visibility="Collapsed">
    <!-- This is where your slide out control is going to go -->
  </Grid>

  <!-- Use whatever layout you need -->
  <ContentControl x:Name="MainContent" />

</Grid>

现在,与其压缩内容,叠加网格将会在其上方,类似于魅力栏!所有这些都是通过XAML实现的。
如果您对此有任何疑问,请告诉我!
编辑;我的魅力实现 - 可以自由使用作为灵感!
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
    }

我发现一个小技巧,就是在未展开时将不透明度设置为0,但将宽度设置为10,这样用户可以将鼠标放在屏幕侧边,然后在一两秒后它就会出现...谢谢。
注:如需编辑,请使用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;
        }
    }

谢谢你的回答!!!实际上我很接近了...我尝试了ZIndex但没有让它工作。缺失的部分是对如何将子项添加到网格的简单误解。我以为你必须定义ColumnDefinitions和RowDefinitions才能使其工作。在不指定行或列的情况下向主网格添加子容器有所不同。我还发现透明度和最小高度的建议非常有用。这非常好地模仿了Windows 8应用程序风格。 - Jakob Lithner
大家好,我有点晚到这里,但我想知道你们是从哪里引用AnimationHelper.CreateDoubleAnimation类/方法的?我已经找了一些,但我发现那个类的所有内容都在某些第三方项目中... - Keith Vinson
@KeithVinson 嗨,那是我当时写的一个类..它并不特别,只是一个包装动画的类。据我记得,它只是包装了这个类http://msdn.microsoft.com/en-us/library/system.windows.media.animation.doubleanimation(v=vs.110).aspx - Steoates
嗨,Steoates。明白了,我在谷歌搜索时看到了那个类,所以我就按照同样的方式去做。感谢您的回复... - Keith Vinson
@Steoates 做得很棒!有没有把你的AnimationHelper类添加到答案中的机会? - Dan Smith
1
@DanSmith 我进行了一些调查,最终找到了它...请看答案 :) - Steoates

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接