XAML C# WPF中的矩形按钮,带有沿轮廓沿着的进度条

slhcrj9b  于 11个月前  发布在  C#
关注(0)|答案(2)|浏览(163)

我需要在C# 6.0 WPF中创建一个圆角矩形按钮。这个按钮应该有一个进度条,而不是一个顺时针填充的框架(从顶部边框的中间开始)。
我尝试了很多方法来做到这一点,甚至做了一个稍微可行的版本使用路径,但没有圆角,这是我需要的。
请告诉我如何使用xaml标记和如何管理进度。
这是一个圆角的常规按钮:

<UserControl x:Class="Example.ProgressButton"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Example"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid HorizontalAlignment="Center" VerticalAlignment="Center">
        <Button x:Name="button" Width="150" Height="60" Content="Click me" Background="LightGray" BorderBrush="Transparent">
            <Button.Style>
                <Style TargetType="{x:Type Button}">
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type Button}">
                                <Border CornerRadius="10" Background="{TemplateBinding Background}" BorderBrush="Green" BorderThickness="2">
                                    <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
                                </Border>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </Button.Style>
        </Button>
    </Grid>
</UserControl>

字符串
最初,框架不应可见(按钮的大小不应随框架的变化而变化);单击时,进度条从上边框的中间开始顺时针填充。填充100%后,按钮应如图所示:x1c 0d1x
此外,进度条的填充即使在圆角上也应该平滑。
我完全不知道该怎么做。请帮帮我。

**更新:**我可能没有很好地解释我的问题,我会尝试澄清。我需要将边框本身作为进度条(作为元素或动画,这并不重要),它应该填充指定的时间,大约如图:load_button

qcbq4gxm

qcbq4gxm1#

您可以创建一个派生的Border控件,在其边框顶部绘制一个描边的几何体。描边的长度可以由用于绘制边框几何体的Pen的虚线数组控制。
该控件声明了两个附加属性ProgressBrushProgressValue,它们是0到1范围内的双精度值。
下面的示例仅使用BorderThicknessLeft组件和CornerRadiusTopLeft组件,因此它不支持不规则的边界厚度或拐角半径。

public class ProgressBorder : Border
{
    public static readonly DependencyProperty ProgressBrushProperty = DependencyProperty.Register(
        nameof(ProgressBrush), typeof(Brush), typeof(ProgressBorder),
        new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));

    public static readonly DependencyProperty ProgressValueProperty = DependencyProperty.Register(
        nameof(ProgressValue), typeof(double), typeof(ProgressBorder),
        new FrameworkPropertyMetadata(0d, FrameworkPropertyMetadataOptions.AffectsRender));

    public Brush ProgressBrush
    {
        get => (Brush)GetValue(ProgressBrushProperty);
        set => SetValue(ProgressBrushProperty, value);
    }

    public double ProgressValue
    {
        get => (double)GetValue(ProgressValueProperty);
        set => SetValue(ProgressValueProperty, value);
    }

    protected override void OnRender(DrawingContext drawingContext)
    {
        base.OnRender(drawingContext);

        var w = RenderSize.Width;
        var h = RenderSize.Height;
        var t = BorderThickness.Left;
        var d = t / 2;
        var r = Math.Max(0, Math.Min(CornerRadius.TopLeft, Math.Min(w / 2 - t, h / 2 - t)));
        var geometry = new StreamGeometry();

        using (var dc = geometry.Open())
        {
            dc.BeginFigure(new Point(w / 2, d), true, true);

            dc.LineTo(new Point(w - d - r, d), true, true);
            dc.ArcTo(new Point(w - d, d + r), new Size(r, r), 0, false, SweepDirection.Clockwise, true, true);

            dc.LineTo(new Point(w - d, h - d - r), true, true);
            dc.ArcTo(new Point(w - d - r, h - d), new Size(r, r), 0, false, SweepDirection.Clockwise, true, true);

            dc.LineTo(new Point(d + r, h - d), true, true);
            dc.ArcTo(new Point(d, h - d - r), new Size(r, r), 0, false, SweepDirection.Clockwise, true, true);

            dc.LineTo(new Point(d, d + r), true, true);
            dc.ArcTo(new Point(d + r, d), new Size(r, r), 0, false, SweepDirection.Clockwise, true, true);

            dc.LineTo(new Point(w / 2, d), true, true);
        }

        var length = (2 * w + 2 * h + 4 * ((0.5 * Math.PI - 2) * r - t)) / t;
        var dashes = new double[] { ProgressValue * length, (1 - ProgressValue) * length };
        var pen = new Pen
        {
            Brush = ProgressBrush,
            Thickness = t,
            StartLineCap = PenLineCap.Flat,
            EndLineCap = PenLineCap.Flat,
            DashCap = PenLineCap.Flat,
            DashStyle = new DashStyle(dashes, 0),
            LineJoin = PenLineJoin.Round
        };

        drawingContext.DrawGeometry(null, pen, geometry);
    }
}

字符串
XAML中的一个示例用法:

