• WPF必须掌握的技能之自定义控件——实战:自制上传文件显示进度按钮


    自定义控件在WPF开发中是很常见的,有时候某些控件需要契合业务或者美化统一样式,这时候就需要对控件做出一些改造。

    话不多说直接看效果

    默认效果:
    image
    上传效果:

    image
    image

    按钮设置圆角

    因为按钮本身没有CornerRadius属性,所以只能重写Button的控件模板。

    <Style TargetType="Button">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button">
                        <Border CornerRadius="5"
                                Width="{TemplateBinding Width}"
                                Background="{TemplateBinding Background}"
                                BorderThickness="1"
                                Height="{TemplateBinding Height}">
                            <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
                                              VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                        Border>
                    ControlTemplate>
                Setter.Value>
            Setter>
        Style>
    

    在按钮的模板中加入一个Border即可,但是按钮本身没有CornerRadius属性,就没办法使用TemplateBinding ,只能写死在样式,那肯定不行,所以我们就需要拓展一下Button按钮。

    1.创建一个类MyProgressButton继承Button类,由于是新创建的一个类,所以我们可以直接使用依赖属性来完成这件事,在MyProgressButton中定义一个圆角弧度的依赖属性。

    public CornerRadius CornerRadius
            {
                get { return (CornerRadius)GetValue(CornerRadiusProperty); }
                set { SetValue(CornerRadiusProperty, value); }
            }
    
            public static readonly DependencyProperty CornerRadiusProperty =
                DependencyProperty.Register(nameof(CornerRadius), typeof(CornerRadius), typeof(MyProgressButton), new PropertyMetadata(default));
    

    2.创建一个ProgressButtonStyle.xaml的资源文件,针对MyProgressButton定义一些样式,包括弧度的绑定和鼠标移入移出的阴影效果,让我们的按钮立体起来

    <Style TargetType="local:MyProgressButton">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="local:MyProgressButton">
                        <Border CornerRadius="{TemplateBinding CornerRadius}"
                                Width="{TemplateBinding Width}"
                                Background="{TemplateBinding Background}"
                                BorderThickness="1"
                                Height="{TemplateBinding Height}">
                            <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
                                              VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                        Border>
                    ControlTemplate>
                Setter.Value>
            Setter>
            <Style.Triggers>
                <Trigger Property="IsMouseOver" Value="False">
                    <Setter Property="Effect">
                        <Setter.Value>
                            <DropShadowEffect Color="#cccccc" Direction="270" ShadowDepth="2" Opacity="1" />
                        Setter.Value>
                    Setter>
                Trigger>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Effect" >
                        <Setter.Value>
                            <DropShadowEffect Color="#bbbbbb" Direction="270" ShadowDepth="2" Opacity="1" />
                        Setter.Value>
                    Setter>
                Trigger>
            Style.Triggers>
        Style>
    

    3.最后在主界面将MyProgressButton的命名控件加入进来,并且用xaml创建一个MyProgressButton按钮,自定义一些属性,并且将ProgressButtonStyle.xaml样式加入到App.xaml中

    <Window x:Class="ProgressButton.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:ProgressButton"
            mc:Ignorable="d"
            Title="MainWindow" Height="450" Width="800">
        <Grid>
            <local:MyProgressButton Content="上传文件" 
                                    Foreground="#555555"
                                    Cursor="Hand"
                                    FontSize="14"
                                    CornerRadius="5"
                                    HorizontalAlignment="Center"
                                    VerticalAlignment="Center"
                                    Height="40" Width="135" 
                                    Background="Salmon"
                                    x:Name="upload_btn">
            local:MyProgressButton>
        Grid>
    Window>
    
    
    <Application.Resources>
            <ResourceDictionary>
                <ResourceDictionary.MergedDictionaries>
                    <ResourceDictionary Source="pack://application:,,,/ProgressButton;component/Button/ProgressButtonStyle.xaml">ResourceDictionary>
                ResourceDictionary.MergedDictionaries>
            ResourceDictionary>
        Application.Resources>
    

    看看效果:
    image

    按钮上传文件相关定义

    1.定义按钮类型MyProgressButton文件上传进度,是否上传,以及上传时按钮背景色三个依赖属性

    /// 
            /// 文件上传进度
            /// 
            public double Progress
            {
                get { return (double)GetValue(ProgressProperty); }
                set { SetValue(ProgressProperty, value); }
            }
    
            public static readonly DependencyProperty ProgressProperty =
                DependencyProperty.Register(nameof(Progress), typeof(double), typeof(MyProgressButton), new PropertyMetadata(double.NegativeZero, OnProgressChanged));
    
            /// 
            /// 文件是否上传
            /// 
            public bool IsUploading
            {
                get { return (bool)GetValue(IsUploadingProperty); }
                set { SetValue(IsUploadingProperty, value); }
            }
    
            public static readonly DependencyProperty IsUploadingProperty =
                DependencyProperty.Register(nameof(IsUploading), typeof(bool), typeof(MyProgressButton), new PropertyMetadata(false, OnIsUploadingChanged));
    
            /// 
            /// 上传时按钮背景色
            /// 
            public Color UploadingColor
            {
                get { return (Color)GetValue(UploadingColorProperty); }
                set { SetValue(UploadingColorProperty, value); }
            }
    
            // Using a DependencyProperty as the backing store for UploadingColor.  This enables animation, styling, binding, etc...
            public static readonly DependencyProperty UploadingColorProperty =
                DependencyProperty.Register(nameof(UploadingColor), typeof(Color), typeof(MyProgressButton), new PropertyMetadata(Colors.White));
    

    2.如何实现按钮内部的进度显示?有几种办法,比如使用渐进色修改偏移,或者按钮内部套一个进度条,或者按钮内部放两个不同颜色的块控件,动态修改两者的长度。我们选择第一种。

    在Progress属性被修改的时候,我们动态修改下按钮内部渐进色的偏移。为ProgressProperty添加值变化的回调。

    private static void OnProgressChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                var btn = d as MyProgressButton;
                var progress = (double)e.NewValue;
                if (progress != double.NegativeZero)
                {
                    Brush brush = null;
                    if ((brush = btn.Background as LinearGradientBrush) != null) //如果按钮本身是线性渐变色则直接修改偏移
                    {
                        GradientStopCollection collections =
                            brush.GetValue(GradientBrush.GradientStopsProperty) as GradientStopCollection;
    
                        collections[1].Offset = collections[0].Offset = progress / 100;
                    }
                    else //如果本身不是线性渐变色则将背景色修改为线性渐变色
                    {
                        LinearGradientBrush linearGradientBrush = new LinearGradientBrush();
                        //设置一个横向的线
                        linearGradientBrush.StartPoint = new Point(0, 0.5);
                        linearGradientBrush.EndPoint = new Point(1, 0.5);
    
                        GradientStop gradientStop = new GradientStop(); //右边的颜色,即按钮设置的上传时背景色
                        gradientStop.Color = btn!.UploadingColor;
    
                        GradientStop gradientStop1 = new GradientStop();//左边的颜色,即按钮原本的颜色
                        gradientStop1.Color = (btn!.Background as SolidColorBrush)!.Color;
    
                        gradientStop.Offset = gradientStop1.Offset = progress / 100;
    
                        linearGradientBrush.GradientStops.Add(gradientStop1);
                        linearGradientBrush.GradientStops.Add(gradientStop);
                        btn.Background = linearGradientBrush;
                    }
                }
            }
    

    在上传文件的时候,将按钮置为禁用,防止重复点击。写一个IsUploadingProperty属性的值变化的回调。

    private static void OnIsUploadingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                var btn = d as MyProgressButton;
                if ((bool)e.NewValue)
                {
                    btn!.IsEnabled = false;
                }
                else
                {
                    btn!.IsEnabled = true;
                }
            }
    
    测试代码
    Binding binding = new Binding();
                binding.Source = this;
                binding.Path = new PropertyPath("Progress");
                binding.Mode = BindingMode.OneWay;
                upload_btn.SetBinding(MyProgressButton.ProgressProperty, binding);
    
                Binding binding1 = new Binding();
                binding1.Source = this;
                binding1.Path = new PropertyPath("IsUploading");
                binding1.Mode = BindingMode.OneWay;
                upload_btn.SetBinding(MyProgressButton.IsUploadingProperty, binding1);
    
    private async void upload_btn_Click(object sender, RoutedEventArgs e)
            {
                IsUploading = true;
                try
                {
                    using (FileStream fread = new FileStream("d://d3dcompiler_47.dll", FileMode.Open, FileAccess.Read))
                    using (FileStream fwrite = new FileStream("d://d3dcompiler_47_copy.dll", FileMode.OpenOrCreate, FileAccess.Write))
                    {
                        var allLength = new FileInfo("d://d3dcompiler_47.dll").Length;
                        long copyedBytes = 0;
                        while (true)
                        {
                            var buffer = ArrayPool<byte>.Shared.Rent(1024 * 10);
                            try
                            {
                                var len = await fread.ReadAsync(buffer, 0, buffer.Length);
                                if (len > 0)
                                {
                                    await fwrite.WriteAsync(buffer[..len]);
                                    copyedBytes += len;
                                    Progress = copyedBytes * 100 / allLength;
                                    await Task.Delay(20);
                                }
                                else
                                {
                                    break;
                                }
                            }
                            catch { break; }
                            finally
                            {
                                ArrayPool<byte>.Shared.Return(buffer);
                            }
                        }
    
                        MessageBox.Show("上传成功");
                    };
                }
                finally
                {
                    IsUploading = false;
                }
            }
    
  • 相关阅读:
    docker和K8S环境xxl-job定时任务不执行问题总结
    vue3 | HighCharts实战自定义封装之径向条形图
    汇编语言王爽第四版17.3完程可运行可调试
    【C指针详解】初阶篇
    【Web】Ctfshow SSTI刷题记录1
    笔记本开启WiFi
    基于开源模型搭建实时人脸识别系统(五):人脸跟踪
    pytest:如何在测试中编写和报告断言
    CANOE功能介绍
    批量清理Unity项目Library目录 降低项目空闲占用空间
  • 原文地址:https://www.cnblogs.com/qwqwQAQ/p/17473005.html