Preview 3: Complete control implementation with XAML data binding

Major milestone adding full control functionality:

Controls Enhanced:
- Entry/Editor: Full keyboard input, cursor navigation, selection, clipboard
- CollectionView: Data binding, selection highlighting, scrolling
- CheckBox/Switch/Slider: Interactive state management
- Picker/DatePicker/TimePicker: Dropdown selection with popup overlays
- ProgressBar/ActivityIndicator: Animated progress display
- Button: Press/release visual states
- Border/Frame: Rounded corners, stroke styling
- Label: Text wrapping, alignment, decorations
- Grid/StackLayout: Margin and padding support

Features Added:
- DisplayAlert dialogs with button actions
- NavigationPage with toolbar and back navigation
- Shell with flyout menu navigation
- XAML value converters for data binding
- Margin support in all layout containers
- Popup overlay system for pickers

New Samples:
- TodoApp: Full CRUD task manager with NavigationPage
- ShellDemo: Comprehensive control showcase

Removed:
- ControlGallery (replaced by ShellDemo)
- LinuxDemo (replaced by TodoApp)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
logikonline
2025-12-21 13:26:56 -05:00
parent f945d2a537
commit 1d55ac672a
142 changed files with 38925 additions and 4201 deletions

View File

@@ -6,40 +6,156 @@ using SkiaSharp;
namespace Microsoft.Maui.Platform;
/// <summary>
/// Skia-rendered progress bar control.
/// Skia-rendered progress bar control with full XAML styling support.
/// </summary>
public class SkiaProgressBar : SkiaView
{
private double _progress;
#region BindableProperties
/// <summary>
/// Bindable property for Progress.
/// </summary>
public static readonly BindableProperty ProgressProperty =
BindableProperty.Create(
nameof(Progress),
typeof(double),
typeof(SkiaProgressBar),
0.0,
BindingMode.TwoWay,
coerceValue: (b, v) => Math.Clamp((double)v, 0, 1),
propertyChanged: (b, o, n) => ((SkiaProgressBar)b).OnProgressChanged());
/// <summary>
/// Bindable property for TrackColor.
/// </summary>
public static readonly BindableProperty TrackColorProperty =
BindableProperty.Create(
nameof(TrackColor),
typeof(SKColor),
typeof(SkiaProgressBar),
new SKColor(0xE0, 0xE0, 0xE0),
propertyChanged: (b, o, n) => ((SkiaProgressBar)b).Invalidate());
/// <summary>
/// Bindable property for ProgressColor.
/// </summary>
public static readonly BindableProperty ProgressColorProperty =
BindableProperty.Create(
nameof(ProgressColor),
typeof(SKColor),
typeof(SkiaProgressBar),
new SKColor(0x21, 0x96, 0xF3),
propertyChanged: (b, o, n) => ((SkiaProgressBar)b).Invalidate());
/// <summary>
/// Bindable property for DisabledColor.
/// </summary>
public static readonly BindableProperty DisabledColorProperty =
BindableProperty.Create(
nameof(DisabledColor),
typeof(SKColor),
typeof(SkiaProgressBar),
new SKColor(0xBD, 0xBD, 0xBD),
propertyChanged: (b, o, n) => ((SkiaProgressBar)b).Invalidate());
/// <summary>
/// Bindable property for BarHeight.
/// </summary>
public static readonly BindableProperty BarHeightProperty =
BindableProperty.Create(
nameof(BarHeight),
typeof(float),
typeof(SkiaProgressBar),
4f,
propertyChanged: (b, o, n) => ((SkiaProgressBar)b).InvalidateMeasure());
/// <summary>
/// Bindable property for CornerRadius.
/// </summary>
public static readonly BindableProperty CornerRadiusProperty =
BindableProperty.Create(
nameof(CornerRadius),
typeof(float),
typeof(SkiaProgressBar),
2f,
propertyChanged: (b, o, n) => ((SkiaProgressBar)b).Invalidate());
#endregion
#region Properties
/// <summary>
/// Gets or sets the progress value (0.0 to 1.0).
/// </summary>
public double Progress
{
get => _progress;
set
{
var clamped = Math.Clamp(value, 0, 1);
if (_progress != clamped)
{
_progress = clamped;
ProgressChanged?.Invoke(this, new ProgressChangedEventArgs(_progress));
Invalidate();
}
}
get => (double)GetValue(ProgressProperty);
set => SetValue(ProgressProperty, value);
}
public SKColor TrackColor { get; set; } = new SKColor(0xE0, 0xE0, 0xE0);
public SKColor ProgressColor { get; set; } = new SKColor(0x21, 0x96, 0xF3);
public SKColor DisabledColor { get; set; } = new SKColor(0xBD, 0xBD, 0xBD);
public float Height { get; set; } = 4;
public float CornerRadius { get; set; } = 2;
/// <summary>
/// Gets or sets the track color.
/// </summary>
public SKColor TrackColor
{
get => (SKColor)GetValue(TrackColorProperty);
set => SetValue(TrackColorProperty, value);
}
/// <summary>
/// Gets or sets the progress color.
/// </summary>
public SKColor ProgressColor
{
get => (SKColor)GetValue(ProgressColorProperty);
set => SetValue(ProgressColorProperty, value);
}
/// <summary>
/// Gets or sets the disabled color.
/// </summary>
public SKColor DisabledColor
{
get => (SKColor)GetValue(DisabledColorProperty);
set => SetValue(DisabledColorProperty, value);
}
/// <summary>
/// Gets or sets the bar height.
/// </summary>
public float BarHeight
{
get => (float)GetValue(BarHeightProperty);
set => SetValue(BarHeightProperty, value);
}
/// <summary>
/// Gets or sets the corner radius.
/// </summary>
public float CornerRadius
{
get => (float)GetValue(CornerRadiusProperty);
set => SetValue(CornerRadiusProperty, value);
}
#endregion
/// <summary>
/// Event raised when progress changes.
/// </summary>
public event EventHandler<ProgressChangedEventArgs>? ProgressChanged;
private void OnProgressChanged()
{
ProgressChanged?.Invoke(this, new ProgressChangedEventArgs(Progress));
Invalidate();
}
protected override void OnDraw(SKCanvas canvas, SKRect bounds)
{
var trackY = bounds.MidY;
var trackTop = trackY - Height / 2;
var trackBottom = trackY + Height / 2;
var trackTop = trackY - BarHeight / 2;
var trackBottom = trackY + BarHeight / 2;
// Draw track
using var trackPaint = new SKPaint
@@ -75,10 +191,13 @@ public class SkiaProgressBar : SkiaView
protected override SKSize MeasureOverride(SKSize availableSize)
{
return new SKSize(200, Height + 8);
return new SKSize(200, BarHeight + 8);
}
}
/// <summary>
/// Event args for progress changed events.
/// </summary>
public class ProgressChangedEventArgs : EventArgs
{
public double Progress { get; }