2011-02-02 5 views

답변

21

두 가지 옵션이 있습니다. 첫 번째 방법은 ProgressBar 템플릿 템플릿입니다. 이것은 조금 까다로운 것으로 판명되었습니다. 나는 use an attached ViewModel to achieve the required effect하는 방법을 설명하는 블로그 게시물을 썼습니다.

다른 방법은 처음부터 직접 컨트롤을 만드는 것입니다. 당신은 다음 작업을 수행 할 수 있습니다 :

  1. 이 새로운 가치, 최대 및 최소 종속성 속성을 추가 새 사용자 컨트롤을 만듭니다.
  2. Angle 속성을 계산하려면 사용자 정의 컨트롤에서 값, 최대 및 최소 속성 변경 이벤트를 처리하십시오.
  3. 두 개의 '파이 조각'을 코드 뒤에 넣고 (this post 참조) UI에 추가하십시오.
+3

그리고 왜 기존 ProgressBar를 템플릿으로 만들지 않습니까? WPF의 CustomControls는이 목적을 위해 정확하게 보이지 않습니다. 당신이 필요로 할 때 그것을 재사용하지 않을 때 흠 잡을 데없는 컨트롤을 만드는 것의 모든 허점을 UI의 다른 표현으로 가져 왔습니까? – NVM

+2

@NVM, 원칙적으로 동의하지만 일부 코드가 필요할 것입니다. 순수 XAML을 사용하는 WPF의 기본 제공 셰이프를 사용하여 잘라낸 호를 만드는 쉬운 방법은 없습니다. 익스프레션 블렌드 SDK를 사용하고 있다면 아크 셰이프가있어서 쉽게 처리 할 수 ​​있습니다. 그래서 OP는 아마도 파이를 그릴 수있는 일종의 컨트롤을 만들어야 할 것입니다. 그러나 진행 막대의 구현은이 새로운 "파이"컨트롤을 사용하는 템플릿이어야합니다. – Josh

+0

내 수정 된 답변보기 그게 아니라 당신이 당신이 제안하는 방식으로 그것을 할 수 없습니다. 나는 일반적으로 당신이 떠날 수있는 것처럼 작은 코드로 작성하는 것이 더 좋다고 생각한다. – NVM

6

ValueConverter 님을 보았습니까? TemplateBinding을 사용하여 템플릿의 Value 속성에 바인딩하고 적절한 값 변환기를 사용하여 Circs 진행률 막대에 유용한 값을 변경할 수 있습니다.

편집 : 템플릿에서

:

  1. 이 원 노란색 채우기 추가합니다.

  2. 주황색으로 다른 원을 추가하십시오.

  3. 3에 리턴 지오메트리 2의 원형 클립 2

  4. 첨가 원을 위해 (아마도 이용 호 세그먼트)은 클립핑 기하학을 반환하는 값 변환 (또는 다중 값 변환)을 사용한다.

  5. Downvoter가 내 답장을 돌려줍니다.

+0

-1, 값 변환기로는이를 달성 할 수 없습니다. 시각적 트리에서 다루어 져야합니다. – Josh

+0

새 수정 사항을 확인하십시오. – NVM

+0

나는 downvote를 제거했다. 왜냐하면 네가 기술적으로 그렇게 할 수 있기 때문이다. :) 그러나 UIElement의 하위 클래스로이 작업을 수행하면 ProgressBar의 템플릿에 그대로 두는 것이 훨씬 더 간단합니다. – Josh

6

다소 까다 롭지 만 불가능하지는 않습니다. 다음은 부드러운 애니메이션을 사용하여 구현 한 것입니다. CircularProgressBar를 만들려면 값 변환기를 사용해야합니다.

enter image description here

CircularProgressBar.cs

public partial 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; 

     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)); 
} 

AngleToPointConverter.cs

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(); 
    } 
} 

AngleToIsLargeConverter.CS

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(); 
    } 
} 

App.xaml

