feat: 增加晚点未定功能

This commit is contained in:
denglihong2007
2026-03-28 22:00:33 +08:00
parent fbbf75413f
commit 2044c19f24
11 changed files with 254 additions and 61 deletions

View File

@@ -0,0 +1,14 @@
namespace CRSim.Core.Models
{
public static class TrainStatus
{
// 00:00:01 is reserved for "delay unknown"; normal input/export paths only use minute precision.
public static readonly TimeSpan DelayUnknown = TimeSpan.FromSeconds(1);
public static bool IsDelayUnknown(TimeSpan? status) =>
status.HasValue && status.Value == DelayUnknown;
public static TimeSpan GetScheduleOffset(TimeSpan? status) =>
IsDelayUnknown(status) ? TimeSpan.Zero : status ?? TimeSpan.Zero;
}
}

View File

@@ -38,6 +38,7 @@ namespace CRSim.ScreenSimulator.Converters
{
if (values[0] is DateTime arriveTime){
if (values.Length > 1 && values[1] is TimeSpan state){
if (TrainStatus.IsDelayUnknown(state)) return StopCheckInColor;
if (state.TotalMinutes > 0) return ArrivingLateText;
if (state.TotalMinutes < 0) return ArrivingEarlyText;
return now >= arriveTime ? ArrivedText : ArrivingText;
@@ -51,6 +52,10 @@ namespace CRSim.ScreenSimulator.Converters
if (values[2] is null)
return StopCheckInColor;
// 晚点未定
if (values[2] is TimeSpan unknownState && TrainStatus.IsDelayUnknown(unknownState))
return StopCheckInColor;
// 正点/晚点判断
if (values[0] != null && values[1] == null && values[2] is TimeSpan status)
return GetStatusColor(status);

View File

@@ -21,6 +21,7 @@ namespace CRSim.ScreenSimulator.Converters
public string CheckInText { get; set; } = "正在检票";
public string StopCheckInText { get; set; } = "停止检票";
public string SuspendText { get; set; } = "列车停运";
public string DelayUnknownText { get; set; } = "晚点未定";
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
_settings ??= StyleManager.ServiceProvider
@@ -39,6 +40,8 @@ namespace CRSim.ScreenSimulator.Converters
{
if (values[2] is TimeSpan state)
{
if (TrainStatus.IsDelayUnknown(state))
return DelayUnknownText;
// arriveTime += TimeSpan.FromMinutes(state.TotalMinutes);
if (now >= arriveTime)
{
@@ -67,6 +70,10 @@ namespace CRSim.ScreenSimulator.Converters
if (values[2] is null)
return SuspendText;
// 晚点未定
if (values[2] is TimeSpan unknownState && TrainStatus.IsDelayUnknown(unknownState))
return DelayUnknownText;
// 有出发时间
if (values[1] is DateTime departureTime && departureTime != DateTime.MinValue)
{
@@ -114,4 +121,4 @@ namespace CRSim.ScreenSimulator.Converters
return null;
}
}
}
}

View File

@@ -1,4 +1,6 @@
namespace CRSim.ScreenSimulator.Models
using CRSim.Core.Models;
namespace CRSim.ScreenSimulator.Models
{
public class TrainInfo
{
@@ -9,11 +11,11 @@
private DateTime? departureTime { get; set; }
public DateTime? ArrivalTime
{
get
get
{
return State is null ? arrivalTime : arrivalTime + State;
}
set
return State is null || TrainStatus.IsDelayUnknown(State) ? arrivalTime : arrivalTime + State;
}
set
{
arrivalTime = value;
}
@@ -22,7 +24,7 @@
{
get
{
return State is null || State < TimeSpan.Zero ? departureTime : departureTime + State;
return State is null || State < TimeSpan.Zero || TrainStatus.IsDelayUnknown(State) ? departureTime : departureTime + State;
}
set
{

View File

@@ -196,22 +196,27 @@ namespace CRSim.ScreenSimulator.ViewModels
{
var now = TimeService.GetDateTimeNow();
var today = now.Date;
var scheduleOffset = TrainStatus.GetScheduleOffset(trainNumber.Status);
if (_settings.LoadTodayOnly && today.Add((trainNumber.DepartureTime ?? trainNumber.ArrivalTime)!.Value).Add(trainNumber.Status.Value) < now)
if (_settings.LoadTodayOnly && today.Add((trainNumber.DepartureTime ?? trainNumber.ArrivalTime)!.Value).Add(scheduleOffset) < now)
{
continue;
}
DateTime? AdjustTime(TimeSpan? time, TimeSpan? status) =>
time.HasValue ? (today.Add(time.Value).Add(status.Value) > now ? today.Add(time.Value) : today.Add(time.Value).AddDays(1)) : null;
DateTime? AdjustTime(TimeSpan? time, TimeSpan status) =>
time.HasValue ? (today.Add(time.Value).Add(status) > now ? today.Add(time.Value) : today.Add(time.Value).AddDays(1)) : null;
var departureOffset = trainNumber.Status is TimeSpan knownStatus && knownStatus > TimeSpan.Zero && !TrainStatus.IsDelayUnknown(knownStatus)
? knownStatus
: TimeSpan.Zero;
TrainInfo.Add(new TrainInfo
{
TrainNumber = trainNumber.Number,
Terminal = trainNumber.Terminal,
Origin = trainNumber.Origin,
ArrivalTime = AdjustTime(trainNumber.ArrivalTime, trainNumber.Status),
DepartureTime = trainNumber.Status > TimeSpan.Zero ? AdjustTime(trainNumber.DepartureTime, trainNumber.Status) : AdjustTime(trainNumber.DepartureTime, TimeSpan.Zero),
ArrivalTime = AdjustTime(trainNumber.ArrivalTime, scheduleOffset),
DepartureTime = AdjustTime(trainNumber.DepartureTime, departureOffset),
TicketChecks = trainNumber.TicketCheckIds is null ? [] : [.. station.WaitingAreas
.SelectMany(w => w.TicketChecks)
.Where(tc => trainNumber.TicketCheckIds.Contains(tc.Id))
@@ -232,4 +237,4 @@ namespace CRSim.ScreenSimulator.ViewModels
DataLoaded.SetResult(true);
}
}
}
}

View File

@@ -53,12 +53,13 @@ namespace CRSim.ScreenSimulator.ViewModels
{
var now = TimeService.GetDateTimeNow();
var today = now.Date;
if (_settings.LoadTodayOnly && today.Add((trainNumber.DepartureTime ?? trainNumber.ArrivalTime)!.Value).Add(trainNumber.Status.Value) < now)
var scheduleOffset = TrainStatus.GetScheduleOffset(trainNumber.Status);
if (_settings.LoadTodayOnly && today.Add((trainNumber.DepartureTime ?? trainNumber.ArrivalTime)!.Value).Add(scheduleOffset) < now)
{
continue;
}
DateTime? AdjustTime(TimeSpan? time) =>
time.HasValue ? (today.Add(time.Value).Add(trainNumber.Status.Value) > now ? today.Add(time.Value) : today.Add(time.Value).AddDays(1)) : null;
time.HasValue ? (today.Add(time.Value).Add(scheduleOffset) > now ? today.Add(time.Value) : today.Add(time.Value).AddDays(1)) : null;
TrainInfos.Add(new TrainInfo
{

View File

@@ -1,4 +1,5 @@
using Microsoft.UI.Xaml.Data;
using CRSim.Core.Models;
using Microsoft.UI.Xaml.Data;
using System.Globalization;
namespace CRSim.Converters
@@ -10,10 +11,15 @@ namespace CRSim.Converters
public string Culture { get; set; } = "zh-CN";
public string NullString { get; set; } = string.Empty;
public string ZeroString { get; set; } = string.Empty;
public string UnknownString { get; set; } = string.Empty;
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is TimeSpan timeSpan)
{
if (TrainStatus.IsDelayUnknown(timeSpan))
{
return UnknownString;
}
if (timeSpan == TimeSpan.Zero)
{
return ZeroString;

View File

@@ -597,6 +597,7 @@ public partial class StationManagementPageViewModel : ObservableObject
Status = worksheet.Cells[row, 10].Text switch
{
"停运" => null,
"晚点未定" => TrainStatus.DelayUnknown,
"正点" => TimeSpan.Zero,
var t when t.StartsWith('-') && TimeSpan.TryParseExact(t[1..], @"hh\:mm", null, out var ts)
=> -ts,
@@ -645,6 +646,7 @@ public partial class StationManagementPageViewModel : ObservableObject
worksheet.Cells[i + 2, 10].Value = TrainStops[i].Status switch
{
null => "停运",
var ts when TrainStatus.IsDelayUnknown(ts) => "晚点未定",
var ts when ts == TimeSpan.Zero => "正点",
var ts when ts < TimeSpan.Zero => $"-{ts.Value.Duration():hh\\:mm}",
var ts => ts.Value.ToString(@"hh\:mm")
@@ -855,4 +857,4 @@ public partial class StationManagementPageViewModel : ObservableObject
{
OnPropertyChanged(nameof(FilteredStationNames));
}
}
}

View File

@@ -10,24 +10,29 @@
xmlns:selectors="using:CRSim.Selectors"
mc:Ignorable="d">
<Page.Resources>
<DataTemplate x:Key="WaitingAreaTemplate" x:DataType="coremodels:WaitingArea">
<TreeViewItem Content="{x:Bind Name}" ItemsSource="{x:Bind TicketChecks}" />
<DataTemplate x:Key="WaitingAreaTemplate"
x:DataType="coremodels:WaitingArea">
<TreeViewItem Content="{x:Bind Name}"
ItemsSource="{x:Bind TicketChecks}"/>
</DataTemplate>
<DataTemplate x:Key="TicketCheckTemplate" x:DataType="coremodels:TicketCheck">
<TextBlock Text="{x:Bind Name}" />
<DataTemplate x:Key="TicketCheckTemplate"
x:DataType="coremodels:TicketCheck">
<TextBlock Text="{x:Bind Name}"/>
</DataTemplate>
<selectors:TreeViewTemplateSelector
x:Key="TreeViewTemplateSelector"
WaitingAreaTemplate="{StaticResource WaitingAreaTemplate}"
TicketCheckTemplate="{StaticResource TicketCheckTemplate}" />
TicketCheckTemplate="{StaticResource TicketCheckTemplate}"/>
</Page.Resources>
<Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<Grid VerticalAlignment="Stretch"
HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Vertical">
<Grid ColumnSpacing="24" RowSpacing="16">
<Grid ColumnSpacing="24"
RowSpacing="16">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
@@ -38,47 +43,136 @@
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Grid.Column="0" x:Name="NumberTextBox" Header="车次" Width="96" TextChanged="Validate"/>
<TextBox Grid.Row="0" Grid.Column="1" x:Name="LengthTextBox" Header="长度" TextChanged="Validate" Text="16"/>
<ComboBox Grid.Row="0" Grid.Column="2" x:Name="PlatformComboBox" Header="站台" Width="96" SelectedIndex="0" ItemsSource="{x:Bind Platforms}"/>
<ComboBox Grid.Row="1" Grid.Column="0" x:Name="LandmarkComboBox" Header="地标" Width="96" SelectedIndex="0" ItemsSource="{x:Bind Landmarks}"/>
<TextBox Grid.Row="1" Grid.Column="1" x:Name="DepartureTextBox" Header="始发站" Width="96" TextChanged="Validate"/>
<TextBox Grid.Row="1" Grid.Column="2" x:Name="ArrivalTextBox" Header="终到站" Width="96" TextChanged="Validate"/>
<TextBox Grid.Row="2" Grid.Column="0" x:Name="StatusTextBox" Header="正晚点" Width="96" Text="0" Height="60" VerticalAlignment="Top" TextChanged="Validate"/>
<ToggleSwitch Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2" x:Name="SuspendToggleSwitch" Header="停运" IsOn="False" Width="96" />
<TextBox Grid.Row="0"
Grid.Column="0"
x:Name="NumberTextBox"
Header="车次"
Width="96"
TextChanged="Validate"/>
<TextBox Grid.Row="0"
Grid.Column="1"
x:Name="LengthTextBox"
Header="长度"
TextChanged="Validate"
Text="16"/>
<ComboBox Grid.Row="0"
Grid.Column="2"
x:Name="PlatformComboBox"
Header="站台"
Width="96"
SelectedIndex="0"
ItemsSource="{x:Bind Platforms}"/>
<ComboBox Grid.Row="1"
Grid.Column="0"
x:Name="LandmarkComboBox"
Header="地标"
Width="96"
SelectedIndex="0"
ItemsSource="{x:Bind Landmarks}"/>
<TextBox Grid.Row="1"
Grid.Column="1"
x:Name="DepartureTextBox"
Header="始发站"
Width="96"
TextChanged="Validate"/>
<TextBox Grid.Row="1"
Grid.Column="2"
x:Name="ArrivalTextBox"
Header="终到站"
Width="96"
TextChanged="Validate"/>
<ComboBox Grid.Row="2"
Grid.Column="0"
x:Name="StatusComboBox"
Header="状态"
Width="96"
SelectionChanged="StatusComboBox_SelectionChanged">
<x:String>正点</x:String>
<x:String>早点</x:String>
<x:String>晚点</x:String>
<x:String>停运</x:String>
<x:String>晚点未定</x:String>
</ComboBox>
<TextBox Grid.Row="2"
Grid.Column="1"
x:Name="StatusMinutesTextBox"
Header="分钟"
Width="96"
Text="0"
TextChanged="Validate"/>
</Grid>
<RadioButtons x:Name="StationKindPanelRadioButtons" SelectedIndex="1" MaxColumns="3"
SelectionChanged="StationKindPanelRadioButtons_SelectionChanged" Header="站点类型" Margin="0,16,0,0">
<RadioButtons x:Name="StationKindPanelRadioButtons"
SelectedIndex="1"
MaxColumns="3"
SelectionChanged="StationKindPanelRadioButtons_SelectionChanged"
Header="站点类型"
Margin="0,16,0,0">
<x:String>始发站</x:String>
<x:String>中间站</x:String>
<x:String>终到站</x:String>
</RadioButtons>
<TextBlock Text="停站时间" Margin="0,16,0,0"/>
<StackPanel Orientation="Horizontal" Margin="0,12,0,0">
<TextBlock Margin="0,0,8,0" Text="由" VerticalAlignment="Center"/>
<TextBox TextChanged="StartHour_TextChanged" x:Name="StartHour" Margin="0,0,8,0" Width="70" />
<TextBlock Margin="0,0,8,0" Text="时" VerticalAlignment="Center"/>
<TextBox TextChanged="StartMinute_TextChanged" x:Name="StartMinute" Margin="0,0,8,0" Width="70" />
<TextBlock Text="分" VerticalAlignment="Center"/>
<TextBlock Text="停站时间"
Margin="0,16,0,0"/>
<StackPanel Orientation="Horizontal"
Margin="0,12,0,0">
<TextBlock Margin="0,0,8,0"
Text="由"
VerticalAlignment="Center"/>
<TextBox TextChanged="StartHour_TextChanged"
x:Name="StartHour"
Margin="0,0,8,0"
Width="70"/>
<TextBlock Margin="0,0,8,0"
Text="时"
VerticalAlignment="Center"/>
<TextBox TextChanged="StartMinute_TextChanged"
x:Name="StartMinute"
Margin="0,0,8,0"
Width="70"/>
<TextBlock Text="分"
VerticalAlignment="Center"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,8,0,0">
<TextBlock Margin="0,0,8,0" Text="到" VerticalAlignment="Center"/>
<TextBox TextChanged="EndHour_TextChanged" x:Name="EndHour" Margin="0,0,8,0" Width="70" />
<TextBlock Margin="0,0,8,0" Text="时" VerticalAlignment="Center"/>
<TextBox TextChanged="Validate" x:Name="EndMinute" Margin="0,0,8,0" Width="70" />
<TextBlock Text="分" VerticalAlignment="Center"/>
<StackPanel Orientation="Horizontal"
Margin="0,8,0,0">
<TextBlock Margin="0,0,8,0"
Text="到"
VerticalAlignment="Center"/>
<TextBox TextChanged="EndHour_TextChanged"
x:Name="EndHour"
Margin="0,0,8,0"
Width="70"/>
<TextBlock Margin="0,0,8,0"
Text="时"
VerticalAlignment="Center"/>
<TextBox TextChanged="Validate"
x:Name="EndMinute"
Margin="0,0,8,0"
Width="70"/>
<TextBlock Text="分"
VerticalAlignment="Center"/>
</StackPanel>
</StackPanel>
<Grid Grid.Column="1" VerticalAlignment="Stretch">
<Grid Grid.Column="1"
VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Text="检票口" Margin="0,0,0,8" Grid.Row="0"/>
<Border Style="{StaticResource ListViewBorder}" VerticalAlignment="Stretch" Grid.Row="1">
<ScrollViewer VerticalScrollBarVisibility="Visible" MaxHeight="383">
<TreeView SelectionMode="Multiple" Name="WaitingAreasList" Width="120" SelectionChanged="Validate" Loaded="WaitingAreasList_Loaded"
ItemsSource="{x:Bind WaitingAreas, Mode=OneWay}" ItemTemplateSelector="{StaticResource TreeViewTemplateSelector}" />
<TextBlock Text="检票口"
Margin="0,0,0,8"
Grid.Row="0"/>
<Border Style="{StaticResource ListViewBorder}"
VerticalAlignment="Stretch"
Grid.Row="1">
<ScrollViewer VerticalScrollBarVisibility="Visible"
MaxHeight="383">
<TreeView SelectionMode="Multiple"
Name="WaitingAreasList"
Width="120"
SelectionChanged="Validate"
Loaded="WaitingAreasList_Loaded"
ItemsSource="{x:Bind WaitingAreas, Mode=OneWay}"
ItemTemplateSelector="{StaticResource TreeViewTemplateSelector}"/>
</ScrollViewer>
</Border>
</Grid>

View File

@@ -1,5 +1,3 @@
using System.Text.RegularExpressions;
namespace CRSim.Views.DialogContents;
public sealed partial class TrainStopDialog : Page
{
@@ -24,6 +22,8 @@ public sealed partial class TrainStopDialog : Page
}
InitializeComponent();
_onValidityChanged = onValidityChanged;
StatusComboBox.SelectedIndex = 0;
UpdateStatusInputState();
Validate(this, EventArgs.Empty);
}
public TrainStopDialog(List<WaitingArea> waitingAreas, List<string> platforms, TrainStop trainStop, Action<bool> onValidityChanged)
@@ -63,14 +63,35 @@ public sealed partial class TrainStopDialog : Page
EndMinute.IsEnabled = false;
StationKindPanelRadioButtons.SelectedIndex = 2;
}
if (trainStop.Status is null)
{
SuspendToggleSwitch.IsOn = true;
StatusComboBox.SelectedItem = "停运";
StatusMinutesTextBox.Text = "0";
}
else if (TrainStatus.IsDelayUnknown(trainStop.Status))
{
StatusComboBox.SelectedItem = "晚点未定";
StatusMinutesTextBox.Text = "0";
}
else if (trainStop.Status.Value > TimeSpan.Zero)
{
StatusComboBox.SelectedItem = "晚点";
StatusMinutesTextBox.Text = Math.Abs((int)Math.Round(trainStop.Status.Value.TotalMinutes)).ToString();
}
else if (trainStop.Status.Value < TimeSpan.Zero)
{
StatusComboBox.SelectedItem = "早点";
StatusMinutesTextBox.Text = Math.Abs((int)Math.Round(trainStop.Status.Value.TotalMinutes)).ToString();
}
else
{
StatusTextBox.Text = trainStop.Status.Value.TotalMinutes.ToString();
StatusComboBox.SelectedItem = "正点";
StatusMinutesTextBox.Text = "0";
}
UpdateStatusInputState();
Validate(this, EventArgs.Empty);
originalTrainStop = trainStop;
}
private static bool ValidateTime(string input, int maxValue)
@@ -81,6 +102,13 @@ public sealed partial class TrainStopDialog : Page
}
return false;
}
private bool IsEarlyOrLateSelected()
{
if (StatusComboBox?.SelectedItem is not string status) return false;
return status == "早点" || status == "晚点";
}
private void Validate(object sender, object e)
{
if (StartHour is null) return;
@@ -89,6 +117,11 @@ public sealed partial class TrainStopDialog : Page
(!StartMinute.IsEnabled || ValidateTime(StartMinute.Text, 60)) &&
(!EndHour.IsEnabled || ValidateTime(EndHour.Text, 24)) &&
(!EndMinute.IsEnabled || ValidateTime(EndMinute.Text, 60));
bool isStatusValid =
!IsEarlyOrLateSelected() ||
(int.TryParse(StatusMinutesTextBox.Text, out int minutes) && minutes > 0);
bool isValid =
(!WaitingAreasList.IsEnabled ||
WaitingAreasList.SelectedItems?.Count != 0) &&
@@ -98,7 +131,8 @@ public sealed partial class TrainStopDialog : Page
!string.IsNullOrWhiteSpace(DepartureTextBox.Text) &&
int.TryParse(LengthTextBox.Text, out int i) && i > 0 &&
PlatformComboBox.SelectedItem != null &&
int.TryParse(StatusTextBox.Text, out int m);
StatusComboBox.SelectedItem != null &&
isStatusValid;
_onValidityChanged?.Invoke(isValid);
}
@@ -119,8 +153,25 @@ public sealed partial class TrainStopDialog : Page
Validate(this, EventArgs.Empty);
if (EndHour.Text.Length == 2) EndMinute.Focus(FocusState.Programmatic);
}
private void StatusComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
UpdateStatusInputState();
Validate(this, EventArgs.Empty);
}
public void GenerateTrainStop()
{
var selectedStatus = StatusComboBox.SelectedItem as string ?? "正点";
TimeSpan? statusValue = selectedStatus switch
{
"停运" => null,
"晚点未定" => TrainStatus.DelayUnknown,
"晚点" => TimeSpan.FromMinutes(int.Parse(StatusMinutesTextBox.Text)),
"早点" => -TimeSpan.FromMinutes(int.Parse(StatusMinutesTextBox.Text)),
_ => TimeSpan.Zero
};
GeneratedTrainStop = new TrainStop
{
Number = NumberTextBox.Text,
@@ -134,10 +185,16 @@ public sealed partial class TrainStopDialog : Page
Platform = (string)PlatformComboBox.SelectedItem,
Length = int.Parse(LengthTextBox.Text),
Landmark = (string)LandmarkComboBox.SelectedItem == "无" ? null : (string)LandmarkComboBox.SelectedItem,
Status = SuspendToggleSwitch.IsOn ? null : TimeSpan.FromMinutes(int.Parse(StatusTextBox.Text))
Status = statusValue
};
}
private void UpdateStatusInputState()
{
if (StatusMinutesTextBox is null || StatusComboBox is null) return;
StatusMinutesTextBox.IsEnabled = IsEarlyOrLateSelected();
}
private void StationKindPanelRadioButtons_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (StartHour == null) return;
@@ -199,4 +256,4 @@ public sealed partial class TrainStopDialog : Page
}
}
}
}

View File

@@ -19,7 +19,7 @@
d:DataContext="{d:DesignInstance Type=viewmodels:StationManagementPageViewModel}">
<Page.Resources>
<converters:TimeSpanToStringConverter x:Key="TimeSpanToStringConverter"/>
<converters:TimeSpanToStringConverter x:Key="TimeSpanToStringConverterForStatus" NullString="停运" ZeroString="正点"/>
<converters:TimeSpanToStringConverter x:Key="TimeSpanToStringConverterForStatus" NullString="停运" ZeroString="正点" UnknownString="晚点未定"/>
<converters:TicketChecksToStringConverter x:Key="TicketChecksToStringConverter" Separator=" " WaitingAreas="{x:Bind ViewModel.WaitingAreas}"/>
<DataTemplate x:Key="WaitingAreaTemplate" x:DataType="coremodels:WaitingArea">
<TreeViewItem Content="{x:Bind Name}" ItemsSource="{x:Bind TicketChecks}" />