使用C#和XAML的UWP多重倒计时

使用C#和XAML的UWP多重倒计时,c#,xaml,data-binding,win-universal-app,countdown,C#,Xaml,Data Binding,Win Universal App,Countdown,我正在用C#和XAML构建一个UWP应用程序。在一个自定义用户控件上,我构建了一个“列表”,更好的是一个包含DataTemplate和网格的ListView。 在这个网格中,我有多个带有数据绑定的文本块。在生成的每一行中,我现在需要一个从DateTimeOffset.now到未来特定DateTimeOffset(或过去的负值)的倒计时计时器 我试图用文本属性构建一个类countdown元素,并用该字符串调用我的“countdown”类。在Xaml中,我在文本块中对该字符串进行了数据绑定。 但它没

我正在用C#和XAML构建一个UWP应用程序。在一个自定义用户控件上,我构建了一个“列表”,更好的是一个包含DataTemplate和网格的ListView。 在这个网格中,我有多个带有数据绑定的文本块。在生成的每一行中,我现在需要一个从DateTimeOffset.now到未来特定DateTimeOffset(或过去的负值)的倒计时计时器

我试图用文本属性构建一个类countdown元素,并用该字符串调用我的“countdown”类。在Xaml中,我在文本块中对该字符串进行了数据绑定。 但它没有改变。我想用户界面没有更新?是否有不同的方法-可能只使用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");
        }
    }
}
这是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,在这里我获取数据并创建incidentViewModels,并将它们放入可观察的集合:

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;
    }
}
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>
IncidentViewModel.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未被触及

编辑:最终版本 新建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));
    }
}
公共类ListCountdownElement:INotifyPropertyChanged
{
公共调度员;
公共日期时间偏移开始时间;
私人时间间隔时间;
专用日期时间偏移量_endTime;
私有字符串_countdown元素;
公共字符串倒计时元素
{
获取{return\u countdownElement;}
设置
{
if(等于(值,_countdown元素))
返回;
_倒计时元素=值;
OnPropertyChanged();
}
}
公共列表倒计时元素(事件,int类型)
{
开关(类型)
{
案例1:
if(incident.Resolvebykpiid!=null)
{
_endTime=(DateTimeOffset)incident.Resolvebykpiid.Failuretime;
DispatcherTimerSetup();
}
打破
案例2:
if(incident.Firstresponsebykpiid!=null)
{
if(incident.Firstresponsesent!=null&&incident.Firstresponsesent.Value)
{
//CountdownElement=“sent”;-->稍后再考虑
}
其他的
{
_endTime=(DateTimeOffset)incident.FirstResponseByKPID.Failuretime;
DispatcherTimerSetup();
}
}
打破
案例3:
if(incident.Resolvebykpiid!=null)
{
_endTime=(DateTimeOffset)incident.Resolvebykpiid.Failuretime;
DispatcherTimerSetup();
}
打破
违约:
打破
}
}
私有void DispatcherTimerSetup()
{
Dispatchermer=新Dispatchermer();
StartTime=DateTimeOffset.Now;
_时间=新的时间跨度();
_时间=_endTime-StartTime;
Dispatchermer.Interval=新的时间跨度(0,0,0,1);
Dispatchermer.Tick+=Dispatchermer\u Tick;
dispatchermer.Start();
}
私有无效Dispatcher_Tick(对象发送方,对象e)
{
_时间=_time.Subtract(新的时间跨度(0,0,1));

如果(_time那么您似乎没有触发正确的事件来让ui知道文本已更改。 您正在展示的代码看起来也有点复杂,因为您正试图完成

首先确保倒计时类继承了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();
    }
}
****重要的****

在Dispatchermer_Tick事件中,更改Tb(属性)的值,而不是字段的值

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

public string CountdownElement
{ get { return _countDown.Tb; } }
唯一要做的是,添加一个类型为countDown的_countDown字段,并确保在ListCountdownElement构造函数中指定该字段


我想这应该会解决它…(虽然没有测试:)

因此,您似乎没有触发正确的事件来让ui知道文本已更改。 您正在展示的代码看起来也有点复杂,因为您正试图完成

首先确保倒计时类继承了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();
    }
}
****重要的****

在Dispatchermer_Tick事件中,更改Tb(属性)的值,而不是字段的值

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

public string CountdownElement
{ get { return _countDown.Tb; } }
唯一要做的是,添加一个类型为countDown的_countDown字段,并确保在ListCountdownElement构造函数中指定该字段


我想应该可以解决这个问题了……(不过没有经过测试:))

我们在您的代码中看到的唯一一件事是,字段_tb正在倒计时类中更新,没有其他操作。那么,您能告诉我们如何将其绑定到您的UI吗?好的,我用所有相关代码更新了我的帖子,但仍然看不到UI可以更新的地方。如果使用“绑定”,您应该告诉UIata已更改,这通常是通过NotifyPropertyChanged事件完成的。您唯一要做的就是更改tb文本。是的,这是真的。但我不知道在哪里执行。这就是我问的原因。