UWP 使用 C# 和 XAML 进行多次倒计时

本文关键字:倒计时 XAML 使用 UWP | 更新日期: 2023-09-27 18:35:02

我正在使用 C# 和 XAML 构建一个 UWP 应用程序。在自定义用户控件上,我构建了一个"列表",最好是带有数据模板和网格的列表视图。在这个网格中,我有多个带有数据绑定的文本块。在每个生成的行中,我现在想要一个倒数计时器,从 DateTimeOffset.Now 到将来的特定 DateTimeOffset (或过去 - 负值(。

我尝试用文本属性构建一个类 CountDownElement,并用该字符串调用我的"countdown"类。在 Xaml 中,我在文本块中对该字符串进行了数据绑定。但它没有改变。我认为 UI 没有更新?是否有其他方法 - 也许仅使用 XAML 构建倒计时并将其值绑定到 ViewModel 的生成值?

感谢您的帮助!

这是我的倒计时课:

 public class Countdown
{
    public DispatcherTimer DispatcherTimer;
    public DateTimeOffset StartTime;
    private TimeSpan _time;
    private string _tb;
    private readonly DateTimeOffset _endTime;
    public Countdown(string tb, DateTimeOffset endTime)
    {
        _tb = tb;
        _endTime = endTime;
        DispatcherTimerSetup();
    }
    private void DispatcherTimerSetup()
    {
        DispatcherTimer = new DispatcherTimer();
        StartTime = DateTimeOffset.Now;
        _time = new TimeSpan();
        _time = _endTime - StartTime;
        DispatcherTimer.Interval = new TimeSpan(0, 0, 0, 1);
        DispatcherTimer.Tick += dispatcherTimer_Tick;
        DispatcherTimer.Start();
    }
    private void dispatcherTimer_Tick(object sender, object e)
    {
        _time = _time.Subtract(new TimeSpan(0, 0, 1));
        if (_time <= TimeSpan.Zero)
        {
            _tb = "- " + _time.ToString(@"dd':hh':mm':ss");
        }
        else
        {
            _tb = "  " + _time.ToString(@"dd':hh':mm':ss");
        }
    }
}

这是我的倒计时元素,我在其中创建了一个新的倒计时对象:

 public class ListCountdownElement
{
    public string CountdownElement;
    public ListCountdownElement(Incident incident, int type)
    {
        CountdownElement = "Countdown";
        switch (type)
        {
            case 1:
                if (incident.Resolvebykpiid != null)
                {
                    var endTime = incident.Resolvebykpiid.Failuretime;
                    if (endTime != null)
                    {
                        var c = new Countdown(CountdownElement, (DateTimeOffset)endTime);
                    }
                }
                break;
            case 2:
                if (incident.Firstresponsebykpiid != null)
                {
                    if (incident.Firstresponsesent != null && incident.Firstresponsesent.Value)
                    {
                        CountdownElement.Foreground = new SolidColorBrush(Colors.Green);
                        CountdownElement.Text = "sent";
                    }
                    else
                    {
                        var endTime = incident.Firstresponsebykpiid.Failuretime;
                        if (endTime != null)
                        {
                            var c = new Countdown(CountdownElement, (DateTimeOffset)endTime);
                        }
                    }
                }
                break;
            case 3:
                if (incident.Resolvebykpiid != null)
                {
                    var endTime = incident.Resolvebykpiid.Failuretime;
                    if (endTime != null)
                    {
                        var c = new Countdown(CountdownElement, (DateTimeOffset)endTime);
                    }
                }
                break;
            default:
                break;
        }
    }
}

这是 IncidentViewModel,我在其中创建倒计时元素 - 这是我绑定到的数据。

 public class IncidentViewModel
{
    public string Id { get; set; }
    public string Title { get; set; }
    public string Customer { get; set; }
    public ListStatusBar StatusBar { get; set; }
    public ListCountdownElement Countdown { get; set; }
    public ListStatusLight Status { get; set; }
    public IncidentViewModel(Incident incident, IncidentTypes type)
    {
        this.Id = incident.Ticketnumber;
        this.Title = incident.Title;
        this.Customer = incident.Customerid_account.Name;
        this.StatusBar = new ListStatusBar(incident, type);
        this.Countdown = new ListCountdownElement(incident, type.GetHashCode());
        this.Status = new ListStatusLight(incident, type);
    }
}

这是ListViewModel,我在其中获取数据并创建事件视图模型并将它们放入可观察的集合中:

public class IncidentListViewModel
{
    public ObservableCollection<Incident> Incidents;
    public ObservableCollection<IncidentViewModel> IncidentsCollection;
    public IncidentListViewModel()
    {
        IncidentsCollection = new ObservableCollection<IncidentViewModel>();
    }
    public async Task<ObservableCollection<IncidentViewModel>> GetData(string accessToken, IncidentTypes type)
    {
        Incidents = await DataLoader.LoadIncidentsData(accessToken, type.GetHashCode());
        IncidentsCollection.Clear();
        foreach (var incident in Incidents)
        {
            var incidentVm = new IncidentViewModel(incident, type);
            IncidentsCollection.Add(incidentVm);
        }
        return IncidentsCollection;
    }
}