<Application x:Class="WpfApplication1.App" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     StartupUri="MainWindow.xaml" 
     xmlns:my="clr-namespace:WpfApplication1"> 
<Application.Resources> 
    <my:AngleToPointConverter x:Key="prConverter"/> 
    <my:AngleToIsLargeConverter x:Key="isLargeConverter"/> 

    <Style x:Key="circularProgressBar" TargetType="my:CircularProgressBar"> 
     <Setter Property="Value" Value="10"/> 
     <Setter Property="Maximum" Value="100"/> 
     <Setter Property="StrokeThickness" Value="10"/> 
     <Setter Property="Template"> 
      <Setter.Value> 
       <ControlTemplate TargetType="my:CircularProgressBar"> 
         <Canvas Width="100" Height="100"> 
         <Ellipse Width="100" Height="100" Stroke="LightGray" 
            StrokeThickness="1"/> 

         <Path Stroke="{TemplateBinding Background}" 
            StrokeThickness="{TemplateBinding StrokeThickness}"> 
           <Path.Data> 
            <PathGeometry> 
             <PathFigure x:Name="fig" StartPoint="50,0"> 
              <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"> 
           <TextBlock Foreground="Gray" HorizontalAlignment="Center" VerticalAlignment="Center" 
             Text="{Binding Path=Value, StringFormat={}%{0}, 
           RelativeSource={RelativeSource TemplatedParent}}" 
              FontSize="{TemplateBinding FontSize}"/> 
          </Border> 
         </Canvas> 

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

그것은 더 등 InnerRadius, 반경

0

과 같은 몇 가지 이상의 속성을 추가하여 사용자 정의 할 수 있습니다 내가 아는이 이전 문제이지만 어쨌든 여기 내 해결책이 있습니다 :

윈폼에 대한

:

using System; 
using System.Drawing; 
using System.Drawing.Drawing2D; 
using System.Windows.Forms; 

