feat(ui): add ThemedDatePicker control and adopt in Prime settings

New themed picker supports single-date, date+time, and range modes
(replaces inconsistent CalendarDatePicker / DatePicker / TimePicker
visuals). Used in the Prime schedules row to combine StartDate /
EndDate into a single range picker.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
mika kuns
2026-04-29 10:39:53 +02:00
parent 121e8cd476
commit 47b07373af
3 changed files with 602 additions and 11 deletions

View File

@@ -0,0 +1,167 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ClaudeDo.Ui.Views.Controls.ThemedDatePicker"
x:Name="Root">
<UserControl.Styles>
<Style Selector="ToggleButton.trigger">
<Setter Property="Background" Value="{DynamicResource DeepBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource LineBrush}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="CornerRadius" Value="{StaticResource InputCornerRadius}"/>
<Setter Property="Padding" Value="10,6"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Foreground" Value="{DynamicResource TextBrush}"/>
<Setter Property="MinHeight" Value="30"/>
</Style>
<Style Selector="ToggleButton.trigger:pointerover /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource Surface2Brush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource LineBrightBrush}"/>
</Style>
<Style Selector="ToggleButton.trigger:checked /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource Surface2Brush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource AccentBrush}"/>
</Style>
<Style Selector="Button.quick">
<Setter Property="Background" Value="{DynamicResource DeepBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource LineBrush}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Foreground" Value="{DynamicResource TextDimBrush}"/>
<Setter Property="CornerRadius" Value="999"/>
<Setter Property="Padding" Value="10,3"/>
<Setter Property="FontSize" Value="11"/>
<Setter Property="MinHeight" Value="22"/>
</Style>
<Style Selector="Button.quick:pointerover /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource Surface3Brush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource LineBrightBrush}"/>
<Setter Property="TextElement.Foreground" Value="{DynamicResource TextBrush}"/>
</Style>
<Style Selector="Button.nav">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Foreground" Value="{DynamicResource TextDimBrush}"/>
<Setter Property="Padding" Value="6,2"/>
<Setter Property="CornerRadius" Value="6"/>
<Setter Property="MinWidth" Value="28"/>
</Style>
<Style Selector="Button.nav:pointerover /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource Surface3Brush}"/>
<Setter Property="TextElement.Foreground" Value="{DynamicResource TextBrush}"/>
</Style>
<Style Selector="Button.day">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Foreground" Value="{DynamicResource TextBrush}"/>
<Setter Property="Width" Value="32"/>
<Setter Property="Height" Value="32"/>
<Setter Property="CornerRadius" Value="999"/>
<Setter Property="FontSize" Value="12"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="0"/>
</Style>
<Style Selector="Button.day:pointerover /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource Surface3Brush}"/>
</Style>
<Style Selector="Button.day.outside">
<Setter Property="Foreground" Value="{DynamicResource TextFaintBrush}"/>
</Style>
<Style Selector="Button.day.today">
<Setter Property="BorderBrush" Value="{DynamicResource AccentBrush}"/>
</Style>
<Style Selector="Button.day.selected /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource AccentBrush}"/>
<Setter Property="TextElement.Foreground" Value="White"/>
</Style>
<Style Selector="Button.day.selected:pointerover /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource AccentDimBrush}"/>
</Style>
<Style Selector="TextBlock.weekday">
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="Foreground" Value="{DynamicResource TextMuteBrush}"/>
<Setter Property="FontSize" Value="10"/>
<Setter Property="FontWeight" Value="SemiBold"/>
</Style>
</UserControl.Styles>
<Grid>
<ToggleButton x:Name="TriggerButton" Classes="trigger">
<Grid ColumnDefinitions="Auto,*,Auto">
<PathIcon Grid.Column="0" Width="14" Height="14"
Margin="0,0,8,0"
Foreground="{DynamicResource TextDimBrush}"
Data="M19,4H18V2H16V4H8V2H6V4H5A2,2 0 0,0 3,6V20A2,2 0 0,0 5,22H19A2,2 0 0,0 21,20V6A2,2 0 0,0 19,4M19,20H5V10H19V20M19,8H5V6H19V8Z"/>
<TextBlock Grid.Column="1"
Text="{Binding #Root.DisplayText}"
Foreground="{Binding #Root.DisplayForeground}"
VerticalAlignment="Center"
TextTrimming="CharacterEllipsis"/>
<PathIcon Grid.Column="2" Width="10" Height="10"
Margin="8,0,0,0"
Foreground="{DynamicResource TextMuteBrush}"
Data="M7,10L12,15L17,10H7Z"/>
</Grid>
</ToggleButton>
<Popup x:Name="PickerPopup"
PlacementTarget="{Binding #TriggerButton}"
Placement="Bottom"
IsOpen="{Binding #TriggerButton.IsChecked, Mode=TwoWay}"
IsLightDismissEnabled="True">
<Border Background="{DynamicResource Surface2Brush}"
BorderBrush="{DynamicResource LineBrush}"
BorderThickness="1"
CornerRadius="10"
Padding="14"
Margin="0,4,0,0"
BoxShadow="{StaticResource ModalShadow}"
MinWidth="300">
<StackPanel Spacing="10">
<StackPanel Orientation="Horizontal" Spacing="6">
<Button Classes="quick" Content="Today" Click="OnTodayClick"/>
<Button Classes="quick" Content="Tomorrow" Click="OnTomorrowClick"/>
<Button Classes="quick" Content="Next Mon" Click="OnNextMondayClick"/>
<Button Classes="quick" Content="Clear" Click="OnClearClick"/>
</StackPanel>
<Grid ColumnDefinitions="Auto,*,Auto" Margin="0,2,0,0">
<Button Grid.Column="0" Click="OnPrevMonthClick" Classes="nav" Content="◀"/>
<TextBlock Grid.Column="1" x:Name="MonthHeader"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontWeight="SemiBold"
FontSize="13"
Foreground="{DynamicResource TextBrush}"/>
<Button Grid.Column="2" Click="OnNextMonthClick" Classes="nav" Content="▶"/>
</Grid>
<UniformGrid Columns="7" x:Name="WeekdayHeaders"/>
<UniformGrid Columns="7" Rows="6" x:Name="DayGrid"/>
<Grid x:Name="TimeRow"
ColumnDefinitions="Auto,*,Auto"
Margin="0,4,0,0">
<TextBlock Grid.Column="0" Text="Time"
VerticalAlignment="Center"
Foreground="{DynamicResource TextDimBrush}"
Margin="0,0,8,0"/>
<TextBox Grid.Column="1" x:Name="TimeInput"
Watermark="HH:mm" MaxLength="5"
Text="{Binding #Root.TimeText, Mode=TwoWay}"/>
<Button Grid.Column="2" Content="Done"
Click="OnDoneClick"
Margin="8,0,0,0"/>
</Grid>
</StackPanel>
</Border>
</Popup>
</Grid>
</UserControl>