这是列表控件的 xaml.cs,我在其中绑定到可观察集合:

public sealed partial class IncidentListControl : UserControl
{
    private readonly IncidentListViewModel _incidentListVm;
    private readonly string _accessToken;
    private readonly IncidentTypes _type;
    public IncidentListControl(string accessToken, IncidentTypes type)
    {
        this.InitializeComponent();
        this._accessToken = accessToken;
        this._type = type;
        this.ListHeader.Text = type.ToString();
        _incidentListVm = new IncidentListViewModel();
        GetIncidentData();
        //Get Incident Data and update UI
        var period = TimeSpan.FromSeconds(60);
        var periodicTimer = ThreadPoolTimer.CreatePeriodicTimer(async (source) =>
        {
            await Dispatcher.RunAsync(CoreDispatcherPriority.High,
                GetIncidentData);
        }, period);
    }
    private async void GetIncidentData()
    {
        await _incidentListVm.GetData(_accessToken, _type);
        this.ListViewIncidents.ItemsSource = _incidentListVm.IncidentsCollection;
    }
}

最后是 xaml,我在文本块上执行绑定:

<ListView Name="ListViewIncidents" Grid.Row="1">
        <ListView.ItemTemplate>
            <DataTemplate>
                <Grid Margin="5">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="150"/>
                        <ColumnDefinition Width="200"/>
                        <ColumnDefinition Width="200"/>
                        <ColumnDefinition Width="100"/>
                        <ColumnDefinition Width="100"/>
                        <ColumnDefinition Width="*"/>
                    </Grid.ColumnDefinitions>
                    <TextBlock Text="{Binding Id}" Margin="3" Grid.Column="0" FontWeight="Bold" FontSize="12" TextWrapping="Wrap" />
                    <TextBlock Text="{Binding Title}" Margin="3" Grid.Column="1" FontWeight="Bold" FontSize="12" TextWrapping="Wrap" />
                    <TextBlock Text="{Binding Customer}" Margin="3" Grid.Column="2" FontWeight="Bold" FontSize="12" TextWrapping="Wrap" />
                    <Border Margin="3" Grid.Column="3" Height="10" BorderThickness="1" BorderBrush="Black">
                        <Rectangle  Fill="{Binding StatusBar.Color}" Width="{Binding StatusBar.Width}" HorizontalAlignment="Left" Height="10"></Rectangle>
                    </Border>
                   <TextBlock Text="{Binding Countdown.CountdownElement}" Margin="3" Grid.Column="4" FontWeight="Bold" FontSize="12" TextWrapping="Wrap" />
                    <Ellipse Margin="3" Height="10" Width="10" Stroke="Black" Fill="{Binding Status.StatusColor}" Grid.Column="5"/>
                </Grid>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>

这只是相关的部分。我希望此更新对您有所帮助!

更新的代码:倒计时.cs

public class Countdown : INotifyPropertyChanged
{
    public DispatcherTimer DispatcherTimer;
    public DateTimeOffset StartTime;
    private TimeSpan _time;
    private string _tb;
    public string Tb
    {
        get { return _tb; }
        set
        {
            if (Equals(value, _tb))
                return;
            _tb = value;
            OnPropertyChanged();
        }
    }
    private readonly DateTimeOffset _endTime;
    public Countdown( DateTimeOffset endTime)
    {
      //  _tb = tb;
        _endTime = endTime;
        DispatcherTimerSetup();
    }
    private void DispatcherTimerSetup()
    {
        DispatcherTimer = new DispatcherTimer();
        StartTime = DateTimeOffset.Now;
        _time = new TimeSpan();
        _time = _endTime - StartTime;
        DispatcherTimer.Interval = new TimeSpan(0, 0, 0, 1);
        DispatcherTimer.Tick += dispatcherTimer_Tick;
        DispatcherTimer.Start();
    }
    private void dispatcherTimer_Tick(object sender, object e)
    {
        _time = _time.Subtract(new TimeSpan(0, 0, 1));
        if (_time <= TimeSpan.Zero)
        {
            Tb = "- " + _time.ToString(@"dd':hh':mm':ss");
        }
        else
        {
            Tb = "  " + _time.ToString(@"dd':hh':mm':ss");
        }
    }
    public event PropertyChangedEventHandler PropertyChanged;
    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

ListCountdownElement.cs

 public class ListCountdownElement 
{
    private Countdown _countDown;
    public string CountdownElement
    {
        get {return _countDown.Tb ; }          
    }

