我正在做一个定时器应用程序.当您点击主页的"开始"按钮时,它会导航到另一个显示经过时间的页面.

我的问题是,在Android上加载视图需要时间(可能是因为我处于调试模式,而我使用的设备相对较旧),并且Time Count活动(在ViewModel中)在View实际启动前几秒启动.

从View Model开始的时间计数将由View启动,如下所示:

((TimerSessionViewModel)BindingContext).BeginSession();

在此之后,ViewModel执行的操作基本上是使用一种简单的同步机制来计算已用时间,如下面的代码片段所示:

_timer = new Timer(TimerCallback, null, TimeSpan.Zero, TimeSpan.FromMilliseconds(TimerToolkitConstants.TimerTickMilliseconds));

public void BeginSession()
{
    SessionIsStarted = true;
    SessionStarted?.Invoke(this, new EventArgs());
    OnSessionStart();
}


private void TimerCallback(object? state)
{
    if (SessionIsStarted && !TimerPaused)
    {
        SessionElapsedTime = SessionElapsedTime.Add(TimeSpan.FromMilliseconds(TimerToolkitConstants.TimerTickMilliseconds));
    }
}

-> So the whole idea is to have the View call "BeginSession" when it is loaded and ready to show the time count.

现在我遇到的问题是,我try 过的View提供的所有事件(已加载、出现、LayoutChanged...)都在View实际出现在我的手机上之前引发.现在我相信这很大程度上是由于上下文(调试模式,相对较旧的设备),但它将是理想的,能够有一些工作良好,即使在那个竞争,这样我就可以知道应用程序运行良好,即使在恶劣的条件.

我不确定是否有可能比我在这里做得更好,但我必须征求意见,以防有什么我遗漏了什么.感谢您在这方面的任何意见.

推荐答案

对于您的时间关键型操作,我想建议几个优化以供试验.第一个是避免不得不导航到新的视图.使用On Page体系 struct ,其中视图重叠在公共网格上,并且可见性由值OnePageState控制.这应该会让您非常迅速地从主页视图切换到"看起来"导航的页面,但实际上并不是.

switching virtual views

第二种方法是基于Stopwatch进行计时,并使用异步Task.Delay调用来更新可见显示.即使切换视图需要几十毫秒,所用时间也是从调用启动命令的那一刻开始的,因此它仍然被考虑在内.你要求的是完美的同步,重要的是要注意,它不会达到用核同位素测量时间的原子精度,但它真的不坏.一百零二


首先创建一个IValueConverter类,如果两个enum值相等,则返回true.在这里,如果绑定值OnePageState不是OnePageState.Main,则默认MAUI页面现在变为不可见.同样,当值变为OnePageState.Timer时,替代虚拟页面变为可见.


IValueConverter


    class EnumToBoolConverter : IValueConverter
    {
        public object Convert(object unk, Type targetType, object parameter, CultureInfo culture)
        {
            if (unk is Enum enum1 && parameter is Enum @enum2)
            {
                return enum1.Equals(@enum2);
            }
            return false;
        }
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) =>
            throw new NotImplementedException();
    }

Timer Layout Overlaps Maui Default

将Timer视图添加到公共网格中的Maui默认视图之上.

<!--1 x 1 Grid to contain overlapping virtual page representations-->
<Grid x:Name="OnePageGrid">
    <!--Maui Default Virtual Page-->
    <ScrollView
        IsVisible="{Binding
            OnePageState, 
            Converter={StaticResource EnumToBoolConverter}, 
            ConverterParameter={x:Static local:OnePageState.Main}}">
        <VerticalStackLayout
            Spacing="25"
            Padding="30,0"
            VerticalOptions="Center">
            .
            .
            .            
    </ScrollView>

