我的头衔很烂,你可以随意改.

我找到一张tutorial for making custom controls in Maui using SkiaSharp美元的钞票.我使用C#标记而不是XAML对其进行了一些修改.除了必须用ProgressBar.Maui.ProgressBar progressBar而不是ProgressBar progressBar实例化进度条之外,一切都运行得很好.

我想稍微修改一下,将重画限制为仅在达到某个进度更改阈值后才会发生.(即,每x%进度只重画一次).我添加了一个按小于阈值的速度递增进度的按钮,因此我应该必须单击这些按钮几次才能看到进度条更新.但是,每次我单击该按钮时,进度条都会更新.

如果我手动更改进度,比如将progressBar.Progress = 0.01f;添加到页面构造函数的末尾,进度栏将按预期运行.

以下是所有相关代码[我认为]

我的库项目只有2个文件

ProgressBar.cs

using SkiaSharp;
using SkiaSharp.Views.Maui;
using SkiaSharp.Views.Maui.Controls;
using System.Diagnostics;

namespace ProgressBar.Maui;

// All the code in this file is included in all platforms.
public class ProgressBar : SKCanvasView
{
   public static readonly BindableProperty ProgressProperty =
   BindableProperty.Create(nameof(Progress), typeof(float), typeof(ProgressBar), 0f, propertyChanged: OnBindablePropertyChanged);

   public static readonly BindableProperty ProgressColorProperty =
      BindableProperty.Create(nameof(ProgressColor), typeof(Color), typeof(ProgressBar), Colors.CornflowerBlue, propertyChanged: OnBindablePropertyChanged);

   public static readonly BindableProperty BaseColorProperty =
      BindableProperty.Create(nameof(BaseColor), typeof(Color), typeof(ProgressBar), Colors.LightGray, propertyChanged: OnBindablePropertyChanged);

   public float Progress
   {
      get => (float)GetValue(ProgressProperty);
      set
      {
         if (Math.Abs(value - _previousProgress) >= _RedrawThreshold)
         {
            SetValue(ProgressProperty, value);
            _previousProgress = value;
            Debug.WriteLine("Progress updated 和 surface invalidated.");
         }
         else
         {
            Debug.WriteLine("Progress change below threshold. No redraw.");
         }
      }
   }

   public Color ProgressColor
   {
      get => (Color)GetValue(ProgressColorProperty);
      set => SetValue(ProgressColorProperty, value);
   }
   public Color BaseColor
   {
      get => (Color)GetValue(BaseColorProperty);
      set => SetValue(BaseColorProperty, value);
   }


   // actual canvas instance to draw on
   private SKCanvas _canvas;

   // rectangle which will be used to draw the Progress Bar
   private SKRect _drawRect;

   // holds information about the dimensions, etc.
   private SKImageInfo _info;

   private const float _RedrawThreshold = 0.5f; // 1% change
   private float _previousProgress = 0f;

   private SKPaint _basePaint = new()
   {
      Style = SKPaintStyle.Fill,
      IsAntialias = true,
   };

   private SKPaint _progressPaint = new()
   {
      Style = SKPaintStyle.Fill,
      IsAntialias = true,
   };



   protected override void OnPaintSurface (SKPaintSurfaceEventArgs e)
   {
      base.OnPaintSurface(e);

      _canvas = e.Surface.Canvas;
      _canvas.Clear();

      _info = e.Info;

      _drawRect = new SKRect(0, 0, _info.Width, _info.Height);

      DrawBase();
      DrawProgress();
   }

   private void DrawBase ()
   {
      using SKPath basePath = new();

      basePath.AddRect(_drawRect);

      _basePaint.Color = BaseColor.ToSKColor();

      _canvas.DrawPath(basePath, _basePaint);
   }

   private void DrawProgress ()
   {
      using SKPath progressPath = new();

      var progressRect = new SKRect(0, 0, _info.Width * Progress, _info.Height);

      progressPath.AddRect(progressRect);

      _progressPaint.Color = ProgressColor.ToSKColor();

      _canvas.DrawPath(progressPath, _progressPaint);
   }

   private static void OnBindablePropertyChanged (BindableObject bindable, object oldValue, object newValue)
   {
      if (oldValue != newValue)
         ((ProgressBar)bindable).InvalidateSurface();
   }
}

Registration.cs

using SkiaSharp.Views.Maui.H和lers;

namespace ProgressBar.Maui;
public static class Registration
{
   public static MauiAppBuilder UseProgressBar(this MauiAppBuilder builder)
   {
      builder.ConfigureMauiH和lers(h =>
      {
         h.AddH和ler<ProgressBar, SKCanvasViewH和ler>();
      });

      return builder;
   }
}

我的样例项目包含

SamplePage.cs


namespace ProgressBarSample.Views;

public partial class SamplePage : ContentPage
{
   public SamplePage (SampleViewModel viewModel)
   {
      BindingContext = viewModel;

      Content = new VerticalStackLayout
      {
         Spacing = 30,
         Padding = new Thickness(30, 0),
         VerticalOptions = LayoutOptions.Center,

         Children =
         {
            new ProgressBar.Maui.ProgressBar()
            {
               WidthRequest = 300,
               HeightRequest = 5,
               ProgressColor = Colors.DeepSkyBlue,
            }
            .Assign(out ProgressBar.Maui.ProgressBar progressBar)
            .Bind(ProgressBar.Maui.ProgressBar.ProgressProperty, nameof(SampleViewModel.Progress), BindingMode.OneWay),

            new Button
            {
               Text = "Increment",
            }
            .BindComm和(nameof(SampleViewModel.IncrementProgressComm和))
         }
      };
   }
}

SampleViewModel.cs

namespace ProgressBarSample.ViewModels;