View File

@@ -0,0 +1,423 @@
using System;
using System.Globalization;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.Media;
namespace ClaudeDo.Ui.Views.Controls;
public partial class ThemedDatePicker : UserControl
{
public static readonly StyledProperty<DateTime?> SelectedDateProperty =
AvaloniaProperty.Register<ThemedDatePicker, DateTime?>(
nameof(SelectedDate), defaultBindingMode: BindingMode.TwoWay);
public static readonly StyledProperty<bool> ShowTimeProperty =
AvaloniaProperty.Register<ThemedDatePicker, bool>(nameof(ShowTime), false);
public static readonly StyledProperty<bool> IsRangeProperty =
AvaloniaProperty.Register<ThemedDatePicker, bool>(nameof(IsRange), false);
public static readonly StyledProperty<DateTime?> StartDateProperty =
AvaloniaProperty.Register<ThemedDatePicker, DateTime?>(
nameof(StartDate), defaultBindingMode: BindingMode.TwoWay);
public static readonly StyledProperty<DateTime?> EndDateProperty =
AvaloniaProperty.Register<ThemedDatePicker, DateTime?>(
nameof(EndDate), defaultBindingMode: BindingMode.TwoWay);
public static readonly StyledProperty<string?> WatermarkProperty =
AvaloniaProperty.Register<ThemedDatePicker, string?>(nameof(Watermark), "Pick a date");
public static readonly StyledProperty<string?> DisplayTextProperty =
AvaloniaProperty.Register<ThemedDatePicker, string?>(nameof(DisplayText));
public static readonly StyledProperty<IBrush?> DisplayForegroundProperty =
AvaloniaProperty.Register<ThemedDatePicker, IBrush?>(nameof(DisplayForeground));
public static readonly StyledProperty<string?> TimeTextProperty =
AvaloniaProperty.Register<ThemedDatePicker, string?>(
nameof(TimeText), "09:00",
defaultBindingMode: BindingMode.TwoWay);
public DateTime? SelectedDate
{
get => GetValue(SelectedDateProperty);
set => SetValue(SelectedDateProperty, value);
}
public bool ShowTime
{
get => GetValue(ShowTimeProperty);
set => SetValue(ShowTimeProperty, value);
}
public bool IsRange
{
get => GetValue(IsRangeProperty);
set => SetValue(IsRangeProperty, value);
}
public DateTime? StartDate
{
get => GetValue(StartDateProperty);
set => SetValue(StartDateProperty, value);
}
public DateTime? EndDate
{
get => GetValue(EndDateProperty);
set => SetValue(EndDateProperty, value);
}
public string? Watermark
{
get => GetValue(WatermarkProperty);
set => SetValue(WatermarkProperty, value);
}
public string? DisplayText
{
get => GetValue(DisplayTextProperty);
set => SetValue(DisplayTextProperty, value);
}
public IBrush? DisplayForeground
{
get => GetValue(DisplayForegroundProperty);
set => SetValue(DisplayForegroundProperty, value);
}
public string? TimeText
{
get => GetValue(TimeTextProperty);
set => SetValue(TimeTextProperty, value);
}
private static readonly string[] TimeFormats = { @"h\:mm", @"hh\:mm" };
private DateTime _displayMonth;
private bool _suppressTimeSync;
public ThemedDatePicker()
{
InitializeComponent();
_displayMonth = new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1);
BuildWeekdayHeaders();
BuildDayGrid();
UpdateDisplayText();
UpdateTimeRowVisibility();
PickerPopup.Opened += OnPopupOpened;
}
private void UpdateTimeRowVisibility()
{
if (TimeRow is null) return;
TimeRow.IsVisible = ShowTime && !IsRange;
}
private void OnPopupOpened(object? sender, EventArgs e)
{
var seed = AnchorDate() ?? DateTime.Today;
_displayMonth = new DateTime(seed.Year, seed.Month, 1);
BuildDayGrid();
}
private DateTime? AnchorDate() =>
IsRange ? (StartDate ?? EndDate) : SelectedDate;
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (change.Property == SelectedDateProperty)
{
UpdateDisplayText();
SyncTimeFromSelected();
BuildDayGrid();
}
else if (change.Property == StartDateProperty || change.Property == EndDateProperty)
{
UpdateDisplayText();
BuildDayGrid();
}
else if (change.Property == ShowTimeProperty || change.Property == WatermarkProperty
|| change.Property == IsRangeProperty)
{
UpdateDisplayText();
BuildDayGrid();
UpdateTimeRowVisibility();
}
else if (change.Property == TimeTextProperty)
{
ApplyTimeTextToSelected();
}
}
private void UpdateDisplayText()
{
if (IsRange)
{
var (s, end) = NormalizeRange(StartDate?.Date, EndDate?.Date);
if (s is null && end is null)
{
DisplayText = Watermark ?? "Pick a range";
DisplayForeground = TryGetBrush("TextDimBrush");
return;
}
if (s is not null && end is null)
{
DisplayText = $"{s.Value:MMM d} select end";
DisplayForeground = TryGetBrush("TextBrush");
return;
}
// both set
var sd = s!.Value;
var ed = end!.Value;
DisplayText = sd.Year == ed.Year
? $"{sd:MMM d} {ed:MMM d, yyyy}"
: $"{sd:MMM d, yyyy} {ed:MMM d, yyyy}";
DisplayForeground = TryGetBrush("TextBrush");
return;
}
if (SelectedDate is null)
{
DisplayText = Watermark ?? "Pick a date";
DisplayForeground = TryGetBrush("TextDimBrush");
return;
}
var d = SelectedDate.Value;
DisplayText = ShowTime
? d.ToString("MMM d, yyyy · HH:mm", CultureInfo.CurrentCulture)
: d.ToString("MMM d, yyyy", CultureInfo.CurrentCulture);
DisplayForeground = TryGetBrush("TextBrush");
}
private static (DateTime? Start, DateTime? End) NormalizeRange(DateTime? a, DateTime? b)
{
if (a is null && b is null) return (null, null);
if (a is null) return (b, b);
if (b is null) return (a, null);
return a.Value <= b.Value ? (a, b) : (b, a);
}
private IBrush? TryGetBrush(string key)
{
if (this.TryFindResource(key, out var v) && v is IBrush b) return b;
return null;
}
private void SyncTimeFromSelected()
{
if (SelectedDate is null) return;
_suppressTimeSync = true;
TimeText = SelectedDate.Value.ToString("HH:mm");
_suppressTimeSync = false;
}
private void ApplyTimeTextToSelected()
{
if (_suppressTimeSync || !ShowTime || IsRange || SelectedDate is null) return;
if (TryParseTime(TimeText, out var t))
{
var d = SelectedDate.Value.Date + t;
if (d != SelectedDate) SelectedDate = d;
}
}
private static bool TryParseTime(string? text, out TimeSpan ts)
{
ts = default;
if (string.IsNullOrWhiteSpace(text)) return false;
return TimeSpan.TryParseExact(text, TimeFormats, CultureInfo.InvariantCulture, out ts);
}
private void BuildWeekdayHeaders()
{
if (WeekdayHeaders is null) return;
WeekdayHeaders.Children.Clear();
var firstDow = CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek;
var names = CultureInfo.CurrentCulture.DateTimeFormat.AbbreviatedDayNames;
for (int i = 0; i < 7; i++)
{
var dow = (DayOfWeek)(((int)firstDow + i) % 7);
var name = names[(int)dow];
if (name.Length > 3) name = name.Substring(0, 3);
WeekdayHeaders.Children.Add(new TextBlock { Text = name, Classes = { "weekday" } });
}
}
private void BuildDayGrid()
{
if (DayGrid is null || MonthHeader is null) return;
DayGrid.Children.Clear();
MonthHeader.Text = _displayMonth.ToString("MMMM yyyy", CultureInfo.CurrentCulture);
var firstDow = CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek;
var offset = ((int)_displayMonth.DayOfWeek - (int)firstDow + 7) % 7;
var start = _displayMonth.AddDays(-offset);
var today = DateTime.Today;
var sel = SelectedDate?.Date;
var (rs, re) = NormalizeRange(StartDate?.Date, EndDate?.Date);
var rangeFill = TryGetBrush("AccentSoftBrush");
for (int i = 0; i < 42; i++)
{
var day = start.AddDays(i);
var cell = new Grid();
if (IsRange && rs.HasValue && re.HasValue && rs.Value != re.Value
&& day >= rs.Value && day <= re.Value)
{
Thickness margin;
if (day == rs.Value)
margin = new Thickness(19, 0, 0, 0);
else if (day == re.Value)
margin = new Thickness(0, 0, 19, 0);
else
margin = default;
cell.Children.Add(new Border
{
Background = rangeFill,
Margin = margin,
HorizontalAlignment = HorizontalAlignment.Stretch,
VerticalAlignment = VerticalAlignment.Stretch
});
}
var btn = new Button
{
Content = day.Day.ToString(CultureInfo.CurrentCulture),
Classes = { "day" },
Tag = day
};
if (day.Month != _displayMonth.Month) btn.Classes.Add("outside");
if (day == today) btn.Classes.Add("today");
var isSelected = IsRange
? (rs.HasValue && day == rs.Value) || (re.HasValue && day == re.Value)
: sel.HasValue && day == sel.Value;
if (isSelected) btn.Classes.Add("selected");
btn.Click += OnDayClick;
cell.Children.Add(btn);
DayGrid.Children.Add(cell);
}
}
private void OnDayClick(object? sender, RoutedEventArgs e)
{
if (sender is not Button { Tag: DateTime day }) return;
if (IsRange)
{
// State A: nothing or both set → start a fresh range
// State B: only start set → complete (or restart) range
if (StartDate is null || EndDate is not null)
{
StartDate = day.Date;
EndDate = null;
}
else
{
var s = StartDate.Value.Date;
if (day.Date < s)
{
StartDate = day.Date;
EndDate = s;
}
else
{
EndDate = day.Date;
}
}
BuildDayGrid();
return;
}
var time = TimeSpan.Zero;
if (ShowTime)
{
if (TryParseTime(TimeText, out var parsed)) time = parsed;
else if (SelectedDate is { } cur) time = cur.TimeOfDay;
}
else if (SelectedDate is { } cur) time = cur.TimeOfDay;
SelectedDate = day.Date + time;
if (!ShowTime) PickerPopup.IsOpen = false;
}
private void OnPrevMonthClick(object? sender, RoutedEventArgs e)
{
_displayMonth = _displayMonth.AddMonths(-1);
BuildDayGrid();
}
private void OnNextMonthClick(object? sender, RoutedEventArgs e)
{
_displayMonth = _displayMonth.AddMonths(1);
BuildDayGrid();
}
private void OnTodayClick(object? sender, RoutedEventArgs e) => SetQuickDate(DateTime.Today);
private void OnTomorrowClick(object? sender, RoutedEventArgs e) => SetQuickDate(DateTime.Today.AddDays(1));
private void OnNextMondayClick(object? sender, RoutedEventArgs e)
{
var today = DateTime.Today;
int delta = ((int)DayOfWeek.Monday - (int)today.DayOfWeek + 7) % 7;
if (delta == 0) delta = 7;
SetQuickDate(today.AddDays(delta));
}
private void OnClearClick(object? sender, RoutedEventArgs e)
{
if (IsRange)
{
StartDate = null;
EndDate = null;
}
else
{
SelectedDate = null;
}
PickerPopup.IsOpen = false;
}
private void SetQuickDate(DateTime date)
{
_displayMonth = new DateTime(date.Year, date.Month, 1);
if (IsRange)
{
StartDate = date.Date;
EndDate = date.Date;
BuildDayGrid();
PickerPopup.IsOpen = false;
return;
}
var time = TimeSpan.Zero;
if (ShowTime)
{
if (!TryParseTime(TimeText, out time))
time = SelectedDate?.TimeOfDay ?? new TimeSpan(9, 0, 0);
}
SelectedDate = date.Date + time;
BuildDayGrid();
if (!ShowTime) PickerPopup.IsOpen = false;
}
private void OnDoneClick(object? sender, RoutedEventArgs e)
{
ApplyTimeTextToSelected();
PickerPopup.IsOpen = false;
}
}

