feat: 定时任务可切换配置 (#5323)

This commit is contained in:
uye
2023-07-12 16:22:18 +08:00
committed by GitHub
10 changed files with 568 additions and 292 deletions

View File

@@ -20,6 +20,7 @@ namespace MaaWpfGui.Constants
{
public const string CurrentConfiguration = "Current";
public const string DefaultConfiguration = "Default";
public const string GlobalConfiguration = "Global";
public const string ConfigurationMap = "Configurations";
public const string ConfigurationData = "Data";
public const string ConfigurationCron = "Cron";
@@ -175,5 +176,6 @@ namespace MaaWpfGui.Constants
public const string GuideStepIndex = "Guide.StepIndex";
public const string ForceScheduledStart = "Timer.ForceScheduledStart";
public const string CustomConfig = "Timer.CustomConfig";
}
}

View File

@@ -29,6 +29,7 @@ namespace MaaWpfGui.Helper
private static Dictionary<string, Dictionary<string, string>> _kvsMap;
private static string _current = ConfigurationKeys.DefaultConfiguration;
private static Dictionary<string, string> _kvs;
private static Dictionary<string, string> _globalKvs;
private static readonly ILogger _logger = Log.ForContext<ConfigurationHelper>();
@@ -57,6 +58,15 @@ namespace MaaWpfGui.Helper
: defaultValue;
}
public static string GetGlobalValue(string key, string defaultValue)
{
var hasValue = _globalKvs.TryGetValue(key, out var value);
_logger.Debug("Read global configuration key {Key} with default value {DefaultValue}, configuration hit: {HasValue}, configuration value {Value}", key, defaultValue, hasValue, value);
return hasValue
? value
: defaultValue;
}
/// <summary>
/// Set a configuration value
/// </summary>
@@ -93,6 +103,36 @@ namespace MaaWpfGui.Helper
}
}
public static bool SetGlobalValue(string key, string value)
{
lock (_lock)
{
var old = string.Empty;
if (_globalKvs.ContainsKey(key))
{
old = _globalKvs[key];
_globalKvs[key] = value;
}
else
{
_globalKvs.Add(key, value);
}
var result = Save();
if (result)
{
ConfigurationUpdateEvent?.Invoke(key, old, value);
_logger.Debug("Global configuration {Key} has been set to {Value}", key, value);
}
else
{
_logger.Warning("Failed to save global configuration {Key} to {Value}", key, value);
}
return result;
}
}
/// <summary>
/// Deletes a configuration
/// </summary>
@@ -156,6 +196,8 @@ namespace MaaWpfGui.Helper
_current = ConfigurationKeys.DefaultConfiguration;
_kvsMap[_current] = new Dictionary<string, string>();
_kvs = _kvsMap[_current];
_kvsMap[ConfigurationKeys.GlobalConfiguration] = new Dictionary<string, string>();
_globalKvs = _kvsMap[ConfigurationKeys.GlobalConfiguration];
return false;
}
@@ -178,6 +220,15 @@ namespace MaaWpfGui.Helper
_kvs = _kvsMap[_current];
}
if (parsed.ContainsKey(ConfigurationKeys.GlobalConfiguration))
{
_globalKvs = parsed[ConfigurationKeys.GlobalConfiguration].ToObject<Dictionary<string, string>>();
}
else
{
_globalKvs = new Dictionary<string, string>();
}
return true;
}
}
@@ -200,6 +251,7 @@ namespace MaaWpfGui.Helper
{
{ ConfigurationKeys.ConfigurationMap, _kvsMap },
{ ConfigurationKeys.CurrentConfiguration, _current },
{ ConfigurationKeys.GlobalConfiguration, _globalKvs },
}, Formatting.Indented);
File.WriteAllText(file ?? _configurationFile, jsonStr);
@@ -235,32 +287,59 @@ namespace MaaWpfGui.Helper
public static string GetTimer(int i, string defaultValue)
{
return GetValue($"Timer.Timer{i + 1}", defaultValue);
// 迁移旧数据,过几个版本后删除
{
var val = GetValue($"Timer.Timer{i + 1}", defaultValue);
if (val != defaultValue)
{
SetTimer(i, val);
}
}
return GetGlobalValue($"Timer.Timer{i + 1}", defaultValue);
}
public static bool SetTimer(int i, string value)
{
return SetValue($"Timer.Timer{i + 1}", value);
return SetGlobalValue($"Timer.Timer{i + 1}", value);
}
public static string GetTimerHour(int i, string defaultValue)
{
return GetValue($"Timer.Timer{i + 1}Hour", defaultValue);
// 迁移旧数据,过几个版本后删除
{
var value = GetValue($"Timer.Timer{i + 1}Hour", defaultValue);
if (value != defaultValue)
{
SetTimerHour(i, value);
}
}
return GetGlobalValue($"Timer.Timer{i + 1}Hour", defaultValue);
}
public static bool SetTimerHour(int i, string value)
{
return SetValue($"Timer.Timer{i + 1}Hour", value);
return SetGlobalValue($"Timer.Timer{i + 1}Hour", value);
}
public static string GetTimerMin(int i, string defaultValue)
{
return GetValue($"Timer.Timer{i + 1}Min", defaultValue);
// 迁移旧数据,过几个版本后删除
{
var value = GetValue($"Timer.Timer{i + 1}Min", defaultValue);
if (value != defaultValue)
{
SetTimerMin(i, value);
}
}
return GetGlobalValue($"Timer.Timer{i + 1}Min", defaultValue);
}
public static bool SetTimerMin(int i, string value)
{
return SetValue($"Timer.Timer{i + 1}Min", value);
return SetGlobalValue($"Timer.Timer{i + 1}Min", value);
}
public static string GetTaskOrder(string task, string defaultValue)
@@ -273,6 +352,16 @@ namespace MaaWpfGui.Helper
return SetValue("TaskQueue.Order." + task, value);
}
public static string GetTimerConfig(int i, string defaultValue)
{
return GetValue($"Timer.Timer{i + 1}.Config", defaultValue);
}
public static bool SetTimerConfig(int i, string value)
{
return SetValue($"Timer.Timer{i + 1}.Config", value);
}
public static void Release()
{
lock (_lock)

View File

@@ -176,8 +176,11 @@
<system:String x:Key="NewVersionFoundButNoPackageDesc">Please manually download the full package to update!</system:String>
<system:String x:Key="ForceScheduledStart">Force scheduled start</system:String>
<system:String x:Key="ForceScheduledStartTip">Stop current task, restart game and start new tasks</system:String>
<system:String x:Key="TimerCustomConfig">Custom config</system:String>
<system:String x:Key="TimerCustomConfigTip">Restart and switch configuration two minutes in advance</system:String>
<system:String x:Key="Timer">Timer</system:String>
<system:String x:Key="SwitchConfiguration">Switch Configuration</system:String>
<system:String x:Key="TimerTip">This option page is for global config</system:String>
<system:String x:Key="SwitchConfiguration">Configuration</system:String>
<system:String x:Key="ConfigurationName">Config</system:String>
<system:String x:Key="AddConfiguration">Add</system:String>
<system:String x:Key="AddConfigSuccess">Configuration &lt;{0}&gt; added successfully</system:String>

View File

@@ -176,7 +176,10 @@
<system:String x:Key="NewVersionFoundButNoPackageDesc">アップデートするには、手動でフルパッケージをダウンロードしてください!</system:String>
<system:String x:Key="ForceScheduledStart">強制的なタイミングで起動</system:String>
<system:String x:Key="ForceScheduledStartTip">現在のタスクを停止して、ゲームを再起動し、新しいタスクを開始します</system:String>
<system:String x:Key="TimerCustomConfig">カスタム構成オプション</system:String>
<system:String x:Key="TimerCustomConfigTip">2分前に再起動して設定を切り替えます</system:String>
<system:String x:Key="Timer">タイマー</system:String>
<system:String x:Key="TimerTip">このオプション ページはグローバル設定用です</system:String>
<system:String x:Key="SwitchConfiguration">構成を切り替える</system:String>
<system:String x:Key="ConfigurationName">構成名</system:String>
<system:String x:Key="AddConfiguration">追加</system:String>

View File

@@ -176,7 +176,10 @@
<system:String x:Key="NewVersionFoundButNoPackageDesc">전체 패키지를 수동으로 다운로드해 업데이트해 주세요!</system:String>
<system:String x:Key="ForceScheduledStart">강제로 예약된 시작</system:String>
<system:String x:Key="ForceScheduledStartTip">현재 작업을 중지하고 게임을 다시 시작하여 새 작업을 시작합니다</system:String>
<system:String x:Key="TimerCustomConfig">사용자 지정 구성 옵션</system:String>
<system:String x:Key="TimerCustomConfigTip">2분 전에 재시작하여 설정을 전환합니다</system:String>
<system:String x:Key="Timer">타이머</system:String>
<system:String x:Key="TimerTip">이 옵션 페이지는 전역 구성용입니다.</system:String>
<system:String x:Key="SwitchConfiguration">구성 전환</system:String>
<system:String x:Key="ConfigurationName">구성 이름</system:String>
<system:String x:Key="AddConfiguration">에 추가</system:String>

View File

@@ -176,7 +176,10 @@
<system:String x:Key="NewVersionFoundButNoPackageDesc">请手动下载完整包更新!</system:String>
<system:String x:Key="ForceScheduledStart">强制定时启动</system:String>
<system:String x:Key="ForceScheduledStartTip">停止当前任务,重启游戏并开始新任务</system:String>
<system:String x:Key="TimerCustomConfig">自定义配置选择</system:String>
<system:String x:Key="TimerCustomConfigTip">将提前两分钟重启并切换配置</system:String>
<system:String x:Key="Timer">定时</system:String>
<system:String x:Key="TimerTip">此选项页为全局配置</system:String>
<system:String x:Key="SwitchConfiguration">切换配置</system:String>
<system:String x:Key="ConfigurationName">配置名称</system:String>
<system:String x:Key="AddConfiguration">添加</system:String>

View File

@@ -176,7 +176,10 @@
<system:String x:Key="NewVersionFoundButNoPackageDesc">請手動下載完整包更新!</system:String>
<system:String x:Key="ForceScheduledStart">強制定時啟動</system:String>
<system:String x:Key="ForceScheduledStartTip">停止當前任務,重新啟動遊戲並開始新任務</system:String>
<system:String x:Key="TimerCustomConfig">自定義配置選擇</system:String>
<system:String x:Key="TimerCustomConfigTip">將提前兩分鐘重啟並切換配置</system:String>
<system:String x:Key="Timer">定時</system:String>
<system:String x:Key="TimerTip">此選項頁為全局配置</system:String>
<system:String x:Key="SwitchConfiguration">切換配置</system:String>
<system:String x:Key="ConfigurationName">配置名稱</system:String>
<system:String x:Key="AddConfiguration">新增</system:String>

View File

@@ -1649,12 +1649,20 @@ namespace MaaWpfGui.ViewModels.UI
{
public event PropertyChangedEventHandler PropertyChanged;
public TimerProperties(int timeId, bool isOn, int hour, int min)
public TimerProperties(int timeId, bool isOn, int hour, int min, string timerConfig)
{
TimerId = timeId;
_isOn = isOn;
_hour = hour;
_min = min;
if (timerConfig == null || !ConfigurationHelper.GetConfigurationList().Contains(timerConfig))
{
_timerConfig = ConfigurationHelper.GetCurrentConfiguration();
}
else
{
_timerConfig = timerConfig;
}
}
protected void OnPropertyChanged([CallerMemberName] string name = null)
@@ -1712,6 +1720,22 @@ namespace MaaWpfGui.ViewModels.UI
}
}
private string _timerConfig;
/// <summary>
/// Gets or sets the config of the timer.
/// </summary>
public string TimerConfig
{
get => _timerConfig;
set
{
_timerConfig = value ?? ConfigurationHelper.GetCurrentConfiguration();
OnPropertyChanged();
ConfigurationHelper.SetTimerConfig(TimerId, value.ToString());
}
}
public TimerProperties()
{
PropertyChanged += (sender, args) => { };
@@ -1728,7 +1752,8 @@ namespace MaaWpfGui.ViewModels.UI
i,
ConfigurationHelper.GetTimer(i, bool.FalseString) == bool.TrueString,
int.Parse(ConfigurationHelper.GetTimerHour(i, $"{i * 3}")),
int.Parse(ConfigurationHelper.GetTimerMin(i, "0")));
int.Parse(ConfigurationHelper.GetTimerMin(i, "0")),
ConfigurationHelper.GetTimerConfig(i, ConfigurationHelper.GetCurrentConfiguration()));
}
}
}
@@ -1750,6 +1775,21 @@ namespace MaaWpfGui.ViewModels.UI
}
}
private bool _customConfig = Convert.ToBoolean(ConfigurationHelper.GetValue(ConfigurationKeys.CustomConfig, bool.FalseString));
/// <summary>
/// Gets or sets a value indicating whether to use custom config.
/// </summary>
public bool CustomConfig
{
get => _customConfig;
set
{
SetAndNotify(ref _customConfig, value);
ConfigurationHelper.SetValue(ConfigurationKeys.CustomConfig, value.ToString());
}
}
/* 刷理智设置 */
private string _penguinId = ConfigurationHelper.GetValue(ConfigurationKeys.PenguinId, string.Empty);

