Я пытаюсь создать циклический индикатор выполнения, используя ProgressTo в сочетании с некоторыми привязками. Я могу заставить его работать, вручную анимируя индикатор выполнения без использования ProgressTo, но не могу заставить его работать правильно с помощью ProgressTo (я не могу сбросить прогресс). Изменить: удален код с ошибками, код, который будет работать, но не будет работать должным образом, см. ниже.
Как только индикатор выполнения достигнет 100 %, он остается там, а не возвращается на велосипеде.
Я тоже не понял, как изменить параметры ProgressTo, что является второй частью вопроса, поскольку скорость его заполнения во время второго и последующих взаимодействий будет отличаться от первой итерации.
При анимации вручную, которая работает, но которую я не хочу использовать, если смогу, отличия от описанной выше:
internal class RandomStringEnumerator(int seed) : IEnumerator
{
Random rng = new Random(seed);
int initialSeed = seed;
string? current;
public string Current => current ?? string.Empty;
object IEnumerator.Current => current ?? string.Empty;
public void Dispose()
{
throw new NotImplementedException();
}
public bool MoveNext()
{
char[] chars = new char[rng.Next(5) + 5];
for (int i = 0; i < chars.Length; i++)
{
chars[i] = (char)(0x20 + rng.Next(0x5e));
}
current = new string(chars);
return true;
}
public void Reset()
{
current = null;
rng = new Random(initialSeed);
}
}
public partial class MainPage : ContentPage
{
private int count = 0;
private readonly SourceClass instanceOfSourceClass;
private readonly int seed = 10;
public MainPage()
{
InitializeComponent();
IEnumerator rse = new RandomStringEnumerator(seed);
// discard first empty string element.
rse.MoveNext();
instanceOfSourceClass = new(DateTime.Now, TimeSpan.FromSeconds(30), rse);
}
protected override void OnAppearing()
{
base.OnAppearing();
lbl.SetBinding(Label.TextProperty, new Binding("Current", source: instanceOfSourceClass, converter: segmentValue));
pb.SetBinding(ProgressBar.ProgressProperty, new Binding("Current", source: instanceOfSourceClass, converter: segmentToProgress));
pb.ProgressTo(1, (uint)(instanceOfSourceClass.Current.End - DateTime.Now).TotalMilliseconds, Easing.Linear);
}
private void OnCounterClicked(object sender, EventArgs e)
{
count++;
if (count == 1)
CounterBtn.Text = $"Clicked {count} time";
else
CounterBtn.Text = $"Clicked {count} times";
SemanticScreenReader.Announce(CounterBtn.Text);
}
private static readonly SegmentToProgress segmentToProgress = new();
internal class SegmentToProgress : IValueConverter
{
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
// since callback will be called when expired, always reset it.
return 0d;
}
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
private static readonly SegmentValue segmentValue = new();
internal class SegmentValue : IValueConverter
{
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
// since callback will be called when expired, always reset it.
if (value is SourceClass.Segment segment)
{
return segment.Value;
}
return "could not find segment value";
}
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
Что делает приведенное выше:
Показывает метку со случайной строкой и индикатором выполнения, показывающим, когда она изменится. .
Когда индикатор выполнения достигает конца, метка меняется, но индикатор выполнения НЕ начинается снова.
Что он должен делать:
Показывает метку со случайной строкой и индикатором выполнения, показывающим, когда она будет меняться.
Когда индикатор выполнения достигает конца, метка меняется, и начинается индикатор выполнения. с самого начала, чтобы показать, когда метка будет изменена в следующий раз.
Я пытаюсь создать циклический индикатор выполнения, используя ProgressTo в сочетании с некоторыми привязками. Я могу заставить его работать, вручную анимируя индикатор выполнения без использования ProgressTo, но не могу заставить его работать правильно с помощью ProgressTo (я не могу сбросить прогресс). [b]Изменить: удален код с ошибками, код, который будет работать, но не будет работать должным образом, см. ниже.[/b] Как только индикатор выполнения достигнет 100 %, он остается там, а не возвращается на велосипеде. Я тоже не понял, как изменить параметры ProgressTo, что является второй частью вопроса, поскольку скорость его заполнения во время второго и последующих взаимодействий будет отличаться от первой итерации. При анимации вручную, которая работает, но которую я не хочу использовать, если смогу, отличия от описанной выше: [list] [*][code]_timer[/code] настроен на запуск сейчас и повторение каждые 10 мс [*][code]_timer[/code] просто вызывает OnPropertyChanged() [*][code]Current { get; }[/code] проверяет срок действия и переходит к новому сегменту, если срок действия _current истек. [*][code]Current { private set; }[/code] не вызывает OnPropertyChanged() [*][code]SegmentToProgress[/code] реализован правильно, а не жестко запрограммирован 0d. [/list] [b]--- Редактировать: похоже, я сделал несколько ошибки при копировании здесь. Минимальный воспроизводимый код, как указано в комментарии, приведен ниже.[/b] [list] [*]Создайте проект MAUI в Visual Studio. У него есть CounterBtn с текстом «Нажми на меня». [*]Добавьте SourceClass (см. ниже). [*]Измените MainPage.xaml. чтобы добавить: и метку [*]Добавить RandomStringEnumerator ( см. ниже) или какой-либо другой бесконечный перечислитель по вашему выбору. [*]Отредактируйте MainPage.xaml.cs [/list] [b]Содержимое SourceClass .cs[/b] [code] public class SourceClass : INotifyPropertyChanged { public event PropertyChangedEventHandler? PropertyChanged;
public SourceClass(DateTime initial, TimeSpan period, IEnumerator emitter) { _initial = initial; _period = period; _emitter = emitter; _current = new Segment(_initial, _initial + _period, emitter.Current); _timer = new Timer((s) => { emitter.MoveNext(); Current = new Segment(Current.End, Current.End + _period, _emitter.Current); }); _timer.Change(Current.End - DateTime.Now, period);
OnPropertyChanged(nameof(Current)); }
public Segment Current { get => _current; private set { _current = value; OnPropertyChanged(); } }
public class Segment(DateTime Start, DateTime End, T Value) { public DateTime Start { get; } = Start; public DateTime End { get; } = End; public T Value = Value; }
public void OnPropertyChanged([CallerMemberName] string name = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); } [/code] [b]Содержимое RandomStringEnumerator.cs[/b] [code] internal class RandomStringEnumerator(int seed) : IEnumerator { Random rng = new Random(seed); int initialSeed = seed; string? current;
public string Current => current ?? string.Empty;
object IEnumerator.Current => current ?? string.Empty;
public void Dispose() { throw new NotImplementedException(); }
public bool MoveNext() { char[] chars = new char[rng.Next(5) + 5]; for (int i = 0; i < chars.Length; i++) { chars[i] = (char)(0x20 + rng.Next(0x5e)); }
current = new string(chars); return true; }
public void Reset() { current = null; rng = new Random(initialSeed); } } [/code] [b]Содержимое MainPage.xaml.cs[/b] [code] public partial class MainPage : ContentPage { private int count = 0; private readonly SourceClass instanceOfSourceClass; private readonly int seed = 10;
public MainPage() { InitializeComponent(); IEnumerator rse = new RandomStringEnumerator(seed); // discard first empty string element. rse.MoveNext(); instanceOfSourceClass = new(DateTime.Now, TimeSpan.FromSeconds(30), rse); }
private static readonly SegmentToProgress segmentToProgress = new(); internal class SegmentToProgress : IValueConverter { public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) { // since callback will be called when expired, always reset it. return 0d; }
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) { throw new NotImplementedException(); } }
private static readonly SegmentValue segmentValue = new(); internal class SegmentValue : IValueConverter { public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) { // since callback will be called when expired, always reset it. if (value is SourceClass.Segment segment) { return segment.Value; }
return "could not find segment value"; }
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) { throw new NotImplementedException(); } }
} [/code] Что делает приведенное выше: [list] [*]Показывает метку со случайной строкой и индикатором выполнения, показывающим, когда она изменится. . [*]Когда индикатор выполнения достигает конца, метка меняется, но индикатор выполнения [b]НЕ начинается снова[/b].
Что он должен делать:
[*]Показывает метку со случайной строкой и индикатором выполнения, показывающим, когда она будет меняться. [*]Когда индикатор выполнения достигает конца, метка меняется, и начинается индикатор выполнения. с самого начала, чтобы показать, когда метка будет изменена в следующий раз. [/list]