diff --git a/src/Wpf.Ui/Controls/Gauge/Gauge.cs b/src/Wpf.Ui/Controls/Gauge/Gauge.cs
new file mode 100644
index 000000000..8430c25b2
--- /dev/null
+++ b/src/Wpf.Ui/Controls/Gauge/Gauge.cs
@@ -0,0 +1,540 @@
+// This Source Code Form is subject to the terms of the MIT License.
+// If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
+// Copyright (C) Leszek Pomianowski and WPF UI Contributors.
+// All Rights Reserved.
+
+using System.Windows.Controls.Primitives;
+using System.Windows.Markup;
+
+namespace Wpf.Ui.Controls;
+
+[DefaultProperty(nameof(Content))]
+[ContentProperty(nameof(Content))]
+public class Gauge : RangeBase
+{
+ /// Identifies the dependency property.
+ public static readonly DependencyProperty StartAngleProperty = DependencyProperty.Register(
+ nameof(StartAngle),
+ typeof(double),
+ typeof(Gauge),
+ new FrameworkPropertyMetadata(0D, FrameworkPropertyMetadataOptions.AffectsRender)
+ );
+
+ /// Identifies the dependency property.
+ public static readonly DependencyProperty EndAngleProperty = DependencyProperty.Register(
+ nameof(EndAngle),
+ typeof(double),
+ typeof(Gauge),
+ new FrameworkPropertyMetadata(0D, FrameworkPropertyMetadataOptions.AffectsRender)
+ );
+
+ /// Identifies the dependency property.
+ public static readonly DependencyProperty ThicknessProperty = DependencyProperty.Register(
+ nameof(Thickness),
+ typeof(double),
+ typeof(Gauge),
+ new FrameworkPropertyMetadata(10D, FrameworkPropertyMetadataOptions.AffectsRender)
+ );
+
+ /// Identifies the dependency property.
+ public static readonly DependencyProperty ContentProperty = DependencyProperty.Register(
+ nameof(Content),
+ typeof(object),
+ typeof(Gauge),
+ new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender)
+ );
+
+ /// Identifies the dependency property.
+ public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register(
+ nameof(Header),
+ typeof(object),
+ typeof(Gauge),
+ new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender)
+ );
+
+ /// Identifies the dependency property.
+ public static readonly DependencyProperty StartLineCapProperty = DependencyProperty.Register(
+ nameof(StartLineCap),
+ typeof(PenLineCap),
+ typeof(Gauge),
+ new FrameworkPropertyMetadata(PenLineCap.Flat, FrameworkPropertyMetadataOptions.AffectsRender)
+ );
+
+ /// Identifies the dependency property.
+ public static readonly DependencyProperty EndLineCapProperty = DependencyProperty.Register(
+ nameof(EndLineCap),
+ typeof(PenLineCap),
+ typeof(Gauge),
+ new FrameworkPropertyMetadata(PenLineCap.Flat, FrameworkPropertyMetadataOptions.AffectsRender)
+ );
+
+ /// Identifies the dependency property.
+ public static readonly DependencyProperty DashCapProperty = DependencyProperty.Register(
+ nameof(DashCap),
+ typeof(PenLineCap),
+ typeof(Gauge),
+ new FrameworkPropertyMetadata(PenLineCap.Flat, FrameworkPropertyMetadataOptions.AffectsRender)
+ );
+
+ /// Identifies the dependency property.
+ public static readonly DependencyProperty DashArrayProperty = DependencyProperty.Register(
+ nameof(DashArray),
+ typeof(DoubleCollection),
+ typeof(Gauge),
+ new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender)
+ );
+
+ /// Identifies the dependency property.
+ public static readonly DependencyProperty DashOffsetProperty = DependencyProperty.Register(
+ nameof(DashOffset),
+ typeof(double),
+ typeof(Gauge),
+ new FrameworkPropertyMetadata(0D, FrameworkPropertyMetadataOptions.AffectsRender)
+ );
+
+ /// Identifies the dependency property.
+ public static readonly DependencyProperty LineJoinProperty = DependencyProperty.Register(
+ nameof(LineJoin),
+ typeof(PenLineJoin),
+ typeof(Gauge),
+ new FrameworkPropertyMetadata(PenLineJoin.Miter, FrameworkPropertyMetadataOptions.AffectsRender)
+ );
+
+ /// Identifies the dependency property.
+ public static readonly DependencyProperty MiterLimitProperty = DependencyProperty.Register(
+ nameof(MiterLimit),
+ typeof(double),
+ typeof(Gauge),
+ new FrameworkPropertyMetadata(0D, FrameworkPropertyMetadataOptions.AffectsRender)
+ );
+
+ /// Identifies the dependency property.
+ public static readonly DependencyProperty IndicatorProperty = DependencyProperty.Register(
+ nameof(Indicator),
+ typeof(Brush),
+ typeof(Gauge),
+ new FrameworkPropertyMetadata(Brushes.Transparent, FrameworkPropertyMetadataOptions.AffectsRender)
+ );
+
+ /// Identifies the dependency property.
+ public static readonly DependencyProperty IndicatorStartLineCapProperty = DependencyProperty.Register(
+ nameof(IndicatorStartLineCap),
+ typeof(PenLineCap),
+ typeof(Gauge),
+ new FrameworkPropertyMetadata(PenLineCap.Flat, FrameworkPropertyMetadataOptions.AffectsRender)
+ );
+
+ /// Identifies the dependency property.
+ public static readonly DependencyProperty IndicatorEndLineCapProperty = DependencyProperty.Register(
+ nameof(IndicatorEndLineCap),
+ typeof(PenLineCap),
+ typeof(Gauge),
+ new FrameworkPropertyMetadata(PenLineCap.Flat, FrameworkPropertyMetadataOptions.AffectsRender)
+ );
+
+ /// Identifies the dependency property.
+ public static readonly DependencyProperty IndicatorDashCapProperty = DependencyProperty.Register(
+ nameof(IndicatorDashCap),
+ typeof(PenLineCap),
+ typeof(Gauge),
+ new FrameworkPropertyMetadata(PenLineCap.Flat, FrameworkPropertyMetadataOptions.AffectsRender)
+ );
+
+ /// Identifies the dependency property.
+ public static readonly DependencyProperty IndicatorDashArrayProperty = DependencyProperty.Register(
+ nameof(IndicatorDashArray),
+ typeof(DoubleCollection),
+ typeof(Gauge),
+ new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender)
+ );
+
+ /// Identifies the dependency property.
+ public static readonly DependencyProperty IndicatorDashOffsetProperty = DependencyProperty.Register(
+ nameof(IndicatorDashOffset),
+ typeof(double),
+ typeof(Gauge),
+ new FrameworkPropertyMetadata(0D, FrameworkPropertyMetadataOptions.AffectsRender)
+ );
+
+ /// Identifies the dependency property.
+ public static readonly DependencyProperty IndicatorLineJoinProperty = DependencyProperty.Register(
+ nameof(IndicatorLineJoin),
+ typeof(PenLineJoin),
+ typeof(Gauge),
+ new FrameworkPropertyMetadata(PenLineJoin.Miter, FrameworkPropertyMetadataOptions.AffectsRender)
+ );
+
+ /// Identifies the dependency property.
+ public static readonly DependencyProperty IndicatorMiterLimitProperty = DependencyProperty.Register(
+ nameof(IndicatorMiterLimit),
+ typeof(double),
+ typeof(Gauge),
+ new FrameworkPropertyMetadata(0D, FrameworkPropertyMetadataOptions.AffectsRender)
+ );
+
+ ///
+ /// Gets or sets the initial angle from which the gauge will be drawn.
+ ///
+ [Category("Common")]
+ [Description("Gets or sets the initial angle from which the gauge will be drawn.")]
+ [DefaultValue(0D)]
+ [TypeConverter(typeof(LengthConverter))]
+ public double StartAngle
+ {
+ get => (double)GetValue(StartAngleProperty);
+ set => SetValue(StartAngleProperty, value);
+ }
+
+ ///
+ /// Gets or sets the final angle from which the gauge will be drawn.
+ ///
+ [Category("Common")]
+ [Description("Gets or sets the final angle from which the gauge will be drawn.")]
+ [DefaultValue(0D)]
+ [TypeConverter(typeof(LengthConverter))]
+ public double EndAngle
+ {
+ get => (double)GetValue(EndAngleProperty);
+ set => SetValue(EndAngleProperty, value);
+ }
+
+ ///
+ /// Gets or sets the thickness that will be used to draw the gauge.
+ ///
+ [Category("Common")]
+ [Description("Gets or sets the thickness that will be used to draw the gauge.")]
+ [DefaultValue(10D)]
+ [TypeConverter(typeof(LengthConverter))]
+ public double Thickness
+ {
+ get => (double)GetValue(ThicknessProperty);
+ set => SetValue(ThicknessProperty, value);
+ }
+
+ ///
+ /// Gets or sets the content within the gauge.
+ ///
+ [Category("Common")]
+ [Description("Gets or sets the content within the gauge.")]
+ [DefaultValue(null)]
+ public object? Content
+ {
+ get => GetValue(ContentProperty);
+ set => SetValue(ContentProperty, value);
+ }
+
+ ///
+ /// Gets or sets the header within the gauge.
+ ///
+ [Category("Common")]
+ [Description("Gets or sets the header within the gauge.")]
+ [DefaultValue(null)]
+ public object? Header
+ {
+ get => GetValue(HeaderProperty);
+ set => SetValue(HeaderProperty, value);
+ }
+
+ ///
+ /// Gets or sets the to use at the start of the bar.
+ ///
+ [Category("GaugeBar")]
+ [Description("Gets or sets the PenLineCap to use at the start of the bar.")]
+ [DefaultValue(PenLineCap.Flat)]
+ public PenLineCap StartLineCap
+ {
+ get => (PenLineCap)GetValue(StartLineCapProperty);
+ set => SetValue(StartLineCapProperty, value);
+ }
+
+ ///
+ /// Gets or sets the to use at the end of the bar.
+ ///
+ [Category("GaugeBar")]
+ [Description("Gets or sets the PenLineCap to use at the end of the bar.")]
+ [DefaultValue(PenLineCap.Flat)]
+ public PenLineCap EndLineCap
+ {
+ get => (PenLineCap)GetValue(EndLineCapProperty);
+ set => SetValue(EndLineCapProperty, value);
+ }
+
+ ///
+ /// Gets or sets the to use for the dashes of the bar.
+ ///
+ [Category("GaugeBar")]
+ [Description("Gets or sets the PenLineCap to use for the dashes of the bar.")]
+ [DefaultValue(PenLineCap.Flat)]
+ public PenLineCap DashCap
+ {
+ get => (PenLineCap)GetValue(DashCapProperty);
+ set => SetValue(DashCapProperty, value);
+ }
+
+ ///
+ /// Gets or sets the sheme of dashes and gaps that will be used within the bar.
+ ///
+ [Category("GaugeBar")]
+ [Description("Gets or sets the sheme of dashes and gaps that will be used within the bar.")]
+ [DefaultValue(null)]
+ [TypeConverter(typeof(DoubleCollectionConverter))]
+ public DoubleCollection? DashArray
+ {
+ get => (DoubleCollection?)GetValue(DashArrayProperty);
+ set => SetValue(DashArrayProperty, value);
+ }
+
+ ///
+ /// Gets or sets the distance within the dash pattern where a dash begins.
+ ///
+ [Category("GaugeBar")]
+ [Description("Gets or sets the distance within the dash pattern where a dash begins.")]
+ [DefaultValue(0D)]
+ [TypeConverter(typeof(LengthConverter))]
+ public double DashOffset
+ {
+ get => (double)GetValue(DashOffsetProperty);
+ set => SetValue(DashOffsetProperty, value);
+ }
+
+ ///
+ /// Gets or sets the shape that joins two lines or segments of the bar.
+ ///
+ [Category("GaugeBar")]
+ [Description("Gets or sets the shape that joins two lines or segments of the bar.")]
+ [DefaultValue(PenLineJoin.Miter)]
+ public PenLineJoin LineJoin
+ {
+ get => (PenLineJoin)GetValue(LineJoinProperty);
+ set => SetValue(LineJoinProperty, value);
+ }
+
+ ///
+ /// Gets or sets the limit on the ratio of the miter length to half the thickness of the bar.
+ ///
+ [Category("GaugeBar")]
+ [Description("Gets or sets the limit on the ratio of the miter length to half the thickness of the bar.")]
+ [DefaultValue(0D)]
+ [TypeConverter(typeof(LengthConverter))]
+ public double MiterLimit
+ {
+ get => (double)GetValue(MiterLimitProperty);
+ set => SetValue(MiterLimitProperty, value);
+ }
+
+ ///
+ /// Gets or sets the brush that will be used to draw the indicator.
+ ///
+ [Description("Gets or sets the brush that will be used to draw the indicator.")]
+ public Brush Indicator
+ {
+ get => (Brush)GetValue(IndicatorProperty);
+ set => SetValue(IndicatorProperty, value);
+ }
+
+ ///
+ /// Gets or sets the to use at the start of the indicator.
+ ///
+ [Category("GaugeIndicator")]
+ [Description("Gets or sets the PenLineCap to use at the start of the indicator.")]
+ [DefaultValue(PenLineCap.Flat)]
+ public PenLineCap IndicatorStartLineCap
+ {
+ get => (PenLineCap)GetValue(IndicatorStartLineCapProperty);
+ set => SetValue(IndicatorStartLineCapProperty, value);
+ }
+
+ ///
+ /// Gets or sets the to use at the end of the indicator.
+ ///
+ [Category("GaugeIndicator")]
+ [Description("Gets or sets the PenLineCap to use at the end of the indicator.")]
+ [DefaultValue(PenLineCap.Flat)]
+ public PenLineCap IndicatorEndLineCap
+ {
+ get => (PenLineCap)GetValue(IndicatorEndLineCapProperty);
+ set => SetValue(IndicatorEndLineCapProperty, value);
+ }
+
+ ///
+ /// Gets or sets the to use for the dashes of the indicator.
+ ///
+ [Category("GaugeIndicator")]
+ [Description("Gets or sets the PenLineCap to use for the dashes of the indicator.")]
+ [DefaultValue(PenLineCap.Flat)]
+ public PenLineCap IndicatorDashCap
+ {
+ get => (PenLineCap)GetValue(IndicatorDashCapProperty);
+ set => SetValue(IndicatorDashCapProperty, value);
+ }
+
+ ///
+ /// Gets or sets the sheme of dashes and gaps that will be used within the indicator.
+ ///
+ [Category("GaugeIndicator")]
+ [Description("Gets or sets the sheme of dashes and gaps that will be used within the indicator.")]
+ [DefaultValue(null)]
+ [TypeConverter(typeof(DoubleCollectionConverter))]
+ public DoubleCollection? IndicatorDashArray
+ {
+ get => (DoubleCollection?)GetValue(IndicatorDashArrayProperty);
+ set => SetValue(IndicatorDashArrayProperty, value);
+ }
+
+ ///
+ /// Gets or sets the distance within the dash pattern where a dash begins.
+ ///
+ [Category("GaugeIndicator")]
+ [Description("Gets or sets the distance within the dash pattern where a dash begins.")]
+ [DefaultValue(0D)]
+ [TypeConverter(typeof(LengthConverter))]
+ public double IndicatorDashOffset
+ {
+ get => (double)GetValue(IndicatorDashOffsetProperty);
+ set => SetValue(IndicatorDashOffsetProperty, value);
+ }
+
+ ///
+ /// Gets or sets the shape that joins two lines or segments of the indicator.
+ ///
+ [Category("GaugeIndicator")]
+ [Description("Gets or sets the shape that joins two lines or segments of the indicator.")]
+ [DefaultValue(PenLineJoin.Miter)]
+ public PenLineJoin IndicatorLineJoin
+ {
+ get => (PenLineJoin)GetValue(IndicatorLineJoinProperty);
+ set => SetValue(IndicatorLineJoinProperty, value);
+ }
+
+ ///
+ /// Gets or sets the limit on the ratio of the miter length to half the thickness of the indicator.
+ ///
+ [Category("GaugeIndicator")]
+ [Description("Gets or sets the limit on the ratio of the miter length to half the thickness of the indicator.")]
+ [DefaultValue(0D)]
+ [TypeConverter(typeof(LengthConverter))]
+ public double IndicatorMiterLimit
+ {
+ get => (double)GetValue(IndicatorMiterLimitProperty);
+ set => SetValue(IndicatorMiterLimitProperty, value);
+ }
+
+ /// Initializes static members of the class. Overrides default properties.
+ static Gauge()
+ {
+ DefaultStyleKeyProperty.OverrideMetadata(typeof(Gauge), new FrameworkPropertyMetadata(typeof(Gauge)));
+
+ _ = MinimumProperty.AddOwner(typeof(Gauge), new FrameworkPropertyMetadata(0D, FrameworkPropertyMetadataOptions.AffectsRender));
+ _ = MaximumProperty.AddOwner(typeof(Gauge), new FrameworkPropertyMetadata(100D, FrameworkPropertyMetadataOptions.AffectsRender));
+ _ = ValueProperty.AddOwner(typeof(Gauge), new FrameworkPropertyMetadata(0D, FrameworkPropertyMetadataOptions.AffectsRender));
+
+ _ = ForegroundProperty.AddOwner(typeof(Gauge), new FrameworkPropertyMetadata(Brushes.Transparent, FrameworkPropertyMetadataOptions.AffectsRender));
+ _ = BackgroundProperty.AddOwner(typeof(Gauge), new FrameworkPropertyMetadata(Brushes.Transparent, FrameworkPropertyMetadataOptions.AffectsRender));
+ }
+
+ ///
+ protected override void OnRender(DrawingContext drawingContext)
+ {
+ // Background
+ Pen backgroundPen = new()
+ {
+ Brush = Background,
+ Thickness = Math.Abs(Thickness),
+
+ StartLineCap = StartLineCap,
+ EndLineCap = EndLineCap,
+ DashCap = DashCap,
+
+ LineJoin = LineJoin,
+ MiterLimit = MiterLimit,
+ };
+
+ if (DashArray is not null)
+ {
+ backgroundPen.DashStyle = new DashStyle(DashArray, DashOffset);
+ }
+
+ drawingContext.DrawGeometry(Background, backgroundPen, GetArcGeometry(EndAngle));
+
+ // Foreground
+ Pen gaugePen = new()
+ {
+ Brush = Indicator,
+ Thickness = Math.Abs(Thickness),
+
+ StartLineCap = IndicatorStartLineCap,
+ EndLineCap = IndicatorEndLineCap,
+ DashCap = IndicatorDashCap,
+
+ LineJoin = IndicatorLineJoin,
+ MiterLimit = IndicatorMiterLimit,
+ };
+
+ if (IndicatorDashArray is not null)
+ {
+ gaugePen.DashStyle = new DashStyle(IndicatorDashArray, IndicatorDashOffset);
+ }
+
+ drawingContext.DrawGeometry(Indicator, gaugePen, GetArcGeometry(GetAngleForValue()));
+
+ base.OnRender(drawingContext);
+ }
+
+ ///
+ /// Gets the geometry of the gauge.
+ ///
+ /// Based on the method.
+ /// The angle to draw the arc to.
+ /// The geometry of the gauge.
+ private StreamGeometry GetArcGeometry(double endAngle)
+ {
+ StreamGeometry streamGeometry = new();
+ using StreamGeometryContext geometryContext = streamGeometry.Open();
+
+ geometryContext.BeginFigure(
+ startPoint: GetPointAtAngle(Math.Min(StartAngle, endAngle)),
+ isFilled: false,
+ isClosed: false);
+
+ geometryContext.ArcTo(
+ point: GetPointAtAngle(Math.Max(StartAngle, endAngle)),
+ size: new Size(Math.Max(0, (RenderSize.Width - Thickness) / 2), Math.Max(0, (RenderSize.Height - Thickness) / 2)),
+ rotationAngle: 0,
+ isLargeArc: Math.Abs(endAngle - StartAngle) > 180,
+ sweepDirection: SweepDirection.Counterclockwise,
+ isStroked: true,
+ isSmoothJoin: false);
+
+ streamGeometry.Transform = new TranslateTransform(Thickness / 2, Thickness / 2);
+
+ return streamGeometry;
+ }
+
+ ///
+ /// Gets the point at the given angle.
+ ///
+ /// Based on the method.
+ /// The angle to get the point at.
+ /// The point at the given angle.
+ protected Point GetPointAtAngle(double angle)
+ {
+ var radAngle = angle * (Math.PI / 180);
+ var xRadius = (RenderSize.Width - Thickness) / 2;
+ var yRadius = (RenderSize.Height - Thickness) / 2;
+
+ return new(xRadius + (xRadius * Math.Cos(radAngle)), yRadius - (yRadius * Math.Sin(radAngle)));
+ }
+
+ ///
+ /// Gets the angle for the current value.
+ ///
+ /// The angle for the current value.
+ protected double GetAngleForValue()
+ {
+ var endAngle = Math.Abs(EndAngle);
+ var minValue = Math.Abs(Minimum);
+ var normalizedValue = (Math.Abs(Value) - minValue) / (Math.Abs(Maximum) - minValue);
+
+ return ((1 - normalizedValue) * (Math.Abs(StartAngle) + endAngle)) - endAngle;
+ }
+}
diff --git a/src/Wpf.Ui/Controls/Gauge/Gauge.xaml b/src/Wpf.Ui/Controls/Gauge/Gauge.xaml
new file mode 100644
index 000000000..6dde0ad62
--- /dev/null
+++ b/src/Wpf.Ui/Controls/Gauge/Gauge.xaml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Wpf.Ui/VisualStudioToolsManifest.xml b/src/Wpf.Ui/VisualStudioToolsManifest.xml
index 7444460fc..acd68ab5a 100644
--- a/src/Wpf.Ui/VisualStudioToolsManifest.xml
+++ b/src/Wpf.Ui/VisualStudioToolsManifest.xml
@@ -22,6 +22,7 @@
+