//cs
using System;
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
namespace WPFDevelopers.Controls
{
public class Gauge : RangeBase
{
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("TitleProperty", typeof(string), typeof(Gauge), new PropertyMetadata("WD"));
public static readonly DependencyProperty ValueFormatProperty =
DependencyProperty.Register("ValueFormat", typeof(string), typeof(Gauge),
new PropertyMetadata("{0:0}%", OnValueFormatChanged));
private static void OnValueFormatChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var gauge = d as Gauge;
gauge?.InvalidateVisual();
}
public string ValueFormat
{
get { return (string)GetValue(ValueFormatProperty); }
set { SetValue(ValueFormatProperty, value); }
}
public double Thickness
{
get { return (double)GetValue(ThicknessProperty); }
set { SetValue(ThicknessProperty, value); }
}
public static readonly DependencyProperty ThicknessProperty =
DependencyProperty.Register("Thickness", typeof(double), typeof(Gauge), new PropertyMetadata(10.0, OnValueFormatChanged));
static Gauge()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Gauge), new FrameworkPropertyMetadata(typeof(Gauge)));
}
public Gauge()
{
SetValue(ValueProperty, 0.0);
SetValue(MinimumProperty, 0.0);
SetValue(MaximumProperty, 100.0);
}
protected override void OnValueChanged(double oldValue, double newValue)
{
InvalidateVisual();
}
protected override void OnMinimumChanged(double oldValue, double newValue)
{
InvalidateVisual();
}
protected override void OnMaximumChanged(double oldValue, double newValue)
{
InvalidateVisual();
}
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
if (Background == null)
Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#293950"));
var width = ActualWidth;
var height = ActualHeight;
var radius = Math.Min(width, height) / 2;
drawingContext.DrawEllipse(Background, new Pen(Background, Thickness), new Point(width / 2, height / 2), radius, radius);
var normalizedValue = (Value - Minimum) / (Maximum - Minimum);
var mappedAngle = -220 + normalizedValue * 260;
var angleInRadians = mappedAngle * Math.PI / 180;
var pointerLength = radius * 0.7;
var pointerX = width / 2 + pointerLength * Math.Cos(angleInRadians);
var pointerY = height / 2 + pointerLength * Math.Sin(angleInRadians);
drawingContext.DrawLine(new Pen(Brushes.Red, 2), new Point(width / 2, height / 2), new Point(pointerX, pointerY));
drawingContext.DrawEllipse(Brushes.White, new Pen(Brushes.Red, 2), new Point(width / 2, height / 2), width / 20, width / 20);
var pathGeometry = new PathGeometry();
var startAngle = -220;
angleInRadians = startAngle * Math.PI / 180;
var startX = width / 2 + radius * Math.Cos(angleInRadians);
var startY = height / 2 + radius * Math.Sin(angleInRadians);
var pathFigure = new PathFigure()
{
StartPoint = new Point(startX, startY),
};
var endAngle = 40;
angleInRadians = endAngle * Math.PI / 180;
var endX = width / 2 + radius * Math.Cos(angleInRadians);
var endY = height / 2 + radius * Math.Sin(angleInRadians);
var isLargeArc = (endAngle - startAngle > 180);
var arcSegment = new ArcSegment()
{
Point = new Point(endX, endY),
Size = new Size(radius, radius),
RotationAngle = 0,
SweepDirection = SweepDirection.Clockwise,
IsLargeArc = isLargeArc,
};
pathFigure.Segments.Add(arcSegment);
pathGeometry.Figures.Add(pathFigure);
if (BorderBrush == null)
{
var gradientBrush = new LinearGradientBrush
{
StartPoint = new Point(0, 0),
EndPoint = new Point(1, 0)
};
gradientBrush.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#37D2C2"), 0.0));
gradientBrush.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#5AD2B2"), 0.01));
gradientBrush.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#B77D29"), 0.49));
gradientBrush.GradientStops.Add(new GradientStop(Colors.Red, 1.0));
gradientBrush.Freeze();
BorderBrush = gradientBrush;
}
drawingContext.DrawGeometry(null, new Pen(BorderBrush, Thickness), pathGeometry);
var tickLength = radius * 0.1;
var step = (Maximum - Minimum) / 10;
for (int i = 0; i <= 10; i++)
{
var angle = startAngle + (i * (endAngle - startAngle) / 10);
var tickStartX = width / 2 + (radius - tickLength) * Math.Cos(angle * Math.PI / 180);
var tickStartY = height / 2 + (radius - tickLength) * Math.Sin(angle * Math.PI / 180);
var tickEndX = width / 2 + (radius + Thickness / 2) * Math.Cos(angle * Math.PI / 180);
var tickEndY = height / 2 + (radius + Thickness / 2) * Math.Sin(angle * Math.PI / 180);
drawingContext.DrawLine(new Pen(Brushes.White, 2), new Point(tickStartX, tickStartY), new Point(tickEndX, tickEndY));
var labelValue = Minimum + step * i;
var formattedText = DrawingContextHelper.GetFormattedText(labelValue.ToString(),Brushes.White, FlowDirection.LeftToRight,FontSize);
var labelRadius = radius - tickLength * 2;
var labelX = width / 2 + labelRadius * Math.Cos(angle * Math.PI / 180) - formattedText.Width / 2;
var labelY = height / 2 + labelRadius * Math.Sin(angle * Math.PI / 180) - formattedText.Height / 2;
drawingContext.DrawText(formattedText, new Point(labelX, labelY));
}
var formattedValue = "{0:0}%";
try
{
formattedValue = string.Format(ValueFormat, Value);
}
catch (FormatException ex)
{
throw new InvalidOperationException("Formatting failed ", ex);
}
var currentValueText = DrawingContextHelper.GetFormattedText(formattedValue, Brushes.White, FlowDirection.LeftToRight, FontSize * 2);
var valueX = width / 2 - currentValueText.Width / 2;
var valueY = height / 2 + radius * 0.4;
drawingContext.DrawText(currentValueText, new Point(valueX, valueY));
var titleValue = DrawingContextHelper.GetFormattedText(Title, Brushes.White, FlowDirection.LeftToRight, FontSize);
valueX = width / 2 - titleValue.Width / 2;
valueY = height / 2 + radius * 0.8;
drawingContext.DrawText(titleValue, new Point(valueX, valueY));
}
}
}
//xaml
<Window x:Class="WpfApp141.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:WpfApp141"
WindowState="Maximized"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<local:Gauge
x:Name="gauge1"
Width="800"
Height="800"
Minimum="0"
Maximum="100"
Background="Cyan"
BorderBrush="Blue"
BorderThickness="5"
FontSize="20"
ValueFormat="{}{0:0}"
Value="{Binding GaugeValue,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</Window>
//xaml.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApp141
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
Random rnd { get; set; }
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
rnd = new Random();
System.Timers.Timer tmr = new System.Timers.Timer();
tmr.Elapsed += Tmr_Elapsed;
tmr.Interval = 1000;
tmr.Start();
}
private void Tmr_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
GaugeValue = rnd.NextDouble() * 100;
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
private double gaugeValue;
public double GaugeValue
{
get
{
return gaugeValue;
}
set
{
if(value!=gaugeValue)
{
gaugeValue = value;
OnPropertyChanged(nameof(GaugeValue));
}
}
}
}
}