自定義控制元件在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;
}
}