使用C#和XAML的UWP多重倒计时
我正在用C#和XAML构建一个UWP应用程序。在一个自定义用户控件上,我构建了一个“列表”,更好的是一个包含DataTemplate和网格的ListView。 在这个网格中,我有多个带有数据绑定的文本块。在生成的每一行中,我现在需要一个从DateTimeOffset.now到未来特定DateTimeOffset(或过去的负值)的倒计时计时器 我试图用文本属性构建一个类countdown元素,并用该字符串调用我的“countdown”类。在Xaml中,我在文本块中对该字符串进行了数据绑定。 但它没有改变。我想用户界面没有更新?是否有不同的方法-可能只使用XAML构建倒计时,并将其值绑定到ViewModel的生成值 谢谢你的帮助 这是我的倒计时课:使用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中,我在文本块中对该字符串进行了数据绑定。 但它没
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文本。是的,这是真的。但我不知道在哪里执行。这就是我问的原因。