View File

@@ -209,8 +209,8 @@ namespace MaaWpfGui.ViewModels.UI
private async void Timer1_Elapsed(object sender, EventArgs e)
{
// 提前记录时间,避免等待超过定时时间
int intHour = DateTime.Now.Hour;
int intMinute = DateTime.Now.Minute;
DateTime currentTime = DateTime.Now;
currentTime = new DateTime(currentTime.Year, currentTime.Month, currentTime.Day, currentTime.Hour, currentTime.Minute, 0);
if (NeedToUpdateDatePrompt())
{
@@ -250,17 +250,38 @@ namespace MaaWpfGui.ViewModels.UI
}
var timeToStart = false;
var timeToChangeConfig = false;
var configIndex = 0;
for (int i = 0; i < 8; ++i)
{
if (Instances.SettingsViewModel.TimerModels.Timers[i].IsOn &&
Instances.SettingsViewModel.TimerModels.Timers[i].Hour == intHour &&
Instances.SettingsViewModel.TimerModels.Timers[i].Min == intMinute)
if (Instances.SettingsViewModel.TimerModels.Timers[i].IsOn)
{
timeToStart = true;
break;
DateTime startTime = new DateTime(currentTime.Year, currentTime.Month, currentTime.Day,
Instances.SettingsViewModel.TimerModels.Timers[i].Hour,
Instances.SettingsViewModel.TimerModels.Timers[i].Min,
0);
DateTime restartDateTime = startTime.AddMinutes(-2);
if (currentTime == restartDateTime)
{
timeToChangeConfig = true;
configIndex = i;
break;
}
else if (currentTime == startTime)
{
timeToStart = true;
break;
}
}
}
if (timeToChangeConfig)
{
// CurrentConfiguration设置后会重启
Instances.SettingsViewModel.CurrentConfiguration = Instances.SettingsViewModel.TimerModels.Timers[2].TimerConfig;
return;
}
if (timeToStart)
{
if (Instances.SettingsViewModel.ForceScheduledStart)

View File

@@ -13,23 +13,44 @@
xmlns:viewModels="clr-namespace:MaaWpfGui.ViewModels"
xmlns:vm="clr-namespace:MaaWpfGui"
d:DataContext="{d:DesignInstance {x:Type ui:SettingsViewModel}}"
d:DesignHeight="300"
d:DesignWidth="400"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition />
<RowDefinition Height="auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<CheckBox
<controls:TextBlock
Grid.Row="0"
Height="30"
HorizontalAlignment="Center"
VerticalContentAlignment="Center"
Content="{DynamicResource ForceScheduledStart}"
IsChecked="{Binding ForceScheduledStart}"
ToolTip="{DynamicResource ForceScheduledStartTip}" />
<Grid Grid.Row="1">
FontWeight="Bold"
Text="{DynamicResource TimerTip}"
TextAlignment="Center" />
<StackPanel
Grid.Row="1"
HorizontalAlignment="Center"
Orientation="Horizontal">
<CheckBox
Height="30"
Margin="5,0"
HorizontalAlignment="Center"
VerticalContentAlignment="Center"
Content="{DynamicResource ForceScheduledStart}"
IsChecked="{Binding ForceScheduledStart}"
ToolTip="{DynamicResource ForceScheduledStartTip}" />
<CheckBox
Height="30"
Margin="5,0"
HorizontalAlignment="Center"
VerticalContentAlignment="Center"
Content="{DynamicResource TimerCustomConfig}"
IsChecked="{Binding CustomConfig}"
ToolTip="{DynamicResource TimerCustomConfigTip}" />
</StackPanel>
<Grid Grid.Row="2">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
@@ -37,296 +58,384 @@
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="200" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel
Grid.Row="0"
Grid.Column="0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<CheckBox Margin="10" IsChecked="{Binding TimerModels.Timers[0].IsOn}">
<CheckBox.Content>
<controls:TextBlock>
<controls:TextBlock.Text>
<MultiBinding StringFormat="{}{0} 1">
<Binding Source="{StaticResource Timer}" />
</MultiBinding>
</controls:TextBlock.Text>
</controls:TextBlock>
</CheckBox.Content>
</CheckBox>
<StackPanel Orientation="Horizontal">
<TextBox
Width="35"
Margin="10"
InputMethod.IsInputMethodEnabled="False"
IsReadOnly="{c:Binding !TimerModels.Timers[0].IsOn}"
Text="{Binding TimerModels.Timers[0].Hour, StringFormat=D2}"
TextWrapping="Wrap" />
<controls:TextBlock Text=":" />
<TextBox
Width="35"
Margin="10"
InputMethod.IsInputMethodEnabled="False"
IsReadOnly="{c:Binding !TimerModels.Timers[0].IsOn}"
Text="{Binding TimerModels.Timers[0].Min, StringFormat=D2}"
TextWrapping="Wrap" />
<StackPanel Grid.Row="0" Grid.Column="0">
<StackPanel
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<CheckBox Margin="10" IsChecked="{Binding TimerModels.Timers[0].IsOn}">
<CheckBox.Content>
<controls:TextBlock>
<controls:TextBlock.Text>
<MultiBinding StringFormat="{}{0} 1">
<Binding Source="{StaticResource Timer}" />
</MultiBinding>
</controls:TextBlock.Text>
</controls:TextBlock>
</CheckBox.Content>
</CheckBox>
<StackPanel Orientation="Horizontal">
<TextBox
Width="35"
Margin="10"
InputMethod.IsInputMethodEnabled="False"
IsReadOnly="{c:Binding !TimerModels.Timers[0].IsOn}"
Text="{Binding TimerModels.Timers[0].Hour, StringFormat=D2}"
TextWrapping="Wrap" />
<controls:TextBlock Text=":" />
<TextBox
Width="35"
Margin="10"
InputMethod.IsInputMethodEnabled="False"
IsReadOnly="{c:Binding !TimerModels.Timers[0].IsOn}"
Text="{Binding TimerModels.Timers[0].Min, StringFormat=D2}"
TextWrapping="Wrap" />
</StackPanel>
</StackPanel>
<ComboBox
Width="150"
HorizontalAlignment="Center"
VerticalAlignment="Center"
IsEditable="True"
IsHitTestVisible="{Binding Idle}"
IsReadOnly="True"
ItemsSource="{Binding ConfigurationList}"
SelectedValue="{Binding TimerModels.Timers[0].TimerConfig}"
SelectedValuePath="Value"
Visibility="{c:Binding CustomConfig}" />
</StackPanel>
<StackPanel
Grid.Row="1"
Grid.Column="0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<CheckBox Margin="10" IsChecked="{Binding TimerModels.Timers[1].IsOn}">
<CheckBox.Content>
<controls:TextBlock>
<controls:TextBlock.Text>
<MultiBinding StringFormat="{}{0} 2">
<Binding Source="{StaticResource Timer}" />
</MultiBinding>
</controls:TextBlock.Text>
</controls:TextBlock>
</CheckBox.Content>
</CheckBox>
<StackPanel Orientation="Horizontal">
<TextBox
Width="35"
Margin="10"
InputMethod.IsInputMethodEnabled="False"
IsReadOnly="{c:Binding !TimerModels.Timers[1].IsOn}"
Text="{Binding TimerModels.Timers[1].Hour, StringFormat=D2}"
TextWrapping="Wrap" />
<controls:TextBlock Text=":" />
<TextBox
Width="35"
Margin="10"
InputMethod.IsInputMethodEnabled="False"
IsReadOnly="{c:Binding !TimerModels.Timers[1].IsOn}"
Text="{Binding TimerModels.Timers[1].Min, StringFormat=D2}"
TextWrapping="Wrap" />
<StackPanel Grid.Row="1" Grid.Column="0">
<StackPanel
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<CheckBox Margin="10" IsChecked="{Binding TimerModels.Timers[1].IsOn}">
<CheckBox.Content>
<controls:TextBlock>
<controls:TextBlock.Text>
<MultiBinding StringFormat="{}{0} 2">
<Binding Source="{StaticResource Timer}" />
</MultiBinding>
</controls:TextBlock.Text>
</controls:TextBlock>
</CheckBox.Content>
</CheckBox>
<StackPanel Orientation="Horizontal">
<TextBox
Width="35"
Margin="10"
InputMethod.IsInputMethodEnabled="False"
IsReadOnly="{c:Binding !TimerModels.Timers[1].IsOn}"
Text="{Binding TimerModels.Timers[1].Hour, StringFormat=D2}"
TextWrapping="Wrap" />
<controls:TextBlock Text=":" />
<TextBox
Width="35"
Margin="10"
InputMethod.IsInputMethodEnabled="False"
IsReadOnly="{c:Binding !TimerModels.Timers[1].IsOn}"
Text="{Binding TimerModels.Timers[1].Min, StringFormat=D2}"
TextWrapping="Wrap" />
</StackPanel>
</StackPanel>
<ComboBox
Width="150"
HorizontalAlignment="Center"
VerticalAlignment="Center"
IsEditable="True"
IsHitTestVisible="{Binding Idle}"
IsReadOnly="True"
ItemsSource="{Binding ConfigurationList}"
SelectedValue="{Binding TimerModels.Timers[1].TimerConfig}"
SelectedValuePath="Value"
Visibility="{c:Binding CustomConfig}" />
</StackPanel>
<StackPanel
Grid.Row="2"
Grid.Column="0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<CheckBox Margin="10" IsChecked="{Binding TimerModels.Timers[2].IsOn}">
<CheckBox.Content>
<controls:TextBlock>
<controls:TextBlock.Text>
<MultiBinding StringFormat="{}{0} 3">
<Binding Source="{StaticResource Timer}" />
</MultiBinding>
</controls:TextBlock.Text>
</controls:TextBlock>
</CheckBox.Content>
</CheckBox>
<StackPanel Orientation="Horizontal">
<TextBox
Width="35"
Margin="10"
InputMethod.IsInputMethodEnabled="False"
IsReadOnly="{c:Binding !TimerModels.Timers[2].IsOn}"
Text="{Binding TimerModels.Timers[2].Hour, StringFormat=D2}"
TextWrapping="Wrap" />
<controls:TextBlock Text=":" />
<TextBox
Width="35"
Margin="10"
InputMethod.IsInputMethodEnabled="False"
IsReadOnly="{c:Binding !TimerModels.Timers[2].IsOn}"
Text="{Binding TimerModels.Timers[2].Min, StringFormat=D2}"
TextWrapping="Wrap" />
<StackPanel Grid.Row="2" Grid.Column="0">
<StackPanel
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<CheckBox Margin="10" IsChecked="{Binding TimerModels.Timers[2].IsOn}">
<CheckBox.Content>
<controls:TextBlock>
<controls:TextBlock.Text>
<MultiBinding StringFormat="{}{0} 3">
<Binding Source="{StaticResource Timer}" />
</MultiBinding>
</controls:TextBlock.Text>
</controls:TextBlock>
</CheckBox.Content>
</CheckBox>
<StackPanel Orientation="Horizontal">
<TextBox
Width="35"
Margin="10"
InputMethod.IsInputMethodEnabled="False"
IsReadOnly="{c:Binding !TimerModels.Timers[2].IsOn}"
Text="{Binding TimerModels.Timers[2].Hour, StringFormat=D2}"
TextWrapping="Wrap" />
<controls:TextBlock Text=":" />
<TextBox
Width="35"
Margin="10"
InputMethod.IsInputMethodEnabled="False"
IsReadOnly="{c:Binding !TimerModels.Timers[2].IsOn}"
Text="{Binding TimerModels.Timers[2].Min, StringFormat=D2}"
TextWrapping="Wrap" />
</StackPanel>
</StackPanel>
<ComboBox
Width="150"
HorizontalAlignment="Center"
VerticalAlignment="Center"
IsEditable="True"
IsHitTestVisible="{Binding Idle}"
IsReadOnly="True"
ItemsSource="{Binding ConfigurationList}"
SelectedValue="{Binding TimerModels.Timers[2].TimerConfig}"
SelectedValuePath="Value"
Visibility="{c:Binding CustomConfig}" />
</StackPanel>
<StackPanel
Grid.Row="3"
Grid.Column="0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<CheckBox Margin="10" IsChecked="{Binding TimerModels.Timers[3].IsOn}">
<CheckBox.Content>
<controls:TextBlock>
<controls:TextBlock.Text>
<MultiBinding StringFormat="{}{0} 4">
<Binding Source="{StaticResource Timer}" />
</MultiBinding>
</controls:TextBlock.Text>
</controls:TextBlock>
</CheckBox.Content>
</CheckBox>
<StackPanel Orientation="Horizontal">
<TextBox
Width="35"
Margin="10"
InputMethod.IsInputMethodEnabled="False"
IsReadOnly="{c:Binding !TimerModels.Timers[3].IsOn}"
Text="{Binding TimerModels.Timers[3].Hour, StringFormat=D2}"
TextWrapping="Wrap" />
<controls:TextBlock Text=":" />
<TextBox
Width="35"
Margin="10"
InputMethod.IsInputMethodEnabled="False"
IsReadOnly="{c:Binding !TimerModels.Timers[3].IsOn}"
Text="{Binding TimerModels.Timers[3].Min, StringFormat=D2}"
TextWrapping="Wrap" />
<StackPanel Grid.Row="3" Grid.Column="0">
<StackPanel
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<CheckBox Margin="10" IsChecked="{Binding TimerModels.Timers[3].IsOn}">
<CheckBox.Content>
<controls:TextBlock>
<controls:TextBlock.Text>
<MultiBinding StringFormat="{}{0} 4">
<Binding Source="{StaticResource Timer}" />
</MultiBinding>
</controls:TextBlock.Text>
</controls:TextBlock>
</CheckBox.Content>
</CheckBox>
<StackPanel Orientation="Horizontal">
<TextBox
Width="35"
Margin="10"
InputMethod.IsInputMethodEnabled="False"
IsReadOnly="{c:Binding !TimerModels.Timers[3].IsOn}"
Text="{Binding TimerModels.Timers[3].Hour, StringFormat=D2}"
TextWrapping="Wrap" />
<controls:TextBlock Text=":" />
<TextBox
Width="35"
Margin="10"
InputMethod.IsInputMethodEnabled="False"
IsReadOnly="{c:Binding !TimerModels.Timers[3].IsOn}"
Text="{Binding TimerModels.Timers[3].Min, StringFormat=D2}"
TextWrapping="Wrap" />
</StackPanel>
</StackPanel>
<ComboBox
Width="150"
HorizontalAlignment="Center"
VerticalAlignment="Center"
IsEditable="True"
IsHitTestVisible="{Binding Idle}"
IsReadOnly="True"
ItemsSource="{Binding ConfigurationList}"
SelectedValue="{Binding TimerModels.Timers[3].TimerConfig}"
SelectedValuePath="Value"
Visibility="{c:Binding CustomConfig}" />
</StackPanel>
<StackPanel
Grid.Row="0"
Grid.Column="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<CheckBox Margin="10" IsChecked="{Binding TimerModels.Timers[4].IsOn}">
<CheckBox.Content>
<controls:TextBlock>
<controls:TextBlock.Text>
<MultiBinding StringFormat="{}{0} 5">
<Binding Source="{StaticResource Timer}" />
</MultiBinding>
</controls:TextBlock.Text>
</controls:TextBlock>
</CheckBox.Content>
</CheckBox>
<StackPanel Orientation="Horizontal">
<TextBox
Width="35"
Margin="10"
InputMethod.IsInputMethodEnabled="False"
IsReadOnly="{c:Binding !TimerModels.Timers[4].IsOn}"
Text="{Binding TimerModels.Timers[4].Hour, StringFormat=D2}"
TextWrapping="Wrap" />
<controls:TextBlock Text=":" />
<TextBox
Width="35"
Margin="10"
InputMethod.IsInputMethodEnabled="False"
IsReadOnly="{c:Binding !TimerModels.Timers[4].IsOn}"
Text="{Binding TimerModels.Timers[4].Min, StringFormat=D2}"
TextWrapping="Wrap" />
<StackPanel Grid.Row="0" Grid.Column="1">
<StackPanel
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<CheckBox Margin="10" IsChecked="{Binding TimerModels.Timers[4].IsOn}">
<CheckBox.Content>
<controls:TextBlock>
<controls:TextBlock.Text>
<MultiBinding StringFormat="{}{0} 5">
<Binding Source="{StaticResource Timer}" />
</MultiBinding>
</controls:TextBlock.Text>
</controls:TextBlock>
</CheckBox.Content>
</CheckBox>
<StackPanel Orientation="Horizontal">
<TextBox
Width="35"
Margin="10"
InputMethod.IsInputMethodEnabled="False"
IsReadOnly="{c:Binding !TimerModels.Timers[4].IsOn}"
Text="{Binding TimerModels.Timers[4].Hour, StringFormat=D2}"
TextWrapping="Wrap" />
<controls:TextBlock Text=":" />
<TextBox
Width="35"
Margin="10"
InputMethod.IsInputMethodEnabled="False"
IsReadOnly="{c:Binding !TimerModels.Timers[4].IsOn}"
Text="{Binding TimerModels.Timers[4].Min, StringFormat=D2}"
TextWrapping="Wrap" />
</StackPanel>
</StackPanel>
<ComboBox
Width="150"
HorizontalAlignment="Center"
VerticalAlignment="Center"
IsEditable="True"
IsHitTestVisible="{Binding Idle}"
IsReadOnly="True"
ItemsSource="{Binding ConfigurationList}"
SelectedValue="{Binding TimerModels.Timers[4].TimerConfig}"
SelectedValuePath="Value"
Visibility="{c:Binding CustomConfig}" />
</StackPanel>
<StackPanel
Grid.Row="1"
Grid.Column="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<CheckBox Margin="10" IsChecked="{Binding TimerModels.Timers[5].IsOn}">
<CheckBox.Content>
<controls:TextBlock>
<controls:TextBlock.Text>
<MultiBinding StringFormat="{}{0} 6">
<Binding Source="{StaticResource Timer}" />
</MultiBinding>
</controls:TextBlock.Text>
</controls:TextBlock>
</CheckBox.Content>
</CheckBox>
<StackPanel Orientation="Horizontal">
<TextBox
Width="35"
Margin="10"
InputMethod.IsInputMethodEnabled="False"
IsReadOnly="{c:Binding !TimerModels.Timers[5].IsOn}"
Text="{Binding TimerModels.Timers[5].Hour, StringFormat=D2}"
TextWrapping="Wrap" />
<controls:TextBlock Text=":" />
<TextBox
Width="35"
Margin="10"
InputMethod.IsInputMethodEnabled="False"
IsReadOnly="{c:Binding !TimerModels.Timers[5].IsOn}"
Text="{Binding TimerModels.Timers[5].Min, StringFormat=D2}"
TextWrapping="Wrap" />
<StackPanel Grid.Row="1" Grid.Column="1">
<StackPanel
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<CheckBox Margin="10" IsChecked="{Binding TimerModels.Timers[5].IsOn}">
<CheckBox.Content>
<controls:TextBlock>
<controls:TextBlock.Text>
<MultiBinding StringFormat="{}{0} 6">
<Binding Source="{StaticResource Timer}" />
</MultiBinding>
</controls:TextBlock.Text>
</controls:TextBlock>
</CheckBox.Content>
</CheckBox>
<StackPanel Orientation="Horizontal">
<TextBox
Width="35"
Margin="10"
InputMethod.IsInputMethodEnabled="False"
IsReadOnly="{c:Binding !TimerModels.Timers[5].IsOn}"
Text="{Binding TimerModels.Timers[5].Hour, StringFormat=D2}"
TextWrapping="Wrap" />
<controls:TextBlock Text=":" />
<TextBox
Width="35"
Margin="10"
InputMethod.IsInputMethodEnabled="False"
IsReadOnly="{c:Binding !TimerModels.Timers[5].IsOn}"
Text="{Binding TimerModels.Timers[5].Min, StringFormat=D2}"
TextWrapping="Wrap" />
</StackPanel>
</StackPanel>
<ComboBox
Width="150"
HorizontalAlignment="Center"
VerticalAlignment="Center"
IsEditable="True"
IsHitTestVisible="{Binding Idle}"
IsReadOnly="True"
ItemsSource="{Binding ConfigurationList}"
SelectedValue="{Binding TimerModels.Timers[5].TimerConfig}"
SelectedValuePath="Value"
Visibility="{c:Binding CustomConfig}" />
</StackPanel>
<StackPanel
Grid.Row="2"
Grid.Column="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<CheckBox Margin="10" IsChecked="{Binding TimerModels.Timers[6].IsOn}">
<CheckBox.Content>
<controls:TextBlock>
<controls:TextBlock.Text>
<MultiBinding StringFormat="{}{0} 7">
<Binding Source="{StaticResource Timer}" />
</MultiBinding>
</controls:TextBlock.Text>
</controls:TextBlock>
</CheckBox.Content>
</CheckBox>
<StackPanel Orientation="Horizontal">
<TextBox
Width="35"
Margin="10"
InputMethod.IsInputMethodEnabled="False"
IsReadOnly="{c:Binding !TimerModels.Timers[6].IsOn}"
Text="{Binding TimerModels.Timers[6].Hour, StringFormat=D2}"
TextWrapping="Wrap" />
<controls:TextBlock Text=":" />
<TextBox
Width="35"
Margin="10"
InputMethod.IsInputMethodEnabled="False"
IsReadOnly="{c:Binding !TimerModels.Timers[6].IsOn}"
Text="{Binding TimerModels.Timers[6].Min, StringFormat=D2}"
TextWrapping="Wrap" />
<StackPanel Grid.Row="2" Grid.Column="1">
<StackPanel
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<CheckBox Margin="10" IsChecked="{Binding TimerModels.Timers[6].IsOn}">
<CheckBox.Content>
<controls:TextBlock>
<controls:TextBlock.Text>
<MultiBinding StringFormat="{}{0} 7">
<Binding Source="{StaticResource Timer}" />
</MultiBinding>
</controls:TextBlock.Text>
</controls:TextBlock>
</CheckBox.Content>
</CheckBox>
<StackPanel Orientation="Horizontal">
<TextBox
Width="35"
Margin="10"
InputMethod.IsInputMethodEnabled="False"
IsReadOnly="{c:Binding !TimerModels.Timers[6].IsOn}"
Text="{Binding TimerModels.Timers[6].Hour, StringFormat=D2}"
TextWrapping="Wrap" />
<controls:TextBlock Text=":" />
<TextBox
Width="35"
Margin="10"
InputMethod.IsInputMethodEnabled="False"
IsReadOnly="{c:Binding !TimerModels.Timers[6].IsOn}"
Text="{Binding TimerModels.Timers[6].Min, StringFormat=D2}"
TextWrapping="Wrap" />
</StackPanel>
</StackPanel>
<ComboBox
Width="150"
HorizontalAlignment="Center"
VerticalAlignment="Center"
IsEditable="True"
IsHitTestVisible="{Binding Idle}"
IsReadOnly="True"
ItemsSource="{Binding ConfigurationList}"
SelectedValue="{Binding TimerModels.Timers[6].TimerConfig}"
SelectedValuePath="Value"
Visibility="{c:Binding CustomConfig}" />
</StackPanel>
<StackPanel
Grid.Row="3"
Grid.Column="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<CheckBox Margin="10" IsChecked="{Binding TimerModels.Timers[7].IsOn}">
<CheckBox.Content>
<controls:TextBlock>
<controls:TextBlock.Text>
<MultiBinding StringFormat="{}{0} 8">
<Binding Source="{StaticResource Timer}" />
</MultiBinding>
</controls:TextBlock.Text>
</controls:TextBlock>
</CheckBox.Content>
</CheckBox>
<StackPanel Orientation="Horizontal">
<TextBox
Width="35"
Margin="10"
InputMethod.IsInputMethodEnabled="False"
IsReadOnly="{c:Binding !TimerModels.Timers[7].IsOn}"
Text="{Binding TimerModels.Timers[7].Hour, StringFormat=D2}"
TextWrapping="Wrap" />
<controls:TextBlock Text=":" />
<TextBox
Width="35"
Margin="10"
InputMethod.IsInputMethodEnabled="False"
IsReadOnly="{c:Binding !TimerModels.Timers[7].IsOn}"
Text="{Binding TimerModels.Timers[7].Min, StringFormat=D2}"
TextWrapping="Wrap" />
<StackPanel Grid.Row="3" Grid.Column="1">
<StackPanel
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<CheckBox Margin="10" IsChecked="{Binding TimerModels.Timers[7].IsOn}">
<CheckBox.Content>
<controls:TextBlock>
<controls:TextBlock.Text>
<MultiBinding StringFormat="{}{0} 8">
<Binding Source="{StaticResource Timer}" />
</MultiBinding>
</controls:TextBlock.Text>
</controls:TextBlock>
</CheckBox.Content>
</CheckBox>
<StackPanel Orientation="Horizontal">
<TextBox
Width="35"
Margin="10"
InputMethod.IsInputMethodEnabled="False"
IsReadOnly="{c:Binding !TimerModels.Timers[7].IsOn}"
Text="{Binding TimerModels.Timers[7].Hour, StringFormat=D2}"
TextWrapping="Wrap" />
<controls:TextBlock Text=":" />
<TextBox
Width="35"
Margin="10"
InputMethod.IsInputMethodEnabled="False"
IsReadOnly="{c:Binding !TimerModels.Timers[7].IsOn}"
Text="{Binding TimerModels.Timers[7].Min, StringFormat=D2}"
TextWrapping="Wrap" />
</StackPanel>
</StackPanel>
<ComboBox
Width="150"
HorizontalAlignment="Center"
VerticalAlignment="Center"
IsEditable="True"
IsHitTestVisible="{Binding Idle}"
IsReadOnly="True"
ItemsSource="{Binding ConfigurationList}"
SelectedValue="{Binding TimerModels.Timers[7].TimerConfig}"
SelectedValuePath="Value"
Visibility="{c:Binding CustomConfig}" />
</StackPanel>
</Grid>
</Grid>