Search code examples
c#wpfprogress-bar

How to create Custom progress bar minimum value 0 starts from the bottom left corner?


I want the starting point 0% and the ending point 100% of the progress bar to be in the lower left corner. And the progress of the value changes to display normally. How can I accomplish it?

I want the progress bar to increase and decrease in value along the circle.

The result of my test is not correct, I don't know where is the problem and how to fix it.

Xaml:

<Window.Resources>
    <local:AngleToPointConverter x:Key="prConverter"/>
    <local:AngleToIsLargeConverter x:Key="isLargeConverter"/>
  
    <Style x:Key="circularProgressBar" TargetType="local:CircularProgressBar">
        <Setter Property="Value" Value="10"/>
        <Setter Property="Maximum" Value="100"/>
        <Setter Property="StrokeThickness" Value="10"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:CircularProgressBar">
                  
                        
                        <Canvas Width="100" Height="130">
                            <Ellipse Width="101" Height="101" Stroke="LightGray" Opacity="0.7" StrokeThickness="4"  />

                            <Path Stroke="{TemplateBinding Background}" 
                              StrokeThickness="{TemplateBinding StrokeThickness}">
                                <Path.Data>
                                    <PathGeometry>
                                        <PathFigure x:Name="fig" StartPoint="20,90">
                                            <ArcSegment RotationAngle="0" SweepDirection="Clockwise"
                                                    Size="50,50"
                                                    Point="{Binding Path=Angle, Converter={StaticResource prConverter}, RelativeSource={RelativeSource FindAncestor, AncestorType=ProgressBar}}"
                                                    IsLargeArc="{Binding Path=Angle, Converter={StaticResource isLargeConverter}, RelativeSource={RelativeSource FindAncestor, AncestorType=ProgressBar}}"
                                                    >
                                            </ArcSegment>
                                        </PathFigure>
                                    </PathGeometry>
                                </Path.Data>
                            </Path>
                            <Border Width="100" Height="100">
                                <Grid>
                                    <Ellipse Width="50" Height="50"   Fill="White"  />
                                    <Image Width="20" Height="20" Margin="30,25,30,30" Source="bulb1.PNG"/>

                                    <TextBlock Width="50" Foreground="Black" Height="20"  Margin="10,40,10,5" TextAlignment="Center"
                                   Text="{Binding Path=Value, StringFormat={}{0}%,  
                            RelativeSource={RelativeSource TemplatedParent}}"
                                       FontSize="{TemplateBinding FontSize}"/>
                                </Grid>

                            </Border>
                            <Canvas  Canvas.Top="110">
                                <Button x:Name="decrease"  Margin="20,0,0,0" Command="{Binding DecreaseCommand}" >
                                        <Button.Style>
                                            <Style TargetType="{x:Type Button}">
                                                <Setter Property="Template">
                                                    <Setter.Value>
                                                        <ControlTemplate>
                                                            <Grid>
                                                                <Ellipse Width="20" Height="20" Stroke="LightGray"  StrokeThickness="1"  />
                                                                <Border Width="20" Height="20" >
                                                                    <TextBlock Foreground="LightGray" Text="-" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center" />
                                                                </Border>
                                                            </Grid>
                                                        </ControlTemplate>
                                                    </Setter.Value>
                                                </Setter>
                                            </Style>
                                        </Button.Style>
                                    </Button>
                                    <Button x:Name="increase"  Margin="60,0,0,0" Grid.Column="1" Command="{Binding IncreaseCommand}" >
                                        <Button.Style>
                                            <Style TargetType="{x:Type Button}">
                                                <Setter Property="Template">
                                                    <Setter.Value>
                                                        <ControlTemplate>
                                                            <Grid>
                                                                <Ellipse Width="20" Height="20" Stroke="LightGray"  StrokeThickness="1" />
                                                                <Border Width="20" Height="20"   Grid.Column="1">
                                                                    <TextBlock Foreground="LightGray" Text="+" FontWeight="Bold"  VerticalAlignment="Center" HorizontalAlignment="Center" />

                                                                </Border>
                                                            </Grid>
                                                        </ControlTemplate>
                                                    </Setter.Value>
                                                </Setter>
                                            </Style>
                                        </Button.Style>
                                    </Button>
                                    <!--<Ellipse Width="20" Height="20" Stroke="LightGray"  StrokeThickness="1"  Margin="20,0,0,0"/>
                                    <Border Width="20" Height="20" Margin="20,0,0,0">
                                        <TextBlock Foreground="LightGray" Text="-" HorizontalAlignment="Center" />
                                    </Border>
                                    <Ellipse Width="20" Height="20" Stroke="LightGray"  StrokeThickness="1"  Margin="60,0,0,0"/>
                                    <Border Width="20" Height="20" Margin="60,0,0,0">
                                        <TextBlock Foreground="LightGray" Text="+" HorizontalAlignment="Center" />

                                    </Border>-->

                            </Canvas>
                        </Canvas>
                  
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>
<Grid Background="DarkBlue">
    <local:CircularProgressBar Background="White" Style="{StaticResource  circularProgressBar }"
                                  Value="{Binding ElementName=CirularSlider, Path= Value}"  Foreground="Black" FontWeight="Bold"
                                 
                                   StrokeThickness="4"  
                               
                                   BorderBrush="LightGray"/>

    <Slider Minimum="0" Maximum="100" 
                x:Name="CirularSlider" IsSnapToTickEnabled="True"
                VerticalAlignment="Top" Value="10"/>