View File

@@ -3,6 +3,7 @@
xmlns:vm="using:ClaudeDo.Ui.ViewModels.Modals" xmlns:vm="using:ClaudeDo.Ui.ViewModels.Modals"
xmlns:settings="using:ClaudeDo.Ui.ViewModels.Modals.Settings" xmlns:settings="using:ClaudeDo.Ui.ViewModels.Modals.Settings"
xmlns:conv="using:ClaudeDo.Ui.Converters" xmlns:conv="using:ClaudeDo.Ui.Converters"
xmlns:ctl="using:ClaudeDo.Ui.Views.Controls"
x:Class="ClaudeDo.Ui.Views.Modals.SettingsModalView" x:Class="ClaudeDo.Ui.Views.Modals.SettingsModalView"
x:DataType="vm:SettingsModalViewModel" x:DataType="vm:SettingsModalViewModel"
Title="Settings" Title="Settings"
@@ -225,23 +226,23 @@
<Border BorderBrush="{DynamicResource LineBrush}" BorderThickness="1" <Border BorderBrush="{DynamicResource LineBrush}" BorderThickness="1"
CornerRadius="6" Padding="10,8" Margin="0,0,0,8" CornerRadius="6" Padding="10,8" Margin="0,0,0,8"
Background="{DynamicResource DeepBrush}"> Background="{DynamicResource DeepBrush}">
<Grid ColumnDefinitions="Auto,*,*,Auto,Auto,Auto,Auto" ColumnSpacing="8"> <Grid ColumnDefinitions="Auto,*,Auto,Auto,Auto,Auto" ColumnSpacing="8">
<CheckBox Grid.Column="0" IsChecked="{Binding Enabled, Mode=TwoWay}" VerticalAlignment="Center"/> <CheckBox Grid.Column="0" IsChecked="{Binding Enabled, Mode=TwoWay}" VerticalAlignment="Center"/>
<CalendarDatePicker Grid.Column="1" <ctl:ThemedDatePicker Grid.Column="1"
SelectedDate="{Binding StartDate, Mode=TwoWay, Converter={StaticResource DateOnlyToDateTime}}" IsRange="True"
VerticalAlignment="Center"/> StartDate="{Binding StartDate, Mode=TwoWay, Converter={StaticResource DateOnlyToDateTime}}"
<CalendarDatePicker Grid.Column="2" EndDate="{Binding EndDate, Mode=TwoWay, Converter={StaticResource DateOnlyToDateTime}}"
SelectedDate="{Binding EndDate, Mode=TwoWay, Converter={StaticResource DateOnlyToDateTime}}" Watermark="Pick a range"
VerticalAlignment="Center"/> VerticalAlignment="Center"/>
<TextBox Grid.Column="3" Width="64" <TextBox Grid.Column="2" Width="64"
Text="{Binding TimeOfDay, Mode=TwoWay, Converter={StaticResource TimeSpanToHhmm}}" Text="{Binding TimeOfDay, Mode=TwoWay, Converter={StaticResource TimeSpanToHhmm}}"
VerticalAlignment="Center"/> VerticalAlignment="Center"/>
<CheckBox Grid.Column="4" Content="MonFri" <CheckBox Grid.Column="3" Content="MonFri"
IsChecked="{Binding WorkdaysOnly, Mode=TwoWay}" VerticalAlignment="Center"/> IsChecked="{Binding WorkdaysOnly, Mode=TwoWay}" VerticalAlignment="Center"/>
<TextBlock Grid.Column="5" Text="{Binding LastRunLabel}" VerticalAlignment="Center" <TextBlock Grid.Column="4" Text="{Binding LastRunLabel}" VerticalAlignment="Center"
Foreground="{DynamicResource TextDimBrush}" FontSize="11" Foreground="{DynamicResource TextDimBrush}" FontSize="11"
MinWidth="80"/> MinWidth="80"/>
<Button Grid.Column="6" Content="✕" <Button Grid.Column="5" Content="✕"
Command="{Binding $parent[ItemsControl].((vm:SettingsModalViewModel)DataContext).Prime.RemoveScheduleCommand}" Command="{Binding $parent[ItemsControl].((vm:SettingsModalViewModel)DataContext).Prime.RemoveScheduleCommand}"
CommandParameter="{Binding}"/> CommandParameter="{Binding}"/>
</Grid> </Grid>