天天看點

wpf 圓形進度條

效果圖

wpf 圓形進度條

一共有三種方法

1.用弧度來畫進度條

2.用餅圖和圓取交集來畫進度條

3.用DashArray來畫進度條

方法一 用弧度來畫進度條

style

<Style x:Key="CircleProgressBar1" TargetType="{x:Type ProgressBar}">
      <Setter Property="Width" Value="100" />
      <Setter Property="Height" Value="100" />
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="{x:Type ProgressBar}">
            <Grid x:Name="TemplateRoot">
              <Rectangle Fill="#ef426f" RadiusX="5" RadiusY="5"/>
              <Ellipse Stroke="#fc8b93" 
                       StrokeThickness="6"
                       Width="86"
                       Height="86"/>
              <Path Stroke="#fbbdaf"
                    StrokeThickness="6"
                    StrokeStartLineCap="Round"
                    StrokeEndLineCap="Round">
                <Path.Data>
                  <MultiBinding Converter="{StaticResource Style1ArcConverter}">
                    <Binding ElementName="TemplateRoot" Path="ActualWidth" />
                    <Binding ElementName="TemplateRoot" Path="ActualHeight" />
                    <Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Value" />
                  </MultiBinding>
                </Path.Data>
              </Path>
              <TextBlock HorizontalAlignment="Center"
                         VerticalAlignment="Center"
                     Foreground="#FFF7ee"
                     Text="{Binding StringFormat={}{0}%, RelativeSource={RelativeSource TemplatedParent}, Path=Value}"/>
            </Grid>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>
           

converter

public class Style1ArcConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            double PIch2 = 2 * Math.PI;

            double centerX = (double)values[0] / 2;
            double centerY = (double)values[1] / 2;
            double angle = (double)values[2] / 100 * PIch2;

            double radius = 40;

            var 起始弧度的X偏移 = Math.Sin(0);
            var 起始弧度的Y偏移 = Math.Cos(0);

            var 終止弧度的X偏移 = Math.Sin(angle);
            var 終止弧度的Y偏移 = Math.Cos(angle);

            Point 起點 = new Point(centerX + radius * 起始弧度的X偏移, centerY - radius * 起始弧度的Y偏移);
            Point 終點 = new Point(centerX + radius * 終止弧度的X偏移, centerY - radius * 終止弧度的Y偏移);

            var converter = TypeDescriptor.GetConverter(typeof(Geometry));
            string data;

            if (angle == 0)
            {
                data = string.Empty;
            }
            else if (angle > 0 && angle < Math.PI)
            {
                data = $"M{起點.X},{起點.Y} A{radius},{radius},0,0,1 {終點.X},{終點.Y}";
            }
            else if (angle >= Math.PI && angle < 2 * Math.PI)
            {
                data = $"M{起點.X},{起點.Y} A{radius},{radius},0,1,1 {終點.X},{終點.Y}";
            }
            else
            {
                data = $"M{起點.X},{起點.Y} A{radius},{radius},0,1,1 {終點.X - 0.001},{終點.Y} Z";
            }

            return (Geometry)converter.ConvertFrom(data);
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
           

方法二 用餅圖和圓取交集來畫進度條

style

<Style x:Key="CircleProgressBar2" TargetType="{x:Type ProgressBar}">
      <Setter Property="Width" Value="100" />
      <Setter Property="Height" Value="100" />
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="{x:Type ProgressBar}">
            <Grid x:Name="TemplateRoot">
              <Rectangle Fill="#ef426f" RadiusX="5" RadiusY="5"/>
              <Path Fill="#fbbdaf">
                <Path.Data>
                  <CombinedGeometry GeometryCombineMode="Intersect">
                    <CombinedGeometry.Geometry1>
                      <GeometryGroup FillRule="EvenOdd">
                        <EllipseGeometry RadiusX="46" RadiusY="46" Center="50,50"/>
                        <EllipseGeometry RadiusX="40" RadiusY="40" Center="50,50"/>
                      </GeometryGroup>
                    </CombinedGeometry.Geometry1>
                    <CombinedGeometry.Geometry2>
                      <PathGeometry>
                        <PathGeometry.Figures>
                          <MultiBinding Converter="{StaticResource Style2ArcConverter}">
                            <Binding ElementName="TemplateRoot" Path="ActualWidth" />
                            <Binding ElementName="TemplateRoot" Path="ActualHeight" />
                            <Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Value" />
                          </MultiBinding>
                        </PathGeometry.Figures>
                      </PathGeometry>
                    </CombinedGeometry.Geometry2>
                  </CombinedGeometry>
                </Path.Data>
              </Path>

              <Path Fill="#fc8b93">
                <Path.Data>
                  <CombinedGeometry GeometryCombineMode="Exclude">
                    <CombinedGeometry.Geometry1>
                      <GeometryGroup FillRule="EvenOdd">
                        <EllipseGeometry RadiusX="46" RadiusY="46" Center="50,50"/>
                        <EllipseGeometry RadiusX="40" RadiusY="40" Center="50,50"/>
                      </GeometryGroup>
                    </CombinedGeometry.Geometry1>
                    <CombinedGeometry.Geometry2>
                      <PathGeometry>
                        <PathGeometry.Figures>
                          <MultiBinding Converter="{StaticResource Style2ArcConverter}">
                            <Binding ElementName="TemplateRoot" Path="ActualWidth" />
                            <Binding ElementName="TemplateRoot" Path="ActualHeight" />
                            <Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Value" />
                          </MultiBinding>
                        </PathGeometry.Figures>
                      </PathGeometry>
                    </CombinedGeometry.Geometry2>
                  </CombinedGeometry>
                </Path.Data>
              </Path>

              <StackPanel Margin="0,16,0,0"
                        VerticalAlignment="Center">
                <TextBlock HorizontalAlignment="Center"
                         VerticalAlignment="Center"
                         Foreground="#FFF7ee"
                         Text="{Binding StringFormat={}{0}%, RelativeSource={RelativeSource TemplatedParent}, Path=Value}"/>
              </StackPanel>
            </Grid>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>
           