<local:ProgressBorder
    Background="AliceBlue"
    BorderBrush="LightGray"
    ProgressBrush="Red"
    BorderThickness="10"
    CornerRadius="10,10,10,10">
    <TextBlock Text="Hello" Margin="10"/>
    <local:ProgressBorder.Triggers>
        <EventTrigger RoutedEvent="Loaded">
            <BeginStoryboard>
                <Storyboard>
                    <DoubleAnimation
                       Storyboard.TargetProperty="ProgressValue"
                       To="1.0" Duration="0:0:3"/>
                </Storyboard>
            </BeginStoryboard>
        </EventTrigger>
    </local:ProgressBorder.Triggers>
</local:ProgressBorder>

niwlg2el

niwlg2el2#

这是几个问题的组合,包括:

我很快地把一些东西打包在一起,让你开始。还有工作要做;)
x1c 0d1x的数据
Xaml:

<Window x:Class="WpfApp3.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:WpfApp3"
        mc:Ignorable="d"
        Title="MainWindow"
        Height="450"
        Width="800">
    <Window.Resources>

        <local:AngleToPointConverter x:Key="angleToPointConverter" />
        <local:AngleToIsLargeConverter x:Key="angleToIsLargeConverter" />

        <Style TargetType="Button">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button">
                        <Grid>
                            <Viewbox Stretch="Fill"
                                     ClipToBounds="True">
                                <Viewbox.Clip>
                                    <RectangleGeometry RadiusX="10"
                                                       RadiusY="10"
                                                       Rect="0,0,200,100" />
                                </Viewbox.Clip>
                                <Path Stroke="LightGray"
                                      StrokeThickness="100"
                                      Width="100"
                                      Height="100">
                                    <Path.Data>
                                        <PathGeometry>
                                            <PathFigure StartPoint="50,0">
                                                <ArcSegment RotationAngle="0"
                                                            SweepDirection="Clockwise"
                                                            Size="50,50"
                                                            Point="{Binding DataContext.Angle, Converter={StaticResource angleToPointConverter}, RelativeSource={RelativeSource FindAncestor, AncestorType=Button}}"
                                                            IsLargeArc="{Binding DataContext.Angle, Converter={StaticResource angleToIsLargeConverter}, RelativeSource={RelativeSource FindAncestor, AncestorType=Button}}">
                                                </ArcSegment>
                                            </PathFigure>
                                        </PathGeometry>
                                    </Path.Data>
                                </Path>
                            </Viewbox>
                            <Border CornerRadius="10"
                                    Background="Transparent"
                                    BorderBrush="Green"
                                    BorderThickness="2">
                                <ContentPresenter HorizontalAlignment="Center"
                                                  VerticalAlignment="Center" />
                            </Border>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <Window.DataContext>
        <local:ViewModel />
    </Window.DataContext>
    <Grid>
        <TextBlock Text="{Binding Progress}"
                   VerticalAlignment="Top" />
        <Button Width="200"
                Height="100"
                Content="Click me"
                Command="{Binding ClickCommand}" />
    </Grid>
</Window>

字符串
Cs:

namespace WpfApp3
{
    public class ViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler? PropertyChanged;

        public DelegateCommand ClickCommand { get; }

        public double Progress { get; set; }
        public double Angle => Progress / 100 * 360;

        public ViewModel()
        {
            var busy = false;

            ClickCommand = new(async o =>
            {
                busy = true;
                ClickCommand!.Update();

                for (int i = 1; i <= 100; i++)
                {
                    SetProgress(i);
                    await Task.Delay(20);
                }

                SetProgress(0);

                busy = false;
                ClickCommand.Update();

            }, o => !busy);
        }

        void SetProgress(double value)
        {
            Progress = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Progress)));
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Angle)));
        }
    }

    public class DelegateCommand : ICommand
    {
        public event EventHandler? CanExecuteChanged;

        readonly Action<object?> execute;
        readonly Predicate<object?> canExecute;

        public DelegateCommand(Action<object?> execute, Predicate<object?> canExecute)
        {
            this.execute = execute;
            this.canExecute = canExecute;
        }

        public void Update() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);

        public bool CanExecute(object? parameter) => canExecute(parameter);

        public void Execute(object? parameter) => execute(parameter);
    }

    class AngleToPointConverter : IValueConverter
    {

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            double angle = (double)value;
            double radius = 50;
            double piang = angle * Math.PI / 180;

            double px = Math.Sin(piang) * radius + radius;
            double py = -Math.Cos(piang) * radius + radius;

            return new Point(px, py);
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

    class AngleToIsLargeConverter : IValueConverter
    {

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            double angle = (double)value;

            return angle > 180;
        }

        public object ConvertBack(object value, Type targetTypes, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}


如果你想



然后做以下更改:

  • 设置路径Stroke="Green"
  • 设置边框Background="{TemplateBinding Background}"BorderBrush="Transparent"

这样,按钮椭圆化动画路径的内部部分,只留下外部线可见,因为它的变化。

相关问题