WPF 自定义控件实战:自制上传文件进度按钮

296 阅读5分钟

前言

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

按钮设置圆角

按钮上传文件相关定义

测试代码

正文

话不多说直接看效果

默认效果

上传效果

按钮设置圆角

因为按钮本身没有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>

看看效果:

按钮上传文件相关定义

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

/// <summary>
/// 文件上传进度
/// </summary>
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));

/// <summary>
/// 文件是否上传
/// </summary>
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));

/// <summary>
/// 上传时按钮背景色
/// </summary>
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);
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;
    }
}

总结

本文通过实战案例,详细介绍了在 WPF 中自定义控件的技能,重点打造了一个上传文件并显示进度的按钮。从控件的设计到功能实现,一步步展示了如何结合 XAML 布局与 C# 代码,实现文件上传及进度动态展示。

过程不仅巩固了 WPF 自定义控件的基础知识,还提升了开发具有特定功能控件的能力,为大家在实际项目中定制个性化控件提供了实用参考。

最后

如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!你的支持是我继续分享知识的动力。如果有任何疑问或需要进一步的帮助,欢迎随时留言。

也可以加入微信公众号 [DotNet技术匠] 社区,与其他热爱技术的同行一起交流心得,共同成长!

优秀是一种习惯,欢迎大家留言学习!

作者:BruceNeter

出处:cnblogs.com/qwqwQAQ/p/17473005.html

声明:网络内容,仅供学习,尊重版权,侵权速删,歉意致谢!