WPF 用装饰器制作抽屉效果

 

wpf实现抽屉效果,一般就一个动画显示就完事了,我这用到了,就研究了一下,用装饰器给控件添加遮罩层,然后在上面添加抽屉控件,虽然麻烦了点,也算是自己研究的成果了。

看看效果:

image

下面就看看代码:

public class SimpleAdorner : Adorner
    {
        private UIElement child;
        public SimpleAdorner(UIElement adornedElement) : base(adornedElement)
        {
        }
        //Adorner 直接继承自 FrameworkElement,
        //没有Content和Child属性,
        //自己添加一个,方便向其中添加我们的控件
        public UIElement Child
        {
            get => child;
            set
            {
                if (value == null)
                {
                    RemoveVisualChild(child);
                }
                else
                {
                    AddVisualChild(value);
                }
                child = value;
            }
        }
        //重写VisualChildrenCount 表示此控件只有一个子控件
        protected override int VisualChildrenCount => 1;
        //控件计算大小的时候,我们在装饰层上添加的控件也计算一下大小
        protected override Size ArrangeOverride(Size finalSize)
        {
            child?.Arrange(new Rect(finalSize));
            return finalSize;
        }
        //重写GetVisualChild,返回我们添加的控件
        protected override Visual GetVisualChild(int index)
        {
            if (index == 0 && child != null) return child;
            return base.GetVisualChild(index);
        }
    }

然后新建一个类:

[TemplatePart(Name = DrawerGrid,Type =typeof(Grid))]
    [TemplatePart(Name = CloseButton,Type =typeof(Button))]
    public class GDrawer : ContentControl
    {
        public Action Closed;
        private const string DrawerGrid = "DrawerGrid";
        private const string CloseButton = "CloseButton";

        private Grid _drawerGrid;
        private Button _closeButton;
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            _drawerGrid = GetTemplateChild(DrawerGrid) as Grid;
            _closeButton = GetTemplateChild(CloseButton) as Button;
            _closeButton.Click += (s, e) => IsOpen = false;
            Loaded += OnLoaded;
        }
        private void OnLoaded(object sender, RoutedEventArgs e)
        {
            IsOpen = true;
        }

        public bool IsOpen
        {
            get { return (bool)GetValue(IsOpenProperty); }
            set { SetValue(IsOpenProperty, value); }
        }
        public static readonly DependencyProperty IsOpenProperty =
            DependencyProperty.Register("IsOpen", typeof(bool), typeof(GDrawer), new PropertyMetadata(false,(s,e)=> {
                var drawer = s as GDrawer;
                if(e.NewValue is bool b && b)
                {
                    drawer.StartAnimationIn();
                }
                else
                {
                    drawer.StartAnimationOut();
                }
            }));

        private async void StartAnimationIn(float seconds=0.3f)
        {
            var sb = new Storyboard();
            var offset = _drawerGrid.ActualWidth;
            var animation = new ThicknessAnimation
            {
                Duration = new Duration(TimeSpan.FromSeconds(seconds)),
                From = new Thickness(-offset,0 , offset,0 ),
                To = new Thickness(0)
            };
            Storyboard.SetTargetProperty(animation, new PropertyPath("Margin"));
            sb.Children.Add(animation);
            sb.Begin(_drawerGrid);
            await Task.Delay((int)(seconds * 1000));
            
        }
        private async void StartAnimationOut(float seconds=0.3f)
        {
            var sb = new Storyboard();
            var offset = _drawerGrid.ActualWidth;
            var animation = new ThicknessAnimation
            {
                Duration = new Duration(TimeSpan.FromSeconds(seconds)),
                From = new Thickness(0),
                To = new Thickness(-offset, 0, offset, 0),
            };
            Storyboard.SetTargetProperty(animation, new PropertyPath("Margin"));
            sb.Children.Add(animation);
            sb.Begin(_drawerGrid);
            await Task.Delay((int)(seconds * 1000));
            Closed?.Invoke();
        }
    }

这个就是抽屉效果的实现类了,

然后,在App.xaml里添加样式和资源 :

<Style TargetType="local:GDrawer">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:GDrawer">
                    <Grid x:Name="DrawerGrid" 
                              MinWidth="300" MinHeight="100" 
                              HorizontalAlignment="Left" 
                              VerticalAlignment="Stretch"
                              Background="White">
                        <Button x:Name="CloseButton" Margin="10" Width="50" Height="30" HorizontalAlignment="Right" VerticalAlignment="Top">关闭</Button>
                            <ContentPresenter />
                        </Grid>
                
                </ControlTemplate>
            </Setter.Value>
        </Setter>
</Style>
    <local:GDrawer x:Key="LeftDrawer">
        <TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="18" Foreground="Red">这是内容</TextBlock>
    </local:GDrawer>

最后,来到MainWindow窗口,xaml代码如下:

<Window x:Class="WPFDemos.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WPFDemos"
        mc:Ignorable="d"
        x:Name="widnow"
        WindowStartupLocation="CenterScreen"
        Title="title" Height="500" Width="1000">
    <Grid x:Name="grid" Background="LightBlue">
        <!--定义装饰层-->
        <AdornerDecorator/>
        <Button Click="Button_Click" HorizontalAlignment="Center" VerticalAlignment="Center">打开</Button>
    </Grid>
</Window>

MainWindow窗体后台代码:

public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

        }
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            ShowModal(grid);
        }
        SimpleAdorner _adorner = null;
        public void ShowModal(Visual visual)
        {
            //获取visual上面的第一个AdornerLayer
            AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(visual);
            //创建我们定义的Adorner
            var drawer = FindResource("LeftDrawer") as GDrawer;
            drawer.Closed = () => adornerLayer.Remove(_adorner);

            _adorner = _adorner?? new SimpleAdorner(adornerLayer)
            {
                Child = new Border()
                {
                    Background = new SolidColorBrush(Color.FromArgb(150, 0, 0, 0)),
                    Child = drawer
                }
            };
            adornerLayer.Add(_adorner);
        }
    }

这样就完成了

© 版权声明
THE END
喜欢就支持一下吧
点赞15 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情

    暂无评论内容