自定义控件在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);
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);
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;
}
} MessageBox.Show("上传成功");
};
}
catch(Exception ex)
{ }
finally
{
IsUploading = false;
}
}

WFP必须掌握的技能之自定义控件——实战:自制上传文件显示进度按钮的更多相关文章

  1. Postman系列三:Postman中post接口实战(上传文件、json请求)

    一:接口测试过程中GET请求与POST请求的主要区别 从开发角度我们看get与post的主要区别是:1.Get是用来从服务器上获得数据,而Post是用来向服务器上传递数据:2.Get安全性比Post低 ...

  2. [实战]MVC5+EF6+MySql企业网盘实战(4)——上传头像

    写在前面 最近又开始忙了,工期紧比较赶,另外明天又要去驾校,只能一个功能一个功能的添加了,也许每次完成的功能确实不算什么,等将功能都实现了,然后在找一个好点的ui对前端重构一下. 系列文章 [EF]v ...

  3. 《selenium2 python 自动化测试实战》(13)——上传文件

    看代码: # coding: utf-8 from selenium import webdriver from time import sleep driver = webdriver.Firefo ...

  4. Android自定义控件实例,圆形头像(图库 + 裁剪+设置),上传头像显示为圆形,附源码

    Android项目开发中经常会遇见需要实现圆角或者圆形的图片功能,如果仅仅使用系统自带的ImageView控件显然无法实现此功能,所以通过系列文章的形式由简到繁全方位的介绍一下此功能的实现,巩固一下自 ...

  5. 【JavaEE企业应用实战学习记录】servlet3.0上传文件

    <%-- Created by IntelliJ IDEA. User: Administrator Date: 2016/10/6 Time: 14:20 To change this tem ...

  6. Flask实战第57天:UEditor编辑器集成以及配置上传文件到七牛

    相关链接 UEditor:http://ueditor.baidu.com/website/​ 下载地址:http://ueditor.baidu.com/website/download.html# ...

  7. PHP使用FTP上传文件到服务器(实战篇)

    我们在做开发的过程中,上传文件肯定是避免不了的,平常我们的程序和上传的文件都在一个服务器上,我们也可以使用第三方sdk上传文件,但是文件在第三方服务器上.现在我们使用PHP的ftp功能把文件上传到我们 ...

  8. java实用技能 上传文件 等等

    1.IOS  AES对称加密,加密结果不同,问题解决 IOS http post请求,使用AFNetworing 框架,默认请求content-type为application/json ,所以无法使 ...

  9. Selenium 2自动化测试实战18(上传文件)

    一.上传文件 上传文件是比较常见的web功能之一,但WebDriver没有提供专门用于上传的方法. 一般web页面的上传功能的操作需要单击“上传”按钮后打开本地的Window窗口,从窗口选择本地文件进 ...

  10. Day12-微信小程序实战-交友小程序-搭建服务器与上传文件到后端

    要搞一个小型的cms内容发布系统 因为小程序上线之后,直接对数据库进行操作的话,慧出问题的,所以一般都会做一个管理系统,让工作人员通过这个管理系统来对这个数据库进行增删改查 微信小程序其实给我们提供了 ...

随机推荐

  1. PHP 微信三方平台代公众号发起网页授权 获取用户信息

    1.获取code 2.通过授权回调地址的code获取用户access_token和open_id 3.通过access_token和open_id 获取用户基本信息 class wx_user { p ...

  2. 相同基准点的多个rvt BIM模型数据配准后位置有错位偏差问题处理

    场景:提供的bim模型数据包含多个rvt格式数据,这些数据具有相同的基准点,如: 在使用ArcGIS Pro处理了其中两份rvt格式数据(建筑和给排水),发布后在前端展示发现数据错位: 红色管线的给排 ...

  3. kubernetes核心实战(四)--- Deployments

    6.Deployments(重点) 一个 Deployment 控制器为 Pods和 ReplicaSets提供描述性的更新方式. 描述 Deployment 中的 desired state,并且 ...

  4. pandas之concat链接操作

    Pandas 通过 concat() 函数能够轻松地将 Series 与 DataFrame 对象组合在一起,函数的语法格式如下: pd.concat(objs,axis=0,join='outer' ...

  5. 【FINALE】NOIP2022 退役记 || THE END.

    我的停课生活相册 - password:1007 目录 Day -4 Day -2 Day -1 Day 1 2022/11/26 NOIP 2022 OI 浅忆录 Day -4 好冷啊.有了那么点冬 ...

  6. ChatGPT API接口编程基础与使用技巧

    总结/朱季谦 趁着这周末空闲时间,在研读完OpenAi官网文档的基础上,及时总结了这篇<ChatGPT API接口编程基础与使用技巧>. 本文大部分内容是围绕编程方面,包括ChatGPT模 ...

  7. CTFshow愚人杯-被遗忘的反序列化

    这题虽然只有100的分值,但是我觉得它涉及到的东西还蛮多的,写个随笔记录一下. 题目 <?php # 当前目录中有一个txt文件哦 error_reporting(0); show_source ...

  8. 关于Java中值传递和址传递

    参数传递在Java中有两种类型 值和址 其实本质都是一份拷贝 在调用函数的时候 进行压栈 传进来的参数会被开辟一份新的空间 传基本类型是把值传过去 传引用数据类型是实例指向实参 void m(int ...

  9. (一)pyahocorasick和marisa_trie,字符串快速查找的python包,自然语言处理,命名实体识别可用的高效包

    Pyahocorasick Pyahocorasick是一个基于AC自动机算法的字符串匹配工具.它可以用于快速查找多个短字符串在一个长字符串中的所有出现位置.Pyahocorasick可以在构建状态机 ...

  10. CSS 点击穿透pointer-events

    在项目中,当需要展示一个元素在最顶层,但又不想让它影响下层的交互,可以pointer-events:none pointer-events介绍 pointer-events: auto | none ...