converter

public class Style2ArcConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            double PIch2 = 2 * Math.PI;

            double centerX = (double)values[0] / 2;
            double centerY = (double)values[1] / 2;
            double angle = (double)values[2] / 100 * PIch2;

            double radius = centerX;

            var 起始弧度的X偏移 = Math.Sin(0);
            var 起始弧度的Y偏移 = Math.Cos(0);

            var 終止弧度的X偏移 = Math.Sin(angle);
            var 終止弧度的Y偏移 = Math.Cos(angle);

            Point 起點 = new Point(centerX + radius * 起始弧度的X偏移, centerY - radius * 起始弧度的Y偏移);
            Point 終點 = new Point(centerX + radius * 終止弧度的X偏移, centerY - radius * 終止弧度的Y偏移);

            var converter = TypeDescriptor.GetConverter(typeof(PathFigureCollection));
            string data;
            if (angle >= 0 && angle < Math.PI)
            {
                data = $"M{起點.X},{起點.Y} A{radius},{radius},0,0,1 {終點.X},{終點.Y} L{centerX},{centerY} Z";
            }
            else if (angle >= Math.PI && angle < 2 * Math.PI)
            {
                data = $"M{起點.X},{起點.Y} A{radius},{radius},0,1,1 {終點.X},{終點.Y} L{centerX},{centerY} Z";
            }
            else
            {
                data = $"M{起點.X},{起點.Y} A{radius},{radius},0,1,1 {終點.X - 0.001},{終點.Y} L{centerX},{centerY} Z";
            }

            return (PathFigureCollection)converter.ConvertFrom(data);
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
           

方法三 用DashArray來畫進度條

style

<Style x:Key="CircleProgressBar3" TargetType="{x:Type ProgressBar}">
      <Setter Property="Width" Value="100" />
      <Setter Property="Height" Value="100" />
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="{x:Type ProgressBar}">
            <Grid>
              <Rectangle Fill="#ef426f" RadiusX="5" RadiusY="5"/>
              <Ellipse Stroke="#fc8b93" 
                       StrokeThickness="6"
                       Width="80"
                       Height="80"/>
              <Ellipse Width="80" Height="80" StrokeThickness="6" Stroke="#fbbdaf" 
                       RenderTransformOrigin="0.5,0.5" 
                       StrokeDashCap="{Binding Value, RelativeSource={RelativeSource TemplatedParent},
                                               Converter={StaticResource Style3DashConverter}}">
                <Ellipse.RenderTransform>
                  <RotateTransform Angle="-90" />
                </Ellipse.RenderTransform>
                <Ellipse.StrokeDashArray>
                  <MultiBinding Converter="{StaticResource Style3ArcConverter}">
                    <Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Value" />
                    <Binding Path="StrokeThickness" RelativeSource="{RelativeSource Self}" />
                  </MultiBinding>
                </Ellipse.StrokeDashArray>
              </Ellipse>
              <TextBlock HorizontalAlignment="Center"
                         VerticalAlignment="Center"
                         Foreground="#FFF7ee"
                         Text="{Binding StringFormat={}{0}%, RelativeSource={RelativeSource TemplatedParent}, Path=Value}"/>
            </Grid>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>
           

converter 有2個

public class Style3ArcConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            double value = (double)values[0];
            double thickness = (double)values[1];

            double radius = 40;

            double 周長 = Math.PI * (2 * radius - thickness) / thickness;

            double showPrecent = value / 100 * 周長;

            var converter = TypeDescriptor.GetConverter(typeof(DoubleCollection));

            return (DoubleCollection)converter.ConvertFrom($"{showPrecent} {周長}");
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
           
public class Style3DashConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            double v = (double)value;

            if (v == 0)
            {
                return PenLineCap.Flat;
            }
            else
            {
                return PenLineCap.Round;
            }

        }

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

使用

前台代碼

<StackPanel Orientation="Vertical">
    <ProgressBar x:Name="progress1"
                 Style="{StaticResource CircleProgressBar1}"
                 Margin="10"
                 Value="0" />
    <ProgressBar x:Name="progress2"
                 Style="{StaticResource CircleProgressBar2}"
                 Margin="10"
                 Value="0" />
    <ProgressBar x:Name="progress3"
                Style="{StaticResource CircleProgressBar3}"
                 Margin="10"
                 Value="0" />
    <StackPanel Orientation="Horizontal">
      <Button Content="開始" Click="Button_Click" />
      <Button Content="重置" Click="Button_Click_1" />
    </StackPanel>
  </StackPanel>
           

對應的背景代碼

using System;
using System.Timers;
using System.Windows;

namespace 圖形變換
{
    public partial class Window4 : Window
    {
        private Timer _timer;
        public Window4()
        {
            InitializeComponent();

            _timer = new Timer
            {
                Interval = 50
            };
            _timer.Elapsed += Timer_Elapsed;
        }

        private void Button_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            _timer.Start();
        }

        private void Timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            Dispatcher.BeginInvoke(new Action(() =>
            {
                if (progress1.Value < 100)
                {
                    progress1.Value++;
                    progress2.Value++;
                    progress3.Value++;
                }
                else
                {
                    _timer.Stop();
                }
            }));
        }

        private void Button_Click_1(object sender, System.Windows.RoutedEventArgs e)
        {
            progress1.Value = 0;
            progress2.Value = 0;
            progress3.Value = 0;
            _timer.Stop();
        }
    }
}