    public ListCountdownElement(Incident incident, int type)
    {
        switch (type)
        {
            case 1:
                if (incident.Resolvebykpiid != null)
                {
                    var endTime = incident.Resolvebykpiid.Failuretime;
                    if (endTime != null)
                    {
                         _countDown = new Countdown( (DateTimeOffset)endTime);
                    }
                }
                break;
            case 2:
                if (incident.Firstresponsebykpiid != null)
                {
                    if (incident.Firstresponsesent != null && incident.Firstresponsesent.Value)
                    {
                        //CountdownElement = "sent"; --> think about that later
                    }
                    else
                    {
                        var endTime = incident.Firstresponsebykpiid.Failuretime;
                        if (endTime != null)
                        {
                            _countDown = new Countdown( (DateTimeOffset)endTime);
                        }
                    }
                }
                break;
            case 3:
                if (incident.Resolvebykpiid != null)
                {
                    var endTime = incident.Resolvebykpiid.Failuretime;
                    if (endTime != null)
                    {
                         _countDown = new Countdown((DateTimeOffset)endTime);
                    }
                }
                break;
            default:
                break;
        }
    }
}

事件视图模型.cs

public class IncidentViewModel
{
    public string Id { get; set; }
    public string Title { get; set; }
    public string Customer { get; set; }
    public ListStatusBar StatusBar { get; set; }
    public ListCountdownElement Countdown { get; set; }
    public ListStatusLight Status { get; set; }
    public IncidentViewModel(Incident incident, IncidentTypes type)
    {
        this.Id = incident.Ticketnumber;
        this.Title = incident.Title;
        this.Customer = incident.Customerid_account.Name;
        this.StatusBar = new ListStatusBar(incident, type);
        this.Countdown = new ListCountdownElement(incident, type.GetHashCode());
        this.Status = new ListStatusLight(incident, type);
    }
}
IncidentListControl.xaml.cs

、IncidentListControl.xaml.cs 和 IncidentListViewModel.cs 保持不变

编辑:最终版本new ListCountdownElement.cs:

public class ListCountdownElement : INotifyPropertyChanged
{
    public DispatcherTimer DispatcherTimer;
    public DateTimeOffset StartTime;
    private TimeSpan _time;
    private DateTimeOffset _endTime;
    private string _countdownElement;
    public string CountdownElement
    {
        get { return _countdownElement; }
        set
        {
            if (Equals(value, _countdownElement))
                return;
            _countdownElement = value;
            OnPropertyChanged();
        }
    }
    public ListCountdownElement(Incident incident, int type)
    {
        switch (type)
        {
            case 1:
                if (incident.Resolvebykpiid != null)
                {
                    _endTime = (DateTimeOffset)incident.Resolvebykpiid.Failuretime;
                    DispatcherTimerSetup();
                }
                break;
            case 2:
                if (incident.Firstresponsebykpiid != null)
                {
                    if (incident.Firstresponsesent != null && incident.Firstresponsesent.Value)
                    {
                        //CountdownElement = "sent"; --> think about that later
                    }
                    else
                    {
                        _endTime = (DateTimeOffset)incident.Firstresponsebykpiid.Failuretime;
                        DispatcherTimerSetup();
                    }
                }
                break;
            case 3:
                if (incident.Resolvebykpiid != null)
                {
                    _endTime = (DateTimeOffset)incident.Resolvebykpiid.Failuretime;
                    DispatcherTimerSetup();
                }
                break;
            default:
                break;
        }
    }
    private void DispatcherTimerSetup()
    {
        DispatcherTimer = new DispatcherTimer();
        StartTime = DateTimeOffset.Now;
        _time = new TimeSpan();
        _time = _endTime - StartTime;
        DispatcherTimer.Interval = new TimeSpan(0, 0, 0, 1);
        DispatcherTimer.Tick += dispatcherTimer_Tick;
        DispatcherTimer.Start();
    }
    private void dispatcherTimer_Tick(object sender, object e)
    {
        _time = _time.Subtract(new TimeSpan(0, 0, 1));
        if (_time <= TimeSpan.Zero)
        {
            CountdownElement = "- " + _time.ToString(@"dd':hh':mm':ss");
        }
        else
        {
            CountdownElement = "  " + _time.ToString(@"dd':hh':mm':ss");
        }
    }
    public event PropertyChangedEventHandler PropertyChanged;
    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

UWP 使用 C# 和 XAML 进行多次倒计时

因此,

您似乎没有触发正确的事件来让 UI 知道文本已更改。您正在显示的代码对于您要完成的任务来说看起来也有点复杂......

首先确保 CountDown 类继承INotifyPropertyChanged 。添加该接口时,还需要向该类添加以下代码:

public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
    var handler = PropertyChanged;
    if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}

之后,将_tb字段更改为实际属性,如下所示:

private string _tb;
public string Tb
{
    get { return _tb; }
    set
    {
        if(Equals(value, _tb))
            return;
        _tb = value;
        OnPropertyChanged();
    }
}

****重要****

在dispatcherTimer_Tick事件中,更改 Tb 的值(属性(,而不是_tb字段。

现在,您需要将该属性路由到 UI,因此在 ListCountdownElement 类中,将 CountdownElement 字段也更改为实属性。

public string CountdownElement
{ get { return _countDown.Tb; } }

唯一要做的就是添加一个 CountDown 类型的_countDown字段,并确保在 ListCountdownElement 构造函数中分配该字段。

我想这应该可以解决它...(虽然:)未测试(