</Grid>

Codebedhind:

public class CircularProgressBar : ProgressBar
    {
        public CircularProgressBar()
        {
            this.ValueChanged += CircularProgressBar_ValueChanged;
        }

        void CircularProgressBar_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            CircularProgressBar bar = sender as CircularProgressBar;
            double currentAngle = bar.Angle;
            double targetAngle = e.NewValue / bar.Maximum * 359.999;
          //  double targetAngle = e.NewValue / bar.Maximum * 179.999;

            DoubleAnimation anim = new DoubleAnimation(currentAngle, targetAngle, TimeSpan.FromMilliseconds(500));
            bar.BeginAnimation(CircularProgressBar.AngleProperty, anim, HandoffBehavior.SnapshotAndReplace);
        }

        public double Angle
        {
            get { return (double)GetValue(AngleProperty); }
            set { SetValue(AngleProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Angle.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty AngleProperty =
            DependencyProperty.Register("Angle", typeof(double), typeof(CircularProgressBar), new PropertyMetadata(0.0));

        public double StrokeThickness
        {
            get { return (double)GetValue(StrokeThicknessProperty); }
            set { SetValue(StrokeThicknessProperty, value); }
        }

        // Using a DependencyProperty as the backing store for StrokeThickness.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty StrokeThicknessProperty =
            DependencyProperty.Register("StrokeThickness", typeof(double), typeof(CircularProgressBar), new PropertyMetadata(10.0));
    }


   
   public 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 piang = angle * Math.PI / 310;

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

            return new System.Windows.Point(px, py);
        }

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

    public class AngleToIsLargeConverter : IValueConverter
    {

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

           return angle > 180;
           // return angle > 300;
        }

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

The result: enter image description here

Edit:

enter image description here

Update:

How can I change the progress value by dragging the ball?

<Style x:Key="circularProgressBar1" TargetType="local:CircularProgressBar">
            <Setter Property="Value" Value="10"/>
            <Setter Property="Maximum" Value="100"/>
            <Setter Property="StrokeThickness" Value="7"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="local:CircularProgressBar">


                        <Canvas Width="100" Height="130">
                            <Ellipse Width="105" Height="104" Margin="-2.4,-1.5,0,0" Stroke="LightGray" Opacity="0.7"  StrokeThickness="8"  />


                           
                            <Path Stroke="{TemplateBinding Background}"  StrokeStartLineCap="Round" StrokeEndLineCap="Round"
                              StrokeThickness="{TemplateBinding StrokeThickness}">
                                <Path.Data>
                                    <PathGeometry>
                                        <PathFigure x:Name="fig" StartPoint="20,90">
                                            <ArcSegment RotationAngle="0" SweepDirection="Clockwise" 
                                                    Size="50,50"
                                                    Point="{Binding Path=Angle, Converter={StaticResource prConverter}, RelativeSource={RelativeSource FindAncestor, AncestorType=ProgressBar}}"
                                                    IsLargeArc="{Binding Path=Angle, Converter={StaticResource isLargeConverter}, RelativeSource={RelativeSource FindAncestor, AncestorType=ProgressBar}}"
                                                    >
                                            </ArcSegment>
                                        </PathFigure>
                                    </PathGeometry>
                                </Path.Data>
                            </Path>
                            <Button>
                                <Button.Style>
                                    <Style TargetType="Button">
                                        <Setter Property="Template">
                                            <Setter.Value>
                                                <ControlTemplate>
                                                    <Path Stroke="Black" StrokeThickness="10" StrokeStartLineCap="Round" StrokeEndLineCap="Round">
                                                        <Path.Data>
                                                            <PathGeometry>
                                                                <PathGeometry.Figures>
                                                                    <PathFigure StartPoint="{Binding Path=Angle, Converter={StaticResource prConverter}, RelativeSource={RelativeSource FindAncestor, AncestorType=ProgressBar}}">
                                                                        <PathFigure.Segments>
                                                                            <LineSegment Point="{Binding Path=Angle, Converter={StaticResource prConverter}, RelativeSource={RelativeSource FindAncestor, AncestorType=ProgressBar}}" />
                                                                        </PathFigure.Segments>
                                                                    </PathFigure>
                                                                </PathGeometry.Figures>
                                                            </PathGeometry>
                                                        </Path.Data>
                                                    </Path>
                                                </ControlTemplate>
                                            </Setter.Value>
                                        </Setter>
                                    </Style>
                                </Button.Style>
                            </Button>
                        


                            <Border Width="100" Height="100">
                                <Grid>
                                    <Ellipse Width="50" Height="50"   Fill="White"  />
                                    <Image Width="20" Height="20" Margin="30,25,30,30" Source="bulb1.PNG"/>

                                    <TextBlock Width="50" Foreground="Black" Height="20"  Margin="10,40,10,5" TextAlignment="Center"
                                   Text="{Binding Path=Value, StringFormat={}{0}%,  
                            RelativeSource={RelativeSource TemplatedParent}}"
                                       FontSize="{TemplateBinding FontSize}"/>
                                </Grid>

                            </Border>
                            <Canvas  Canvas.Top="110">
                                <Button x:Name="decrease"  Margin="20,0,0,0" Command="{Binding DecreaseCommand}" >
                                    <Button.Style>
                                        <Style TargetType="{x:Type Button}">
                                            <Setter Property="Template">
                                                <Setter.Value>
                                                    <ControlTemplate>
                                                        <Grid>
                                                            <Ellipse Width="20" Height="20" Stroke="LightGray"  StrokeThickness="1"  />
                                                            <Border Width="20" Height="20" >
                                                                <TextBlock Foreground="LightGray" Text="-" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center" />
                                                            </Border>
                                                        </Grid>
                                                    </ControlTemplate>
                                                </Setter.Value>
                                            </Setter>
                                        </Style>
                                    </Button.Style>
                                </Button>
                                <Button x:Name="increase"  Margin="60,0,0,0" Grid.Column="1" Command="{Binding IncreaseCommand}" >
                                    <Button.Style>
                                        <Style TargetType="{x:Type Button}">
                                            <Setter Property="Template">
                                                <Setter.Value>
                                                    <ControlTemplate>
                                                        <Grid>
                                                            <Ellipse Width="20" Height="20" Stroke="LightGray"  StrokeThickness="1" />
                                                            <Border Width="20" Height="20"   Grid.Column="1">
                                                                <TextBlock Foreground="LightGray" Text="+" FontWeight="Bold"  VerticalAlignment="Center" HorizontalAlignment="Center" />

                                                            </Border>
                                                        </Grid>
                                                    </ControlTemplate>
                                                </Setter.Value>
                                            </Setter>
                                        </Style>
                                    </Button.Style>
                                </Button>
                                <!--<Ellipse Width="20" Height="20" Stroke="LightGray"  StrokeThickness="1"  Margin="20,0,0,0"/>
                                    <Border Width="20" Height="20" Margin="20,0,0,0">
                                        <TextBlock Foreground="LightGray" Text="-" HorizontalAlignment="Center" />
                                    </Border>
                                    <Ellipse Width="20" Height="20" Stroke="LightGray"  StrokeThickness="1"  Margin="60,0,0,0"/>
                                    <Border Width="20" Height="20" Margin="60,0,0,0">
                                        <TextBlock Foreground="LightGray" Text="+" HorizontalAlignment="Center" />

                                    </Border>-->

                            </Canvas>
                        </Canvas>

                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

enter image description here


Solution

  • change the piang calculation in the convert function so that the starting point at the bottom left is taken into account in the calculation

    double piang = (angle - 143.2) * Math.PI / 180;
    

    so the class looks like this

    public 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 - 143.2) * Math.PI / 180;
            //double piang = angle * Math.PI / 310;
    
            double px = Math.Sin(piang) * radius + radius;
            double py = -Math.Cos(piang) * radius + radius;
    
            return new System.Windows.Point(px, py);
        }
    
        public object ConvertBack(object value, Type targetTypes, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
    

    you may have to adjust the angle (143.2).