Progressbar completed

This commit is contained in:
2026-01-16 05:04:05 +00:00
parent 8f1eba7fbe
commit 0094cdef45
2 changed files with 122 additions and 41 deletions

View File

@@ -5,7 +5,7 @@ using System.ComponentModel;
using Microsoft.Maui.Controls; using Microsoft.Maui.Controls;
using Microsoft.Maui.Handlers; using Microsoft.Maui.Handlers;
using Microsoft.Maui.Graphics; using Microsoft.Maui.Graphics;
using SkiaSharp; using Microsoft.Maui.Platform;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
@@ -56,6 +56,14 @@ public partial class ProgressBarHandler : ViewHandler<IProgress, SkiaProgressBar
{ {
platformView.IsVisible = visualElement.IsVisible; platformView.IsVisible = visualElement.IsVisible;
} }
// Sync properties
if (VirtualView != null)
{
MapProgress(this, VirtualView);
MapProgressColor(this, VirtualView);
MapIsEnabled(this, VirtualView);
}
} }
protected override void DisconnectHandler(SkiaProgressBar platformView) protected override void DisconnectHandler(SkiaProgressBar platformView)
@@ -89,7 +97,7 @@ public partial class ProgressBarHandler : ViewHandler<IProgress, SkiaProgressBar
if (progress.ProgressColor is not null) if (progress.ProgressColor is not null)
{ {
handler.PlatformView.ProgressColor = progress.ProgressColor.ToSKColor(); handler.PlatformView.ProgressColor = progress.ProgressColor;
} }
handler.PlatformView.Invalidate(); handler.PlatformView.Invalidate();
} }
@@ -119,7 +127,7 @@ public partial class ProgressBarHandler : ViewHandler<IProgress, SkiaProgressBar
if (progress is VisualElement visualElement && visualElement.BackgroundColor is not null) if (progress is VisualElement visualElement && visualElement.BackgroundColor is not null)
{ {
handler.PlatformView.BackgroundColor = visualElement.BackgroundColor.ToSKColor(); handler.PlatformView.TrackColor = visualElement.BackgroundColor;
handler.PlatformView.Invalidate(); handler.PlatformView.Invalidate();
} }
} }

View File

