2014-04-13 11 views
13

Sto lavorando a un'app di Unified Fitness per Windows 8.1 e Windows Phone 8.1. Idealmente una delle viste principali avrebbe un indicatore di avanzamento giornaliero. Il problema è che non sono riuscito a trovare un misuratore o un misuratore effettivo. Quello che mi piacerebbe avere è semplicemente una barra di progresso radiale o qualcosa alla pari con i misuratori/misuratori di batteria nelle app comuni della batteria nello store Windows Phone. Da quello che posso dire, WPF/VS 2013 non offre questo tipo di componente fuori dalla scatola. So che Telerik e alcune terze parti offrono qualcosa di simile, ma preferirei usare qualcosa di open source o costruirlo da solo.Barra di avanzamento/Misuratore radiale WPF (ad esempio, Misuratore batteria)

Qualcuno sa di nuovi componenti opensource che funzionano con .NET 4.5 & WPF o hanno esempi su come posso creare il mio componente?

Finora quello che ho trovato sono simili a questo link: Gauges for WPF

ma spero di usare qualcosa di simile a questo: enter image description here

+5

E 'sorprendente come spesso "chiuse" le domande sono quello che in realtà risolvere i miei problemi. –

risposta

47

Si può costruire qualcosa del genere da soli. Prima di tutto, è necessario un arco:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Windows.Shapes; 
using System.Windows.Media; 
using System.Windows; 

... 

public class Arc : Shape 
{ 
    public double StartAngle 
    { 
     get { return (double)GetValue(StartAngleProperty); } 
     set { SetValue(StartAngleProperty, value); } 
    } 

    // Using a DependencyProperty as the backing store for StartAngle. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty StartAngleProperty = 
     DependencyProperty.Register("StartAngle", typeof(double), typeof(Arc), new UIPropertyMetadata(0.0, new PropertyChangedCallback(UpdateArc))); 

    public double EndAngle 
    { 
     get { return (double)GetValue(EndAngleProperty); } 
     set { SetValue(EndAngleProperty, value); } 
    } 

    // Using a DependencyProperty as the backing store for EndAngle. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty EndAngleProperty = 
     DependencyProperty.Register("EndAngle", typeof(double), typeof(Arc), new UIPropertyMetadata(90.0, new PropertyChangedCallback(UpdateArc))); 

    //This controls whether or not the progress bar goes clockwise or counterclockwise 
    public SweepDirection Direction 
    { 
     get { return (SweepDirection) GetValue(DirectionProperty); } 
     set { SetValue(DirectionProperty, value);} 
    } 

    public static readonly DependencyProperty DirectionProperty = 
     DependencyProperty.Register("Direction", typeof (SweepDirection), typeof (Arc), 
      new UIPropertyMetadata(SweepDirection.Clockwise)); 

    //rotate the start/endpoint of the arc a certain number of degree in the direction 
    //ie. if you wanted it to be at 12:00 that would be 270 Clockwise or 90 counterclockwise 
    public double OriginRotationDegrees 
    { 
     get { return (double) GetValue(OriginRotationDegreesProperty); } 
     set { SetValue(OriginRotationDegreesProperty, value);} 
    } 

    public static readonly DependencyProperty OriginRotationDegreesProperty = 
     DependencyProperty.Register("OriginRotationDegrees", typeof (double), typeof (Arc), 
      new UIPropertyMetadata(270.0, new PropertyChangedCallback(UpdateArc))); 

    protected static void UpdateArc(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
     Arc arc = d as Arc; 
     arc.InvalidateVisual(); 
    } 

    protected override Geometry DefiningGeometry 
    { 
     get { return GetArcGeometry(); } 
    } 

    protected override void OnRender(System.Windows.Media.DrawingContext drawingContext) 
    { 
     drawingContext.DrawGeometry(null, new Pen(Stroke, StrokeThickness), GetArcGeometry()); 
    } 

    private Geometry GetArcGeometry() 
    { 
     Point startPoint = PointAtAngle(Math.Min(StartAngle, EndAngle), Direction); 
     Point endPoint = PointAtAngle(Math.Max(StartAngle, EndAngle), Direction); 

     Size arcSize = new Size(Math.Max(0, (RenderSize.Width - StrokeThickness)/2), 
      Math.Max(0, (RenderSize.Height - StrokeThickness)/2)); 
     bool isLargeArc = Math.Abs(EndAngle - StartAngle) > 180; 

     StreamGeometry geom = new StreamGeometry(); 
     using (StreamGeometryContext context = geom.Open()) 
     { 
      context.BeginFigure(startPoint, false, false); 
      context.ArcTo(endPoint, arcSize, 0, isLargeArc, Direction, true, false); 
     } 
     geom.Transform = new TranslateTransform(StrokeThickness/2, StrokeThickness/2); 
     return geom; 
    } 

    private Point PointAtAngle(double angle, SweepDirection sweep) 
    { 
     double translatedAngle = angle + OriginRotationDegrees; 
     double radAngle = translatedAngle * (Math.PI/180); 
     double xr = (RenderSize.Width - StrokeThickness)/2; 
     double yr = (RenderSize.Height - StrokeThickness)/2; 

     double x = xr + xr * Math.Cos(radAngle); 
     double y = yr * Math.Sin(radAngle); 

     if (sweep == SweepDirection.Counterclockwise) 
     { 
      y = yr - y; 
     } 
     else 
     { 
      y = yr + y; 
     } 

     return new Point(x, y); 
    } 
} 