接下来,在Maui默认视图之上,我们为MainPage.Content堆叠了一个完全不同的"外观".

   <!--Timer Virtual Page-->
   <Grid
       IsVisible="{Binding
           OnePageState, 
           Converter={StaticResource EnumToBoolConverter}, 
           ConverterParameter={x:Static local:OnePageState.Timer}}"
       RowDefinitions="50,*,50"
       BackgroundColor="MidnightBlue">
       <HorizontalStackLayout
           HorizontalOptions="Fill"
           BackgroundColor="White">
           <Button
               Text ="&lt;--- Main Page" 
               TextColor="CornflowerBlue"
               BackgroundColor="White"
               FontSize="Medium"
               HorizontalOptions="Start"
               Command="{Binding SetOnePageStateCommand}"
               CommandParameter="{x:Static local:OnePageState.Main}"/>
       </HorizontalStackLayout>
       <Label
           Grid.Row="1"
           Text="{Binding TimerDisplay}"
           TextColor="White"
           FontSize="48"
           FontAttributes="Bold"
           HorizontalOptions="Center"
           VerticalOptions="Center"
           HorizontalTextAlignment="Center"
           VerticalTextAlignment="Center"
           BackgroundColor="Transparent">
           <Label.GestureRecognizers>
               <TapGestureRecognizer Command="{Binding StartTimerCommand}"/>
           </Label.GestureRecognizers>
       </Label>
       <Grid
           Grid.Row="2"
           BackgroundColor="White">
           <Button
               Text ="Reset" 
               TextColor="CornflowerBlue"
               BackgroundColor="White"
               FontSize="Medium"
               HorizontalOptions="CenterAndExpand"
               Command="{Binding ResetTimerCommand}"/>
       </Grid>
   </Grid>
</Grid>

Suggestion: Avoid using a Timer

这里有一种方法可以简化更新所用时间,而不使用Timer个实例和由此产生的负担.



Task _pollingTask;
private Stopwatch _stopwatch = new Stopwatch();
private async void OnStartTimer(object o)
{
    if (!_stopwatch.IsRunning)
    {
        OnePageState = OnePageState.Timer;
        try
        {
            if (_cts != null)
            {
                _cts.Cancel();
            }
            _cts = new CancellationTokenSource();
            var token = _cts.Token;
            _stopwatch.Restart();
            while (!token.IsCancellationRequested)
            {
                var elapsed = _stopwatch.Elapsed;
                MainThread.BeginInvokeOnMainThread(() =>
                {
                    TimerDisplay =
                        elapsed < TimeSpan.FromSeconds(1) ?
                        elapsed.ToString(@"hh\:mm\:ss\.ff") :
                        elapsed.ToString(@"hh\:mm\:ss");
                });
                await Task.Delay(TimeSpan.FromSeconds(0.1), token);
            }
        }
        catch { }
        finally
        {
            _stopwatch.Stop();
            _pollingTask?.Dispose();
        }
    }
}

Csharp相关问答推荐

使用ElasticsearchClient设置忽略属性.默认MappingFor<>

图形.DrawString奇怪异常的字距调整

CsWin32如何创建PWSTR的实例,例如GetWindowText

Blazor:类型或命名空间名称Components在命名空间中不存在''

LINQ无法翻译SQLFunctions方法

使用LINQ to XML获取元素值列表是不起作用的

Microsoft. VisualBasic. FileIO. FileSystem. MoveFile()对话框有错误?

返回TyedResults.BadRequest<;字符串>;时问题详细信息不起作用

将现有字段映射到EFCore中的复杂类型

在EF Core中,有没有什么方法可以防止在查询中写入相同的条件,而代之以表达式?

TeamsBot SendActivityActivityTypes与ActivityTypes同步.键入不再起作用

我的MRG32k3a算法实现返回的数字超出了预期的[0,1]范围

C#动态设置ServerReport报表参数

用C#从XML内部元素中获取特定值

如何在microsoft.clearscript.v8的jsondata中使用Linq

c#在后台实现类型化数组

为什么我可以在注册表编辑器中更改值,但不能在以管理员身份运行的C#表单应用程序中更改?

Xamarin Forms应用程序中登录页面的用户名和密码编辑文本之间不需要的空格

将两个JSON文件与覆盖值的主文件合并

使用';UnityEngineering.Random.Range()';的IF语句仅适用于极高的最大值