@@ -3,15 +3,33 @@
using System; using System;
using Microsoft.Maui.Controls; using Microsoft.Maui.Controls;
using Microsoft.Maui.Graphics;
using SkiaSharp; using SkiaSharp;
namespace Microsoft.Maui.Platform; namespace Microsoft.Maui.Platform;
/// <summary> /// <summary>
/// Skia-rendered progress bar control with full XAML styling support. /// Skia-rendered progress bar control with full MAUI compliance.
/// Implements IProgress interface requirements:
/// - Progress property (0.0 to 1.0)
/// - ProgressColor for the filled portion
/// </summary> /// </summary>
public class SkiaProgressBar : SkiaView public class SkiaProgressBar : SkiaView
{ {
#region SKColor Helper
private static SKColor ToSKColor(Color? color)
{
if (color == null) return SKColors.Transparent;
return new SKColor(
(byte)(color.Red * 255),
(byte)(color.Green * 255),
(byte)(color.Blue * 255),
(byte)(color.Alpha * 255));
}
#endregion
#region BindableProperties #region BindableProperties
/// <summary> /// <summary>
@@ -23,31 +41,31 @@ public class SkiaProgressBar : SkiaView
typeof(double), typeof(double),
typeof(SkiaProgressBar), typeof(SkiaProgressBar),
0.0, 0.0,
BindingMode.OneWay, BindingMode.TwoWay,
coerceValue: (b, v) => Math.Clamp((double)v, 0.0, 1.0), coerceValue: (b, v) => Math.Clamp((double)v, 0.0, 1.0),
propertyChanged: (b, o, n) => ((SkiaProgressBar)b).OnProgressChanged()); propertyChanged: (b, o, n) => ((SkiaProgressBar)b).OnProgressChanged((double)o, (double)n));
/// <summary> /// <summary>
/// Bindable property for TrackColor. /// Bindable property for TrackColor (background track).
/// </summary> /// </summary>
public static readonly BindableProperty TrackColorProperty = public static readonly BindableProperty TrackColorProperty =
BindableProperty.Create( BindableProperty.Create(
nameof(TrackColor), nameof(TrackColor),
typeof(SKColor), typeof(Color),
typeof(SkiaProgressBar), typeof(SkiaProgressBar),
new SKColor(224, 224, 224), Color.FromRgb(0xE0, 0xE0, 0xE0),
BindingMode.TwoWay, BindingMode.TwoWay,
propertyChanged: (b, o, n) => ((SkiaProgressBar)b).Invalidate()); propertyChanged: (b, o, n) => ((SkiaProgressBar)b).Invalidate());
/// <summary> /// <summary>
/// Bindable property for ProgressColor. /// Bindable property for ProgressColor (filled portion).
/// </summary> /// </summary>
public static readonly BindableProperty ProgressColorProperty = public static readonly BindableProperty ProgressColorProperty =
BindableProperty.Create( BindableProperty.Create(
nameof(ProgressColor), nameof(ProgressColor),
typeof(SKColor), typeof(Color),
typeof(SkiaProgressBar), typeof(SkiaProgressBar),
new SKColor(33, 150, 243), Color.FromRgb(0x21, 0x96, 0xF3), // Material Blue
BindingMode.TwoWay, BindingMode.TwoWay,
propertyChanged: (b, o, n) => ((SkiaProgressBar)b).Invalidate()); propertyChanged: (b, o, n) => ((SkiaProgressBar)b).Invalidate());
@@ -57,9 +75,9 @@ public class SkiaProgressBar : SkiaView
public static readonly BindableProperty DisabledColorProperty = public static readonly BindableProperty DisabledColorProperty =
BindableProperty.Create( BindableProperty.Create(
nameof(DisabledColor), nameof(DisabledColor),
typeof(SKColor), typeof(Color),
typeof(SkiaProgressBar), typeof(SkiaProgressBar),
new SKColor(189, 189, 189), Color.FromRgb(0xBD, 0xBD, 0xBD),
BindingMode.TwoWay, BindingMode.TwoWay,
propertyChanged: (b, o, n) => ((SkiaProgressBar)b).Invalidate()); propertyChanged: (b, o, n) => ((SkiaProgressBar)b).Invalidate());
@@ -69,9 +87,9 @@ public class SkiaProgressBar : SkiaView
public static readonly BindableProperty BarHeightProperty = public static readonly BindableProperty BarHeightProperty =
BindableProperty.Create( BindableProperty.Create(
nameof(BarHeight), nameof(BarHeight),
typeof(float), typeof(double),
typeof(SkiaProgressBar), typeof(SkiaProgressBar),
4f, 4.0,
BindingMode.TwoWay, BindingMode.TwoWay,
propertyChanged: (b, o, n) => ((SkiaProgressBar)b).InvalidateMeasure()); propertyChanged: (b, o, n) => ((SkiaProgressBar)b).InvalidateMeasure());
@@ -81,9 +99,9 @@ public class SkiaProgressBar : SkiaView
public static readonly BindableProperty CornerRadiusProperty = public static readonly BindableProperty CornerRadiusProperty =
BindableProperty.Create( BindableProperty.Create(
nameof(CornerRadius), nameof(CornerRadius),
typeof(float), typeof(double),
typeof(SkiaProgressBar), typeof(SkiaProgressBar),
2f, 2.0,
BindingMode.TwoWay, BindingMode.TwoWay,
propertyChanged: (b, o, n) => ((SkiaProgressBar)b).Invalidate()); propertyChanged: (b, o, n) => ((SkiaProgressBar)b).Invalidate());
@@ -101,80 +119,98 @@ public class SkiaProgressBar : SkiaView
} }
/// <summary> /// <summary>
/// Gets or sets the track color. /// Gets or sets the track color (background).
/// </summary> /// </summary>
public SKColor TrackColor public Color TrackColor
{ {
get => (SKColor)GetValue(TrackColorProperty); get => (Color)GetValue(TrackColorProperty);
set => SetValue(TrackColorProperty, value); set => SetValue(TrackColorProperty, value);
} }
/// <summary> /// <summary>
/// Gets or sets the progress color. /// Gets or sets the progress color (filled portion).
/// </summary> /// </summary>
public SKColor ProgressColor public Color ProgressColor
{ {
get => (SKColor)GetValue(ProgressColorProperty); get => (Color)GetValue(ProgressColorProperty);
set => SetValue(ProgressColorProperty, value); set => SetValue(ProgressColorProperty, value);
} }
/// <summary> /// <summary>
/// Gets or sets the disabled color. /// Gets or sets the disabled color.
/// </summary> /// </summary>
public SKColor DisabledColor public Color DisabledColor
{ {
get => (SKColor)GetValue(DisabledColorProperty); get => (Color)GetValue(DisabledColorProperty);
set => SetValue(DisabledColorProperty, value); set => SetValue(DisabledColorProperty, value);
} }
/// <summary> /// <summary>
/// Gets or sets the bar height. /// Gets or sets the bar height.
/// </summary> /// </summary>
public float BarHeight public double BarHeight
{ {
get => (float)GetValue(BarHeightProperty); get => (double)GetValue(BarHeightProperty);
set => SetValue(BarHeightProperty, value); set => SetValue(BarHeightProperty, value);
} }
/// <summary> /// <summary>
/// Gets or sets the corner radius. /// Gets or sets the corner radius.
/// </summary> /// </summary>
public float CornerRadius public double CornerRadius
{ {
get => (float)GetValue(CornerRadiusProperty); get => (double)GetValue(CornerRadiusProperty);
set => SetValue(CornerRadiusProperty, value); set => SetValue(CornerRadiusProperty, value);
} }
#endregion #endregion
#region Events
/// <summary> /// <summary>
/// Event raised when progress changes. /// Event raised when progress changes.
/// </summary> /// </summary>
public event EventHandler<ProgressChangedEventArgs>? ProgressChanged; public event EventHandler<ProgressChangedEventArgs>? ProgressChanged;
private void OnProgressChanged() #endregion
#region Event Handlers
private void OnProgressChanged(double oldValue, double newValue)
{ {
ProgressChanged?.Invoke(this, new ProgressChangedEventArgs(Progress)); ProgressChanged?.Invoke(this, new ProgressChangedEventArgs(oldValue, newValue));
Invalidate(); Invalidate();
} }
#endregion
#region Rendering
protected override void OnDraw(SKCanvas canvas, SKRect bounds) protected override void OnDraw(SKCanvas canvas, SKRect bounds)
{ {
var barHeight = (float)BarHeight;
var cornerRadius = (float)CornerRadius;
float midY = bounds.MidY; float midY = bounds.MidY;
float trackTop = midY - BarHeight / 2f; float trackTop = midY - barHeight / 2f;
float trackBottom = midY + BarHeight / 2f; float trackBottom = midY + barHeight / 2f;
// Get colors
var trackColorSK = ToSKColor(TrackColor);
var progressColorSK = ToSKColor(ProgressColor);
var disabledColorSK = ToSKColor(DisabledColor);
// Draw track // Draw track
using var trackPaint = new SKPaint using var trackPaint = new SKPaint
{ {
Color = IsEnabled ? TrackColor : DisabledColor, Color = IsEnabled ? trackColorSK : disabledColorSK,
IsAntialias = true, IsAntialias = true,
Style = SKPaintStyle.Fill Style = SKPaintStyle.Fill
}; };
var trackRect = new SKRoundRect( var trackRect = new SKRoundRect(
new SKRect(bounds.Left, trackTop, bounds.Right, trackBottom), new SKRect(bounds.Left, trackTop, bounds.Right, trackBottom),
CornerRadius); cornerRadius);
canvas.DrawRoundRect(trackRect, trackPaint); canvas.DrawRoundRect(trackRect, trackPaint);
// Draw progress // Draw progress
@@ -184,22 +220,41 @@ public class SkiaProgressBar : SkiaView
using var progressPaint = new SKPaint using var progressPaint = new SKPaint
{ {
Color = IsEnabled ? ProgressColor : DisabledColor, Color = IsEnabled ? progressColorSK : disabledColorSK,
IsAntialias = true, IsAntialias = true,
Style = SKPaintStyle.Fill Style = SKPaintStyle.Fill
}; };
var progressRect = new SKRoundRect( var progressRect = new SKRoundRect(
new SKRect(bounds.Left, trackTop, bounds.Left + progressWidth, trackBottom), new SKRect(bounds.Left, trackTop, bounds.Left + progressWidth, trackBottom),
CornerRadius); cornerRadius);
canvas.DrawRoundRect(progressRect, progressPaint); canvas.DrawRoundRect(progressRect, progressPaint);
} }
} }
#endregion
#region Lifecycle
protected override void OnEnabledChanged()
{
base.OnEnabledChanged();
SkiaVisualStateManager.GoToState(this, IsEnabled
? SkiaVisualStateManager.CommonStates.Normal
: SkiaVisualStateManager.CommonStates.Disabled);
}
#endregion
#region Layout
protected override SKSize MeasureOverride(SKSize availableSize) protected override SKSize MeasureOverride(SKSize availableSize)
{ {
return new SKSize(200f, BarHeight + 8f); var barHeight = (float)BarHeight;
return new SKSize(200f, barHeight + 8f);
} }
#endregion
} }
/// <summary> /// <summary>
@@ -207,6 +262,24 @@ public class SkiaProgressBar : SkiaView
/// </summary> /// </summary>
public class ProgressChangedEventArgs : EventArgs public class ProgressChangedEventArgs : EventArgs
{ {
public double Progress { get; } /// <summary>
public ProgressChangedEventArgs(double progress) => Progress = progress; /// Gets the old progress value.
/// </summary>
public double OldProgress { get; }
/// <summary>
/// Gets the new progress value.
/// </summary>
public double NewProgress { get; }
/// <summary>
/// Gets the current progress value (same as NewProgress).
/// </summary>
public double Progress => NewProgress;
public ProgressChangedEventArgs(double oldProgress, double newProgress)
{
OldProgress = oldProgress;
NewProgress = newProgress;
}
} }