定时器/时钟在Avalonia(非常类似于wpf) MainWindowViewModel/MainWindow,如何更



如果我做错了,我很抱歉,我已经20年没有接触过c#了,我是MVVM的新手。无论如何,我正在使用Avalonia(据我所知,它与WPF几乎相同,但跨平台),我在UI中显示时钟时遇到了一些麻烦。我有以下代码在我的MainWindow.axaml.

<Border Classes="Footer" Grid.Row="2" Grid.Column="0">
<DockPanel HorizontalAlignment="Stretch">
<TextBlock Text="{Binding NextDownloadCycle}"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<TextBlock Text="{Binding CurrentTime}"/>                       
</StackPanel>
</DockPanel>
</Border>

在我的MainWindowViewModel文件中,我有以下代码,应该使此工作。

public event PropertyChangedEventHandler? PropertyChanged;
public string CurrentTime {
get { return _currentTime; } 
set
{
_currentTime = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CurrentTime)));
Debug.WriteLine(value);
}
}
private string _nextDownloadCycle = "Next crawl session at: (in 30m)";
public string NextDownloadCycle {
get { return _nextDownloadCycle; }
set 
{
_nextDownloadCycle = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(NextDownloadCycle)));
Debug.WriteLine(value);
}
}
private DateTime _nextCycle = DateTime.Now.AddHours(1);
public MainWindowViewModel()
{
StartClock();
}
private void StartClock()
{
new DispatcherTimer(TimeSpan.FromSeconds(1), DispatcherPriority.Normal, TimerTick).Start();            
}
void TimerTick(object sender, EventArgs e)
{
var now = DateTime.Now;
if (now >= _nextCycle)
{
_nextCycle = DateTime.Now.AddHours(1);
StartCrawling();
}
TimeSpan ts = _nextCycle - now;
NextDownloadCycle = String.Format(
"Next crawl session at: {0} (in {1}m{2}s)",
_nextCycle.ToString("HH:mm:ss"),
Math.Round(ts.TotalMinutes),
ts.Seconds
);
CurrentTime = now.ToString("HH:mm:ss");
}

我试着把PropertyChanged?调用内部的Dispatcher.UIThread.InvokeAsync(() => )思维,可能会以某种方式修复它(我是新的,你可能会告诉)无济于事。我在调试控制台中得到的输出大部分是我所期望的,例如

Next crawl session at: 00:22:03 (in 60m32s)
23:22:31

有时事情会有点不对劲,我得到两次提到一秒,然后是2秒的间隔,就像下面这样,我猜这只是由于四舍五入的缘故。

Next crawl session at: 00:47:00 (in 60m48s)
23:47:11
Next crawl session at: 00:47:00 (in 60m48s)
23:47:12
Next crawl session at: 00:47:00 (in 60m46s)
无论如何,我的主要问题是为什么不更新UI?我需要改变什么,我做错了什么?

最终使其工作如下。首先,我在模型文件夹中创建了一个单独的Clock类,它扩展了INotifyPropertyChanged,并将格式和计时器逻辑移到了其中。

public class Clock : INotifyPropertyChanged
{
private DispatcherTimer _disTimer = new DispatcherTimer();
public event PropertyChangedEventHandler? PropertyChanged;
public EventHandler<DateTime> OnEveryHour = (s , e) => { };
public string CurrentTime { get; set; } = "Current Time";
public string NextDownloadCycle { get; set; } = "Downloading in: ";
private DateTime _nextCycle = DateTime.Now.AddHours(1);
public Clock()
{
_disTimer.Interval = TimeSpan.FromSeconds(1);
_disTimer.Tick += DispatcherTimer_Tick;
_disTimer.Start();
}
private void DispatcherTimer_Tick(object? sender, EventArgs e)
{
var now = DateTime.Now;
if (now >= _nextCycle)
{
_nextCycle = DateTime.Now.AddHours(1);
OnEveryHour(this, now);
}
TimeSpan ts = _nextCycle - now;
NextDownloadCycle = String.Format(
"Next crawl session at {0}(in {1}m{2}s)",
_nextCycle.ToString("HH:mm:ss"),
Math.Round(ts.TotalMinutes) - 1,
ts.Seconds
);
CurrentTime = now.ToString("HH:mm:ss");
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CurrentTime)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(NextDownloadCycle)));
}
}

然后在MainWindowViewModel我创建我的时钟实例public Clock Clock { get; set; } = new Clock();

在xaml中调用它,如下所示:

<Border Classes="Footer" Grid.Row="2" Grid.Column="0">
<DockPanel HorizontalAlignment="Stretch" DataContext="{Binding Clock}">
<TextBlock Text="{Binding NextDownloadCycle}"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<TextBlock Text="{Binding CurrentTime}"/>                   
</StackPanel>
</DockPanel>
</Border>

我将简单地订阅MainWindowViewModel中的OnEveryHour来执行StartCrawling(),一切都应该像我想要的那样工作。(额外的好处,代码看起来[c]精简)

相关内容

  • 没有找到相关文章

最新更新