public class CircularProgressBar : Control 
{ 
    /* CREDITS: 
    * Autor: Sajjad Arif Gul/October 12, 2016/C#, Source Codes 
    * https://www.csharpens.com/c-sharp/circular-progress-bar-in-c-sharp-windows-form-applications-23/ 
    * Modified by Jhollman Chacon, 2017 */ 

#region Enums 

public enum _ProgressShape 
{ 
    Round, 
    Flat 
} 

#endregion 

#region Variables 

private long _Value; 
private long _Maximum = 100; 
private Color _ProgressColor1 = Color.Orange; 
private Color _ProgressColor2 = Color.Orange; 
private Color _LineColor = Color.Silver; 
private _ProgressShape ProgressShapeVal; 

#endregion 

#region Custom Properties 

public long Value 
{ 
    get { return _Value; } 
    set 
    { 
     if (value > _Maximum) 
      value = _Maximum; 
     _Value = value; 
     Invalidate(); 
    } 
} 

public long Maximum 
{ 
    get { return _Maximum; } 
    set 
    { 
     if (value < 1) 
      value = 1; 
     _Maximum = value; 
     Invalidate(); 
    } 
} 

public Color ProgressColor1 
{ 
    get { return _ProgressColor1; } 
    set 
    { 
     _ProgressColor1 = value; 
     Invalidate(); 
    } 
} 

public Color ProgressColor2 
{ 
    get { return _ProgressColor2; } 
    set 
    { 
     _ProgressColor2 = value; 
     Invalidate(); 
    } 
} 

public Color LineColor 
{ 
    get { return _LineColor; } 
    set 
    { 
     _LineColor = value; 
     Invalidate(); 
    } 
} 

public _ProgressShape ProgressShape 
{ 
    get { return ProgressShapeVal; } 
    set 
    { 
     ProgressShapeVal = value; 
     Invalidate(); 
    } 
} 

#endregion 

#region EventArgs 

protected override void OnResize(EventArgs e) 
{ 
    base.OnResize(e); 
    SetStandardSize(); 
} 

protected override void OnSizeChanged(EventArgs e) 
{ 
    base.OnSizeChanged(e); 
    SetStandardSize(); 
} 

protected override void OnPaintBackground(PaintEventArgs p) 
{ 
    base.OnPaintBackground(p); 
} 

#endregion 

#region Methods 
public CircularProgressBar() 
{ 
    Size = new Size(130, 130); 
    Font = new Font("Segoe UI", 15); 
    MinimumSize = new Size(100, 100); 
    DoubleBuffered = true; 
    Value = 57; 
    ProgressShape = _ProgressShape.Flat; 
    this.ForeColor = Color.DimGray; 
} 

private void SetStandardSize() 
{ 
    int _Size = Math.Max(Width, Height); 
    Size = new Size(_Size, _Size); 
} 

public void Increment(int Val) 
{ 
    this._Value += Val; 
    Invalidate(); 
} 

public void Decrement(int Val) 
{ 
    this._Value -= Val; 
    Invalidate(); 
} 
#endregion 

#region Events 
protected override void OnPaint(PaintEventArgs e) 
{ 
    base.OnPaint(e); 
    using (Bitmap bitmap = new Bitmap(this.Width, this.Height)) 
    { 
     using (Graphics graphics = Graphics.FromImage(bitmap)) 
     { 
      graphics.SmoothingMode = SmoothingMode.AntiAlias; 
      graphics.Clear(this.BackColor); 

      // Dibuja la Linea 
      using (Pen pen2 = new Pen(LineColor)) 
      { 
       graphics.DrawEllipse(pen2, 0x18 - 6, 0x18 - 6, (this.Width - 0x30) + 12, (this.Height - 0x30) + 12); 
      } 

      //Dibuja la Barra de Progreso 
      using (LinearGradientBrush brush = new LinearGradientBrush(this.ClientRectangle, this._ProgressColor1, this._ProgressColor2, LinearGradientMode.ForwardDiagonal)) 
      { 
       using (Pen pen = new Pen(brush, 14f)) 
       { 
        switch (this.ProgressShapeVal) 
        { 
         case _ProgressShape.Round: 
          pen.StartCap = LineCap.Round; 
          pen.EndCap = LineCap.Round; 
          break; 

         case _ProgressShape.Flat: 
          pen.StartCap = LineCap.Flat; 
          pen.EndCap = LineCap.Flat; 
          break; 
        } 
        //Aqui se dibuja el Progreso 
        graphics.DrawArc(pen, 0x12, 0x12, (this.Width - 0x23) - 2, (this.Height - 0x23) - 2, -90, (int)Math.Round((double)((360.0/((double)this._Maximum)) * this._Value))); 
       } 
      } 

      //Dibuja el Texto de Progreso: 
      Brush FontColor = new SolidBrush(this.ForeColor); 
      SizeF MS = graphics.MeasureString(Convert.ToString(Convert.ToInt32((100/_Maximum) * _Value)), Font); 
      graphics.DrawString(Convert.ToString(Convert.ToInt32((100/_Maximum) * _Value)), Font, FontColor, Convert.ToInt32(Width/2 - MS.Width/2), Convert.ToInt32(Height/2 - MS.Height/2)); 
      e.Graphics.DrawImage(bitmap, 0, 0); 
      graphics.Dispose(); 
      bitmap.Dispose(); 
     } 
    } 
} 
#endregion 
} 

이행 :

  1. 장소 어디에서든 윈폼 프로젝트의 새로운 클래스에 소스 코드, 클래스 'CircularProgressBar.cs'를 이름을 지정합니다.
  2. 프로젝트를 컴파일하십시오.
  3. 컴파일 한 후 도구 모음에 새 컨트롤 또는 '구성 요소'가 나타납니다.
  4. 이 새 컨트롤을 폼에 드래그 앤 드롭하고 해당 속성을 사용자 지정합니다.

는 컨트롤과 같습니다

Control Preview

을 즐길 수 있습니다.