Questo arco ha un angolo iniziale e uno finale. Per convertire da un avanzamento di una barra di avanzamento a questi angoli, è necessario un convertitore:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

... 

public class ProgressToAngleConverter : System.Windows.Data.IMultiValueConverter 
{ 
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     double progress = (double)values[0]; 
     System.Windows.Controls.ProgressBar bar = values[1] as System.Windows.Controls.ProgressBar; 

     return 359.999 * (progress/(bar.Maximum - bar.Minimum)); 
    } 

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

Ok bene. Questo era tutto ciò di cui hai bisogno. Ora puoi scrivere il tuo XAML. Questo potrebbe essere qualcosa di simile:

<Window x:Class="WPFTest.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:local="clr-namespace:WPFTest" 
     Title="MainWindow" Height="525" Width="525"> 
    <Window.Resources> 
     <local:ProgressToAngleConverter x:Key="ProgressConverter"/> 
     <Style TargetType="{x:Type ProgressBar}" x:Key="ProgressBarStyle"> 
      <Setter Property="Template"> 
       <Setter.Value> 
        <ControlTemplate TargetType="{x:Type ProgressBar}"> 
         <Grid> 
          <Ellipse Stroke="Black" Fill="{TemplateBinding Background}"/> 
          <Ellipse Stroke="Black" Margin="40" Fill="White"/> 
          <local:Arc StrokeThickness="30" Stroke="{TemplateBinding BorderBrush}" Margin="5"> 
           <local:Arc.StartAngle> 
            <MultiBinding Converter="{StaticResource ProgressConverter}"> 
             <Binding Path="Minimum" RelativeSource="{RelativeSource TemplatedParent}"/> 
             <Binding Path="." RelativeSource="{RelativeSource TemplatedParent}"/> 
            </MultiBinding> 
           </local:Arc.StartAngle> 
           <local:Arc.EndAngle> 
            <MultiBinding Converter="{StaticResource ProgressConverter}"> 
             <Binding Path="Value" RelativeSource="{RelativeSource TemplatedParent}"/> 
             <Binding Path="." RelativeSource="{RelativeSource TemplatedParent}"/> 
            </MultiBinding> 
           </local:Arc.EndAngle> 
          </local:Arc> 
          <TextBlock Text="{Binding Value, RelativeSource={RelativeSource TemplatedParent}, StringFormat=\{0:0\}}" 
             Foreground="{TemplateBinding Background}" VerticalAlignment="Center" HorizontalAlignment="Center" 
             FontSize="72" FontWeight="Bold"/> 
         </Grid> 
        </ControlTemplate> 
       </Setter.Value> 
      </Setter> 
     </Style> 
    </Window.Resources> 
    <Grid> 
     <Grid.RowDefinitions> 
      <RowDefinition Height="*"/> 
      <RowDefinition Height="auto"/> 
     </Grid.RowDefinitions> 
     <Viewbox> 
      <ProgressBar Style="{StaticResource ProgressBarStyle}" Width="300" Height="300" 
         Value="{Binding ElementName=sliderValue, Path=Value}"/> 
     </Viewbox> 
     <Slider Grid.Row="1" Name="sliderValue" Maximum="100" Value="50" /> 
    </Grid> 
</Window> 

Ora basta prendere la ProgressBarStyle, modificarlo e applicarlo a qualsiasi progressbar che ti piace.

Infine, otterrete qualcosa di simile. Buon divertimento!

EDIT: necessari i seguenti riferimenti (ti consiglierei, per creare solo un nuovo e vuoto progetto WPF):

  • WindowsBase
  • PresentationCore
  • PresentationFramework

MODIFICA: Per controllare il senso di rotazione e la posizione in cui iniziare lo stato di avanzamento, Ho aggiunto due proprietà di dipendenza: Direction OriginRotationDegrees

enter image description here

+0

Grazie per la tua risposta ben dettagliata! Per la maggior parte ho trovato facile capire e utile. Sfortunatamente, ho difficoltà ad implementare la soluzione. Non riesco a "Resolve" o trovare lo spazio dei nomi per StreamGeometry, Pen, Point, UIPropertyMetadata e Media.DrawingContext. DrawingContext non viene trovato nei sistemi a causa di System.Windows.Media non viene rilevato quando si tenta di utilizzare l'importazione. Le raccomandazioni che ho trovato consigliavano di fare riferimento a PresentationCore.dll, ma questo non risolse il problema per me. Eventuali suggerimenti? – TheMoonbeam

+0

Ho aggiunto gli spazi dei nomi necessari. –

+0

grazie per aver aggiunto le importazioni.Alcune delle importazioni che stavo usando erano errate e mi mancavano alcuni riferimenti: PresentationFramework, PresentationCore, WindowsBase. Tutto funziona davvero bene, grazie! :) – TheMoonbeam

Problemi correlati