public partial class SampleViewModel : BaseViewModel
{
    [ObservableProperty]
    private float progress = 0.01f;

    [RelayComm和]
    private void IncrementProgress()
    {
        Debug.WriteLine("IncrementProgress()");
        Progress +=0.01f;
    }
}

BaseViewModel.cs是继承ObservableObject的空类

我还把builder.UseProgressBar()加到了MauiProgram.cs

推荐答案

我想我想通了.在C#标记中,.Bind扩展直接绑定到ProgressProperty,而不是公共属性Progress.这意味着,我认为,当我单击按钮时,阈值逻辑永远不会运行.

我只是稍微修改了一下逻辑

ProgressBar.cs

using SkiaSharp;
using SkiaSharp.Views.Maui;
using SkiaSharp.Views.Maui.Controls;
using System.Diagnostics;

namespace ProgressBar.Maui;

// All the code in this file is included in all platforms.
public class ProgressBar : SKCanvasView
{
   public static readonly BindableProperty ProgressProperty =
   BindableProperty.Create(nameof(Progress), typeof(float), typeof(ProgressBar), 0f, propertyChanged: OnProgressPropertyChanged, coerceValue: ClampProgressValue);

   public static readonly BindableProperty ProgressColorProperty =
      BindableProperty.Create(nameof(ProgressColor), typeof(Color), typeof(ProgressBar), Colors.CornflowerBlue, propertyChanged: OnColorPropertyChanged);

   public static readonly BindableProperty BaseColorProperty =
      BindableProperty.Create(nameof(BaseColor), typeof(Color), typeof(ProgressBar), Colors.LightGray, propertyChanged: OnColorPropertyChanged);

   public float Progress
   {
      get => (float)GetValue(ProgressProperty);
      set => SetValue(ProgressProperty, value);
   }

   public Color ProgressColor
   {
      get => (Color)GetValue(ProgressColorProperty);
      set => SetValue(ProgressColorProperty, value);
   }
   public Color BaseColor
   {
      get => (Color)GetValue(BaseColorProperty);
      set => SetValue(BaseColorProperty, value);
   }


   // actual canvas instance to draw on
   private SKCanvas _canvas;

   // rectangle which will be used to draw the Progress Bar
   private SKRect _drawRect;

   // holds information about the dimensions, etc.
   private SKImageInfo _info;

   private const float _RedrawThreshold = 0.1f; // 1% change
   private float _displayedProgress = 0f;

   private SKPaint _basePaint = new()
   {
      Style = SKPaintStyle.Fill,
      IsAntialias = true,
   };

   private SKPaint _progressPaint = new()
   {
      Style = SKPaintStyle.Fill,
      IsAntialias = true,
   };


   protected override void OnPaintSurface (SKPaintSurfaceEventArgs e)
   {
      base.OnPaintSurface(e);

      _canvas = e.Surface.Canvas;
      _canvas.Clear();

      _info = e.Info;

      _drawRect = new SKRect(0, 0, _info.Width, _info.Height);

      DrawBase();
      DrawProgress();
   }

   private void DrawBase ()
   {
      using SKPath basePath = new();

      basePath.AddRect(_drawRect);

      _basePaint.Color = BaseColor.ToSKColor();

      _canvas.DrawPath(basePath, _basePaint);
   }

   private void DrawProgress ()
   {
      using SKPath progressPath = new();

      var progressRect = new SKRect(0, 0, _info.Width * Progress, _info.Height);

      progressPath.AddRect(progressRect);

      _progressPaint.Color = ProgressColor.ToSKColor();

      _canvas.DrawPath(progressPath, _progressPaint);
   }

   private static void OnProgressPropertyChanged (BindableObject bindable, object oldValue, object newValue)
   {
      if (oldValue == newValue)
         return;

      if ((float)newValue - ((ProgressBar)bindable)._displayedProgress >= _RedrawThreshold)
         ((ProgressBar)bindable).Update();
   }

   private static void OnColorPropertyChanged (BindableObject bindable, object oldValue, object newValue)
   {
      if (oldValue != newValue)
         ((ProgressBar)bindable).InvalidateSurface();
   }

   public void Update ()
   {
      if (_displayedProgress != Progress)
      {
         _displayedProgress = Progress;
         InvalidateSurface();
      }
   }


   private static object ClampProgressValue (BindableObject bindable, object value)
   {
      return (float)value switch
      {
         < 0 => 0,
         > 1 => 1,
         _ => value,
      };
   }
}

Csharp相关问答推荐

Select Many和默认IfEmpty内部Select Many错误工作

如何模拟耐久任务客户端在统一测试和获取错误在调度NewsListationInstanceAsync模拟设置

在具有主构造函数的类中初始化属性时出现警告

从Blob存储中提取tar.gz文件并将提取结果上载到另一个Blob存储

将轮询与超时同步

使用Orleans进行的单元测试找不到接口的实现

方法从数据表中只 Select 一个条件?

如何在Cosmos SDK中控制超时、重试和重试之间的延迟?

该函数不能检测两条曲线的交点

在C#中过滤Excel文件

WPF DataGrid文件名列,允许直接输入文本或通过对话框按键浏览

在ASP.NET Core 8 MVC中本地化共享视图

源代码生成器项目使用`dotnet build`编译,而不是在Visual Studio中编译?

如何从另一个类的列表中按ID取值

将J数组转换为列表,只保留一个嵌套的JToken

如何保存具有多个重叠图片框的图片框?

无法创建工具窗口(用于VBIDE、VBA的COM加载项扩展)

如何提高C#中比较大 struct 的性能?

MudRadioGroup不切换

C#:我需要根据换行符拆分字符串,而不是在字符串中用双引号分隔换行符