Просмотр исходного кода

Calendar now saves settings correctly and zoom functinoality added back in; Calendar sets date columns correctly, and header buttons work now

Kenric Nugteren 1 неделя назад
Родитель
Сommit
31dcb960b6

+ 39 - 2
prs.desktop/Components/Calendar/Appointments/AssignmentAppointment.cs

@@ -1,6 +1,10 @@
 using System;
 using System.Collections.Generic;
+using System.ComponentModel;
 using System.Drawing;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Windows;
 using System.Windows.Media.Imaging;
 using Comal.Classes;
 using Geocoding;
@@ -9,7 +13,7 @@ using InABox.WPF;
 
 namespace PRSDesktop
 {
-    public class AssignmentAppointment : CalendarAppointment<Assignment>
+    public class AssignmentAppointment : CalendarAppointment<Assignment>, INotifyPropertyChanged
     {
         public CalendarAssignmentType AssignmentType { get; }
 
@@ -59,7 +63,40 @@ namespace PRSDesktop
             if (model.Meeting.Link.ID != Guid.Empty)
                 images.Add(Resources.employees.Resize(32,32));
             Image = ImageUtils.MergeBitmaps(images, 10)?.AsBitmapImage();
+
+            WeakEventManager<Assignment, PropertyChangedEventArgs>.AddHandler(model, nameof(Assignment.PropertyChanged), Assignment_Changed);
+        }
+
+        private static Column<Assignment>[] StartColumns = [
+            new(x => x.Booked.Start),
+            new(x => x.Actual.Start),
+            ];
+        private static Column<Assignment>[] FinishColumns = [
+            new(x => x.Booked.Finish),
+            new(x => x.Actual.Finish),
+            ];
+
+        private void Assignment_Changed(object? sender, PropertyChangedEventArgs e)
+        {
+            if(StartColumns.Any(x => x.Property == e.PropertyName))
+            {
+                OnPropertyChanged(nameof(StartTime));
+            }
+            else if(FinishColumns.Any(x => x.Property == e.PropertyName))
+            {
+                OnPropertyChanged(nameof(EndTime));
+            }
+            else if(e.PropertyName == nameof(Assignment.Date))
+            {
+                OnPropertyChanged(nameof(Date));
+            }
+        }
+
+        public event PropertyChangedEventHandler? PropertyChanged;
+
+        private void OnPropertyChanged([CallerMemberName] string? propertyName = null)
+        {
+            PropertyChanged?.Invoke(this, new(propertyName));
         }
-        
     }
 }

+ 2 - 2
prs.desktop/Components/Calendar/Appointments/CalendarAppointment.cs

@@ -41,7 +41,8 @@ namespace PRSDesktop
     }
     
     [DoNotNotify]
-    public abstract class CalendarAppointment<T> : ICalendarAppointment<T> where T : notnull
+    public abstract class CalendarAppointment<T> : ICalendarAppointment<T>
+        where T : notnull
     {
         public Employee Employee { get; set; }
 
@@ -73,7 +74,6 @@ namespace PRSDesktop
             Background = new SolidColorBrush(c2);
             Foreground = new SolidColorBrush(ImageUtils.GetForegroundColor(c2));
         }
-        
     }
     
     public class CalendarAppointments : ScheduleAppointmentCollection //ObservableCollection<ICalendarAppointment> { }}

+ 1 - 3
prs.desktop/Components/Calendar/Appointments/StandardLeaveAppointment.cs

@@ -1,8 +1,6 @@
 using System;
-using System.Windows.Media.Imaging;
 using Comal.Classes;
 using InABox.WPF;
-using PRS.Shared;
 
 namespace PRSDesktop
 {
@@ -14,7 +12,7 @@ namespace PRSDesktop
         public override TimeSpan EndTime => Block.Finish;
 
         public override DateTime Date => Block.Date;
-        
+
         public StandardLeaveAppointment(StandardLeave leave, Employee employee, RosterBlock block)
             : base(leave, employee, x => x.LeaveType.Color)
         {

+ 114 - 140
prs.desktop/Components/Calendar/Calendar.xaml

@@ -10,8 +10,8 @@
              xmlns:dynamicGrid="clr-namespace:InABox.DynamicGrid;assembly=InABox.Wpf"
              xmlns:system="clr-namespace:System;assembly=System.Runtime.InteropServices"
              mc:Ignorable="d"
-             d:DesignHeight="600" d:DesignWidth="800" SizeChanged="Calendar_OnSizeChanged"
-             Name="Main">
+             d:DesignHeight="600" d:DesignWidth="800"
+             x:Name="Main">
     
     <UserControl.Resources>
 
@@ -43,147 +43,122 @@
                     <RowDefinition Height="Auto"/>
                     <RowDefinition Height="*"/>
                 </Grid.RowDefinitions>
-                <DockPanel Grid.Row="0" LastChildFill="False"
-                           Margin="0,0,0,4"
-                           Visibility="{Binding HeaderVisibility, ElementName=Main}">
-                    <Button DockPanel.Dock="Left"
-                            MinWidth="25" MinHeight="25"
-                            Margin="0,0,4,0">
-                        <Polyline Points="6,0 0,6 6,12"
-                                  Stroke="Black"
-                                  StrokeThickness="1"/>
-                    </Button>
-                    <Button DockPanel.Dock="Left"
-                            MinWidth="25" MinHeight="25"
-                            Margin="0,0,4,0">
-                        <Polyline Points="0,0 6,6 0,12"
-                                  Stroke="Black"
-                                  StrokeThickness="1"/>
-                    </Button>
-                    <DatePicker DockPanel.Dock="Left"
-                                SelectedDate="{Binding ElementName=Main,Path=SelectedDate}"/>
-                    <Button DockPanel.Dock="Right"
-                            Content="Today"
-                            MinHeight="25"
-                            Margin="4,0,0,0"/>
-                </DockPanel>
-                <wpf:CalendarControl Name="CalendarControl"
-                                     RowHeight="0"
-                                     MinimumColumnWidth="75"
-                                     Grid.Row="1"
-                                     BlockClicked="Calendar_BlockClicked"
-                                     BlockHeld="Calendar_BlockHeld"
-                                     BlockRightClicked="Calendar_BlockRightClicked"
-                                     ColumnMapping="{Binding Employee}"
-                                     DateMapping="{Binding Date}"
-                                     StartTimeMapping="{Binding StartTime}"
-                                     EndTimeMapping="{Binding EndTime}">
-                    <wpf:CalendarControl.DateTemplate>
-                        <DataTemplate DataType="system:DateTime">
-                            <StackPanel Background="WhiteSmoke"
-                                        Margin="0"
-                                        VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
-                                        Orientation="Vertical">
-                                <Label HorizontalContentAlignment="Center" VerticalContentAlignment="Bottom"
-                                       FontSize="12" FontFamily="Arial" FontWeight="Bold"
-                                       Foreground="Black"
-                                       Margin="0,0,0,-5"
-                                       Content="{Binding Converter={x:Static local:Calendar.DayOfWeekConverter}}"/>
-                                <Label HorizontalContentAlignment="Center" VerticalContentAlignment="Top"
-                                       FontSize="10"
-                                       Content="{Binding StringFormat='dd MMM yy'}"/>
-                            </StackPanel>
-                        </DataTemplate>
-                    </wpf:CalendarControl.DateTemplate>
-                    <wpf:CalendarControl.HeaderTemplate>
-                        <DataTemplate DataType="classes:Employee">
-                            <Border Background="White"
-                                    Height="50">
-                                <TextBlock VerticalAlignment="Center"
-                                           HorizontalAlignment="Stretch"
-                                           TextAlignment="Center"
-                                           Tag="{Binding}"
-                                           SizeChanged="TextBlock_SizeChanged"/>
-                            </Border>
-                        </DataTemplate>
-                    </wpf:CalendarControl.HeaderTemplate>
-                    <wpf:CalendarControl.ItemTemplate>
-                        <DataTemplate DataType="local:ICalendarAppointment">
-                            <Border BorderBrush="Black" BorderThickness="1"
-                                    Margin="2" Padding="2" CornerRadius="2"
-                                    Background="{Binding Background}">
-                                <local:CalendarBlock DataContext="{Binding}" />
-                            </Border>
-                        </DataTemplate>
-                    </wpf:CalendarControl.ItemTemplate>
-                </wpf:CalendarControl>
-            </Grid>
-            <!--local:SfScheduler2
-                x:Name="Bookings"
-                HeaderDateFormat="dd MMMM yyyy"
-                SizeChanged="Schedule_SizeChanged"
-                AppointmentEditorOpening="Schedule_AppointmentEditorOpening"
-                SchedulerContextMenuOpening="Bookings_OnSchedulerContextMenuOpening"
-                AppointmentTapped="Bookings_OnAppointmentTapped"
-                QueryAppointments="Bookings_OnQueryAppointments"
-                ViewType="Day"
-                BorderThickness="1"
-                BorderBrush="Silver"
-                Background="Silver"
-                ResourceGroupType="Date"
-                FirstDayOfWeek="Monday"
-                >
-                
-                <- <syncfusion:SfScheduler.HeaderTemplate> ->
-                <-     <DataTemplate> ->
-                <-         <Border BorderBrush="Transparent" Background="Transparent" Height="0" Width="200"/> ->
-                <-     </DataTemplate> ->
-                <- </syncfusion:SfScheduler.HeaderTemplate> ->
-                
-                <syncfusion:SfScheduler.CellContextMenu>
-                    <ContextMenu />
-                </syncfusion:SfScheduler.CellContextMenu>
-                        
-                <syncfusion:SfScheduler.AppointmentContextMenu>
-                    <ContextMenu />
-                </syncfusion:SfScheduler.AppointmentContextMenu>
-                
-                <syncfusion:SfScheduler.DaysViewSettings>
-
-                    <syncfusion:DaysViewSettings ViewHeaderDateFormat="dd MMM yy" ViewHeaderDayFormat="dddd"
-                                                 ViewHeaderHeight="45" TimeRulerFormat="hh:mm tt" TimeInterval="0:30"
-                                                 MinimumAllDayAppointmentsCount="0">
+                <Border Grid.Row="0" Margin="0,0,0,2"
+                        CornerRadius="5,5,0,0"
+                        BorderThickness="0.75" BorderBrush="Gray"
+                        Background="WhiteSmoke"
+                        Padding="5"
+                        Visibility="{Binding HeaderVisibility}">
+                    <DockPanel LastChildFill="False">
+                        <Button DockPanel.Dock="Left"
+                                MinWidth="25" MinHeight="25"
+                                Margin="0,0,4,0"
+                                Click="Left_Click">
+                            <Polyline Points="6,0 0,6 6,12"
+                                      Stroke="Black"
+                                      StrokeThickness="1"/>
+                        </Button>
+                        <Button DockPanel.Dock="Left"
+                                MinWidth="25" MinHeight="25"
+                                Margin="0,0,4,0"
+                                Click="Right_Click">
+                            <Polyline Points="0,0 6,6 0,12"
+                                      Stroke="Black"
+                                      StrokeThickness="1"/>
+                        </Button>
+                        <DatePicker DockPanel.Dock="Left"
+                                    SelectedDate="{Binding SelectedDate, Mode=TwoWay}"
+                                    VerticalContentAlignment="Center"/>
+                        <Button DockPanel.Dock="Right"
+                                Content="Today"
+                                MinHeight="25"
+                                Margin="4,0,0,0" Padding="5"
+                                Click="Today_Click"/>
+                    </DockPanel>
+                </Border>
 
-                        <syncfusion:DaysViewSettings.ViewHeaderTemplate>
-                            <DataTemplate>
-                                <StackPanel Background="WhiteSmoke" Margin="-10,0,0,0" VerticalAlignment="Stretch"
-                                            HorizontalAlignment="Stretch" Orientation="Vertical">
-                                    <Label HorizontalContentAlignment="Center" Content="{Binding DayText}" FontSize="12"
-                                           Foreground="Black" FontFamily="Arial" FontWeight="Bold"
-                                           VerticalContentAlignment="Bottom" Margin="0,0,0,-5" />
-                                    <Label HorizontalContentAlignment="Center" Content="{Binding DateText}" FontSize="10"
-                                           VerticalContentAlignment="Top" />
+                <Border Grid.Row="1" BorderBrush="LightGray" BorderThickness="1">
+                    <wpf:CalendarControl Name="CalendarControl"
+                                         RowHeight="0"
+                                         MinimumBlockHeight="10"
+                                         MinimumColumnWidth="75"
+                                         BlockClicked="Calendar_BlockClicked"
+                                         BlockHeld="Calendar_BlockHeld"
+                                         BlockRightClicked="Calendar_BlockRightClicked"
+                                         ColumnWidthMode="ConstantColumns"
+                                         ColumnMapping="{Binding Employee}"
+                                         DateMapping="{Binding Date}"
+                                         StartTimeMapping="{Binding StartTime}"
+                                         EndTimeMapping="{Binding EndTime}">
+                        <wpf:CalendarControl.DateTemplate>
+                            <DataTemplate DataType="system:DateTime">
+                                <StackPanel Background="WhiteSmoke"
+                                            Margin="0"
+                                            VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
+                                            Orientation="Vertical">
+                                    <Label HorizontalContentAlignment="Center" VerticalContentAlignment="Bottom"
+                                           FontSize="12" FontFamily="Arial" FontWeight="Bold"
+                                           Foreground="Black"
+                                           Margin="0,0,0,-5"
+                                           Content="{Binding Converter={x:Static local:Calendar.DayOfWeekConverter}}"/>
+                                    <Label HorizontalContentAlignment="Center" VerticalContentAlignment="Top"
+                                           FontSize="10"
+                                           ContentStringFormat="{}{0:dd MMM yy}"
+                                           Content="{Binding}"/>
                                 </StackPanel>
                             </DataTemplate>
-                        </syncfusion:DaysViewSettings.ViewHeaderTemplate>
-
-                        <syncfusion:DaysViewSettings.AppointmentTemplate>
-                            <DataTemplate>
-                                <Border BorderThickness="0" Background="{Binding AppointmentBackground}" Padding="2" CornerRadius="2">
-                        
-                                    <!- <Border.ToolTip> ->
-                                    <!-     <local:CalendarBlock DataContext="{Binding}" /> ->
-                                    <!- </Border.ToolTip> ->
-                                    
-                                    <local:CalendarBlock DataContext="{Binding}" />
-                        
+                        </wpf:CalendarControl.DateTemplate>
+                        <wpf:CalendarControl.HeaderTemplate>
+                            <DataTemplate DataType="classes:Employee">
+                                <Border Background="White"
+                                        Height="50">
+                                    <TextBlock VerticalAlignment="Center"
+                                               HorizontalAlignment="Stretch"
+                                               TextAlignment="Center"
+                                               Tag="{Binding}"
+                                               SizeChanged="TextBlock_SizeChanged"/>
                                 </Border>
                             </DataTemplate>
-                        </syncfusion:DaysViewSettings.AppointmentTemplate>
+                        </wpf:CalendarControl.HeaderTemplate>
+                        <wpf:CalendarControl.ItemTemplate>
+                            <DataTemplate DataType="local:ICalendarAppointment">
+                                <Border BorderBrush="Black" BorderThickness="1"
+                                        Margin="2" Padding="2" CornerRadius="2"
+                                        Background="{Binding Background}">
+                                    <Grid>
+                                        <Grid.ColumnDefinitions>
+                                            <ColumnDefinition Width="*" />
+                                        </Grid.ColumnDefinitions>
+                                        <Grid.RowDefinitions>
+                                            <RowDefinition Height="20" />
+                                            <RowDefinition Height="*" />
+                                        </Grid.RowDefinitions>
 
-                    </syncfusion:DaysViewSettings>
-                </syncfusion:SfScheduler.DaysViewSettings>
-            </local:SfScheduler2-->
+                                        <DockPanel Grid.Row="0" Grid.Column="0">
+                                            <Image Source="{Binding Image}"
+                                                   DockPanel.Dock="Right"
+                                                   VerticalAlignment="Center"/>
+                                            
+                                            <TextBlock Text="{Binding Subject}"
+                                                       Foreground="{Binding Foreground}" 
+                                                       DockPanel.Dock="Left"
+                                                       VerticalAlignment="Center" 
+                                                       FontWeight="DemiBold"/>
+                                        </DockPanel>
+
+                                        <TextBlock Grid.Row="1"
+                                                   Grid.Column="0"
+                                                   HorizontalAlignment="Left"
+                                                   Text="{Binding Notes}"
+                                                   TextWrapping="Wrap"
+                                                   Foreground="{Binding Foreground}"/>
+                                    </Grid>
+                                </Border>
+                            </DataTemplate>
+                        </wpf:CalendarControl.ItemTemplate>
+                    </wpf:CalendarControl>
+                </Border>
+            </Grid>
         </dynamicGrid:DynamicSplitPanel.Master>
         
         
@@ -228,7 +203,7 @@
                     <ComboBox 
                         x:Name="CalendarViewSelector" 
                         DockPanel.Dock="Left" 
-                        SelectedValue="{Binding CalendarView, ElementName=Main}"
+                        SelectedValue="{Binding CalendarView}"
                         VerticalContentAlignment="Center" 
                         SelectedIndex="-1"
                         Margin="5,0,0,0"/>
@@ -378,6 +353,7 @@
                         <Image Source="pack://application:,,,/Resources/rightarrow.png" Height="20" Width="20" />
                     </Button>
                     <TextBox x:Name="ZoomSelector" IsReadOnly="True" DockPanel.Dock="Left" BorderThickness="0,0.75,0,0.75"
+                             Text="100%"
                              BorderBrush="Gray" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" />
                 </DockPanel>
 
@@ -391,8 +367,6 @@
                     <Label Content="Always Start On Today" DockPanel.Dock="Left"
                            VerticalContentAlignment="Center"/>
                 </DockPanel>
-
-
             </Grid>
             
        </dynamicGrid:DynamicSplitPanel.Detail>

+ 74 - 207
prs.desktop/Components/Calendar/Calendar.xaml.cs

@@ -26,42 +26,6 @@ using SelectionChangedEventArgs = System.Windows.Controls.SelectionChangedEventA
 
 namespace PRSDesktop
 {
-
-    public class SfScheduler2 : SfScheduler
-    {
-
-        public static readonly DependencyProperty HeaderVisibilityProperty =
-            DependencyProperty.Register(
-                nameof(HeaderVisibility),
-                typeof(Visibility),
-                typeof(SfScheduler2),
-                new UIPropertyMetadata(Visibility.Collapsed)
-            );
-
-        public Visibility HeaderVisibility
-        {
-            get => (Visibility)GetValue(HeaderVisibilityProperty);
-            set
-            {
-                SetValue(HeaderVisibilityProperty,value);
-                SetHeaderVisibility();
-            }
-
-        }
-
-        private void SetHeaderVisibility()
-        {
-            if (GetTemplateChild("PART_ScheduleHeaderControl") is SchedulerHeaderControl cell)
-                cell.Visibility = HeaderVisibility;
-        }
-
-        public override void OnApplyTemplate()
-        {
-            base.OnApplyTemplate();
-            SetHeaderVisibility();
-        }
-    }
-    
     public partial class Calendar
     {
 
@@ -133,6 +97,7 @@ namespace PRSDesktop
             if (!EventSuppressor.IsSet(Suppress.Settings))
             {
                 calendar.Properties.SettingsVisible = value;
+                calendar.DoSaveSettings();
             }
 
             calendar._splitPanel.View = value == CalendarSettingsVisibility.Visible
@@ -174,6 +139,7 @@ namespace PRSDesktop
             if (!EventSuppressor.IsSet(Suppress.Settings))
             {
                 calendar.Properties.CalendarView = calendar.CalendarView;
+                calendar.DoSaveSettings();
             }
 
             calendar.Refresh();
@@ -204,6 +170,7 @@ namespace PRSDesktop
             if (!EventSuppressor.IsSet(Suppress.Settings))
             {
                 calendar.Properties.EmployeeSelection = calendar.EmployeeSelection;
+                calendar.DoSaveSettings();
             }
 
             calendar.EmployeeSelector.Selection = calendar.EmployeeSelection;
@@ -238,6 +205,7 @@ namespace PRSDesktop
             if (!EventSuppressor.IsSet(Suppress.Settings))
             {
                 calendar.Properties.EmployeeSelector = calendar.EmployeeSettings;
+                calendar.DoSaveSettings();
             }
             calendar.EmployeeSelector.Settings = calendar.EmployeeSettings;
         }
@@ -280,11 +248,11 @@ namespace PRSDesktop
             if (!EventSuppressor.IsSet(Suppress.Settings))
             {
                 calendar.Properties.TimeInterval = calendar.TimeInterval;
+                calendar.DoSaveSettings();
             }
             calendar.IntervalSelector.SelectedIndex = (int)calendar.TimeInterval;
 
             calendar.CalendarControl.RowInterval = TimeIntervalToTimeSpan(calendar.TimeInterval);
-            calendar.UpdateZoom();
         }
 
         private void IntervalSelector_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
@@ -333,6 +301,7 @@ namespace PRSDesktop
             if (!EventSuppressor.IsSet(Suppress.Settings))
             {
                 calendar.Properties.Date = calendar.SelectedDate;
+                calendar.DoSaveSettings();
             }
 
             calendar.Refresh();
@@ -380,10 +349,10 @@ namespace PRSDesktop
             if (!EventSuppressor.IsSet(Suppress.Settings))
             {
                 calendar.Properties.StartHour = calendar.StartHour;
+                calendar.DoSaveSettings();
             }
             calendar.StartTimeSelector.Text = FormatHour(calendar.StartHour);
             calendar.CalendarControl.StartHour = TimeSpan.FromHours(calendar.StartHour);
-            calendar.UpdateZoom();
         }
         
         private void StartTimeSelector_Down_Click(object sender, RoutedEventArgs e)
@@ -434,10 +403,10 @@ namespace PRSDesktop
             if (!EventSuppressor.IsSet(Suppress.Settings))
             {
                 calendar.Properties.EndHour = calendar.EndHour;
+                calendar.DoSaveSettings();
             }
             calendar.FinishTimeSelector.Text = FormatHour(calendar.EndHour);
             calendar.CalendarControl.EndHour = TimeSpan.FromHours(calendar.EndHour);
-            calendar.UpdateZoom();
         }
         
         private void FinishTimeSelector_Down_Click(object sender, RoutedEventArgs e)
@@ -490,6 +459,7 @@ namespace PRSDesktop
             if (!EventSuppressor.IsSet(Suppress.Settings))
             {
                 calendar.Properties.AssignmentType = calendar.AssignmentType;
+                calendar.DoSaveSettings();
             }
             calendar.AssignmentTypeSelector.SelectedIndex = (int)calendar.AssignmentType;
             calendar.Refresh();
@@ -530,6 +500,7 @@ namespace PRSDesktop
             if (!EventSuppressor.IsSet(Suppress.Settings))
             {
                 calendar.Properties.BackgroundType = calendar.BackgroundType;
+                calendar.DoSaveSettings();
             }
             calendar.BackgroundTypeSelector.SelectedIndex = (int)calendar.BackgroundType;
             calendar.Refresh();
@@ -552,33 +523,27 @@ namespace PRSDesktop
                 nameof(Zoom), 
                 typeof(double), 
                 typeof(Calendar), 
-                new UIPropertyMetadata((double)100F)
+                new UIPropertyMetadata(100.0, Zoom_Changed, Zoom_Coerce)
             );
-        
+
+        private static object Zoom_Coerce(DependencyObject d, object baseValue)
+        {
+            if (baseValue is not double zoom) return baseValue;
+            return Math.Max(zoom, 100.0);
+        }
+
         public double Zoom
         {
             get => (double)GetValue(ZoomProperty);
             set => SetValue(ZoomProperty, value);
         }
 
-        // private void SetZoom(double value)
-        // {
-        //     DoSetValue(
-        //         ZoomProperty, 
-        //         value,
-        //         () => ZoomSelector.Text = $"{value:F0}%",
-        //         () =>  UpdateZoom()
-        //     );
-        // }
-
-        private void UpdateZoom()
+        private static void Zoom_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
         {
-            // TODO: Remove this method.
+            if (d is not Calendar calendar) return;
 
-            // if (double.IsNaN(ActualHeight) || (ActualHeight == 0.0F))
-            //     return;
-            // var blocksize = (ActualHeight - (Bookings.DaysViewSettings.ViewHeaderHeight + Bookings.DaysViewSettings.ResourceHeaderSize + 2.0F)) / ((EndHour - StartHour) * this.BlocksPerHour(TimeInterval));
-            // Calendar.RowHeight = (double)Zoom * blocksize / 100.0F;
+            calendar.CalendarControl.Zoom = calendar.Zoom / 100;
+            calendar.ZoomSelector.Text = $"{calendar.Zoom:F0}%";
         }
 
         private void ZoomSelector_Down_Click(object sender, RoutedEventArgs e)
@@ -654,6 +619,7 @@ namespace PRSDesktop
             using (EventSuppressor.All<Suppress>())
             {
                 InitializeComponent();
+                _splitPanel.DataContext = this;
 
                 StartHour = 0;
                 EndHour = 24;
@@ -691,7 +657,6 @@ namespace PRSDesktop
                 
                 var query = new MultiQuery();
                 
-                // TODO: check columns
                 query.Add(
                     Filter<LeaveRequest>.Where(x =>x.Status).IsNotEqualTo(LeaveRequestStatus.Rejected), 
                     Columns.None<LeaveRequest>()
@@ -742,6 +707,11 @@ namespace PRSDesktop
         public virtual void Shutdown(CancelEventArgs? cancel)
         {
         }
+
+        private void DoSaveSettings()
+        {
+            SaveSettings?.Invoke(this, Properties);
+        }
         
         private bool bFirst = true;
         
@@ -812,6 +782,7 @@ namespace PRSDesktop
  
                 try
                 {
+                    CalendarControl.Dates = CoreUtils.Range(startDate, endDate, x => x.AddDays(1));
                     CalendarControl.ItemsSource = appointments;
                 }
                 catch (Exception e)
@@ -978,6 +949,10 @@ namespace PRSDesktop
                             var finish = !timesheet.Approved.IsEmpty()
                                 ? timesheet.ApprovedFinish
                                 : timesheet.Finish;
+                            if(finish == default)
+                            {
+                                finish = TimeSpan.FromHours(24);
+                            }
                             regions.Add(new CalendarRegion
                             {
                                 Date = date,
@@ -1074,59 +1049,6 @@ namespace PRSDesktop
             return new AutoDataModel<Assignment>(Filter<Assignment>.Where(x => x.ID).InList(ids));
         }
 
-        // private void ResizeIntervals(double height)
-        // {
-        //     if (Bookings.FindVisualChildren<ScrollPanel>().Any())
-        //     {
-        //         
-        //         if (height > 95 && Bookings.DaysViewSettings.EndHour - Bookings.DaysViewSettings.StartHour > 0)
-        //         {
-        //             double scrollheight = _employees.Length * 75 > Bookings.ActualWidth ? 15.0F : 0.0F;
-        //             var header = _employees.Length > 1 ? 93.0F + scrollheight : 50.0F;
-        //             Bookings.DaysViewSettings.TimeIntervalSize =
-        //                 (height - header) / ((Bookings.DaysViewSettings.EndHour - Bookings.DaysViewSettings.StartHour) * 2.0F);
-        //         }
-        //     }
-        //     
-        //     // var scrollers = Bookings.FindVisualChildren<ScrollViewer>().Where(x => string.Equals(x.Name, "PART_TimeSlotScrollViewer")).ToArray();
-        //     //     foreach (var scroll in scrollers)
-        //     //         scroll.VerticalScrollBarVisibility = ScrollBarVisibility.Hidden;
-        //
-        // }
-
-        // TODO: Shorten employee name if column too small.
-
-        // private void ResizeColumns(double width)
-        // {
-        //     if (double.IsNaN(width) || !Bookings.FindVisualChildren<ScrollPanel>().Any())
-        //         return;
-
-        //     var maxcount = (int)width / 75;
-        //     Bookings.DaysViewSettings.VisibleResourceCount = Math.Min(maxcount,
-        //         _employees.Length * (Bookings.ViewType == SchedulerViewType.Day ? 1 : Bookings.ViewType == SchedulerViewType.WorkWeek ? 5 : 7));
-        //     
-        //     if (Bookings.ResourceCollection is ObservableCollection<SchedulerResource> resources)
-        //     {
-        //         var colwidth = GetResourceColumnWidth();
-        //         foreach (var emp in _employees)
-        //         {
-        //             var resource = resources.FirstOrDefault(x => String.Equals(x.Id?.ToString(), emp.ID.ToString()));
-        //             if (resource != null)
-        //             {
-        //                 var comps = emp.Name.Split(' ');
-        //                 var display = emp.Name;
-        //                 if (colwidth < 75)
-        //                     display = string.Format("{0}{1}", comps[0].Length > 0 ? comps[0][..1] : "",
-        //                         comps.Length > 1 ? comps.Skip(1).First()[..1].ToUpper() : "");
-        //                 else if (colwidth < 150)
-        //                     display = string.Format("{0} {1}", comps.First(),
-        //                         comps.Length > 1 ? comps.Skip(1).First()[..1].ToUpper() : "");
-        //                 resource.Name = display;
-        //             }
-        //         }
-        //     }
-        // }
-
         private static T CheckGrid<T>([NotNull] ref T? grid) where T : new()
         {
             grid ??= new T();
@@ -1135,84 +1057,10 @@ namespace PRSDesktop
 
         private void ReloadColumns()
         {
-            // var colwidth = GetResourceColumnWidth();
-
             CalendarControl.Columns = _employees.OrderBy(x => x.Name);
-
-            /*
-            var resources = new List<SchedulerResource>();
-            foreach (var emp in _employees)
-            {
-                var comps = emp.Name.Split(' ');
-                var display = emp.Name;
-                if (colwidth < 75)
-                    display = CoreUtils.Codify(emp.Name);
-                else if (colwidth < 150)
-                    display = string.Format("{0} {1}", comps.Length > 0 ? comps.First() : "", comps.Length > 1 ? comps.Skip(1).First().Substring(0, 1).ToUpper() : "");
-                resources.Add(new SchedulerResource { Name = display, Id = emp.ID.ToString() });
-            }
-
-            var sorted = new ObservableCollection<SchedulerResource>();
-            foreach (var resource in resources.OrderBy(x => x.Name))
-                sorted.Add(resource);
-            try
-            {
-                Bookings.DaysViewSettings.ResourceHeaderSize = sorted.Count <= 1 ? 0 : 45;
-            }
-            catch (Exception e)
-            {
-                Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
-            }
-
-            //ResizeIntervals(Bookings.ActualHeight);
-            try
-            {
-                Bookings.ResourceCollection = sorted;
-            }
-            catch (Exception e)
-            {
-                Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
-            }*/
-
             bColumnsLoaded = true;
         }
 
-        // private SchedulerResource GetCurrentResource()
-        // {
-        //     var p = Mouse.GetPosition(Bookings);
-        //     var panels = Bookings.FindVisualChildren<ScrollViewer>().FirstOrDefault(x => string.Equals(x.Name, "PART_ViewHeaderScrollViewer"));
-        //     var resource = (int)((p.X + panels.HorizontalOffset - (Bookings.DaysViewSettings.TimeRulerSize + 20F)) / GetResourceColumnWidth());
-        //     return (Bookings.ResourceCollection as Collection<SchedulerResource>)[resource];
-        // }
-        //
-        // private DateTime GetCurrentTime()
-        // {
-        //     var p = Mouse.GetPosition(Bookings);
-        //     var hours = (p.Y - (_employees.Length > 1 ? 95.0F : 50.0F)) / (Bookings.DaysViewSettings.TimeIntervalSize * 2F) +
-        //                 Bookings.DaysViewSettings.StartHour;
-        //     var result = Bookings.SelectedDate.Value + TimeSpan.FromHours(hours);
-        //     return result;
-        // }
-
-        // private double GetResourceColumnWidth()
-        // {
-        //     var colcount = Math.Max(1, _employees.Length);
-        //     colcount = colcount * (Bookings.ViewType == SchedulerViewType.Day ? 1 : Bookings.ViewType == SchedulerViewType.Day ? 5 : 7);
-        //     var colwidth = Bookings.ActualWidth / colcount;
-        //     var minwidth = (Bookings.ActualWidth - (Bookings.DaysViewSettings.TimeRulerSize + 20F)) / Bookings.DaysViewSettings.VisibleResourceCount;
-        //     return Math.Max(minwidth, colwidth);
-        // }
-        
-
-        // private void Schedule_AppointmentEditorOpening(object sender, AppointmentEditorOpeningEventArgs e)
-        // {
-        //     e.Cancel = true;
-        // }
-        
-        
-        //private Dictionary<CalendarMenuName, MenuItem> _menuitems = new Dictionary<CalendarMenuName, MenuItem>();
-        
-        
         private void Calendar_BlockHeld(object sender, CalendarBlockEventArgs e)
         {
             Calendar_BlockRightClicked(sender, e);
@@ -1254,7 +1102,7 @@ namespace PRSDesktop
                 e.Menu.AddItem("Zoom Out", null, ZoomOut);
                 e.Menu.AddItem("Reset Zoom", null, ResetZoom);
                 
-                CustomiseContextMenu?.Invoke(e.Menu, new CalendarDataEventArgs<Assignment>(appointment.Model));
+                CustomiseContextMenu?.Invoke(e.Menu, new CalendarDataEventArgs(appointment.Model));
             }
             else if(e.Value is null)
             {
@@ -1277,7 +1125,7 @@ namespace PRSDesktop
                 e.Menu.AddItem("Zoom Out", null, ZoomOut);
                 e.Menu.AddItem("Reset Zoom", null, ResetZoom);
 
-                CustomiseContextMenu?.Invoke(e.Menu, new CalendarDataEventArgs<CalendarTimeSlot>(slot));
+                CustomiseContextMenu?.Invoke(e.Menu, new CalendarDataEventArgs(slot));
             }
         }
         
@@ -1301,7 +1149,7 @@ namespace PRSDesktop
             meeting.Time.Start = slot.Start;
             meeting.Time.Finish = slot.End;
             
-            ItemCreated?.Invoke(this, new CalendarDataEventArgs<Meeting>(meeting));
+            ItemCreated?.Invoke(this, new CalendarDataEventArgs(meeting));
             
             var args = new CalendarHandledEventArgs<Meeting>(meeting);
             ItemEditing?.Invoke(this, args);
@@ -1354,7 +1202,7 @@ namespace PRSDesktop
             
             if (DynamicGridUtils.EditEntity<Meeting>(model.Meeting.Link.ID, out var meeting))
             {
-                ItemChanged?.Invoke(this, new CalendarDataEventArgs<Meeting>(meeting));
+                ItemChanged?.Invoke(this, new CalendarDataEventArgs(meeting));
                 Refresh();
             }
         }
@@ -1366,9 +1214,9 @@ namespace PRSDesktop
             
             var meeting = new Meeting { ID = model.Meeting.Link.ID };
             Client.Delete(meeting, "Meeting Deleted from Scheduler");
-            ItemChanged?.Invoke(this, new CalendarDataEventArgs<Meeting>(meeting));
+            ItemChanged?.Invoke(this, new CalendarDataEventArgs(meeting));
             Refresh();
-            SelectionChanged?.Invoke(this, new CalendarDataEventArgs<Meeting>(null));
+            SelectionChanged?.Invoke(this, new CalendarDataEventArgs(null));
         }
         
         public Assignment? CreateAssignment(CalendarTimeSlot slot)
@@ -1388,7 +1236,7 @@ namespace PRSDesktop
             
             ass.EmployeeLink.ID = slot.EmployeeID;
 
-            ItemCreated?.Invoke(this, new CalendarDataEventArgs<Assignment>(ass));
+            ItemCreated?.Invoke(this, new CalendarDataEventArgs(ass));
             
             var args = new CalendarHandledEventArgs<Assignment>(ass);
             ItemEditing?.Invoke(this, args);
@@ -1413,11 +1261,12 @@ namespace PRSDesktop
         private void EditAssignment(Assignment model)
         {
             var grid = CheckGrid(ref ag);
+            Client.EnsureColumns(model, grid.LoadEditorColumns());
 
-            if (DynamicGridUtils.EditEntity<Assignment>(model.ID, out var ass))
+            if (DynamicGridUtils.EditEntity<Assignment>(model))
             {
-                ItemChanged?.Invoke(this, new CalendarDataEventArgs<Assignment>(ass));
-                Refresh();
+                ItemChanged?.Invoke(this, new CalendarDataEventArgs(model));
+                // Refresh();
             }
         }
 
@@ -1428,9 +1277,9 @@ namespace PRSDesktop
             
             var ass = new Assignment { ID = model.ID };
             Client.Delete(ass, "Assignment Deleted from Scheduler");
-            ItemChanged?.Invoke(this, new CalendarDataEventArgs<Assignment>(ass));
+            ItemChanged?.Invoke(this, new CalendarDataEventArgs(ass));
             Refresh();
-            SelectionChanged?.Invoke(this, new CalendarDataEventArgs<Assignment>(null));
+            SelectionChanged?.Invoke(this, new CalendarDataEventArgs(null));
 
         }
         
@@ -1472,21 +1321,14 @@ namespace PRSDesktop
         {
             if(e.Value is AssignmentAppointment appointment)
             {
-                var args = new CalendarDataEventArgs<Assignment>(appointment.Model);
+                var args = new CalendarDataEventArgs(appointment.Model);
                 SelectionChanged?.Invoke(this, args);
             }
             else
             {
-                SelectionChanged?.Invoke(this, new CalendarDataEventArgs<Assignment>(null));
+                SelectionChanged?.Invoke(this, new CalendarDataEventArgs(null));
             }
         }
-        
-        private void Calendar_OnSizeChanged(object sender, SizeChangedEventArgs e)
-        {
-            if (double.IsNaN(this.ActualHeight) || (this.ActualHeight == 0))
-                return;
-            UpdateZoom();
-        }
 
         private void _settingsButton_OnClick(object sender, RoutedEventArgs e)
         {
@@ -1508,7 +1350,7 @@ namespace PRSDesktop
             if (EventSuppressor.IsSet(Suppress.Events)) return;
 
             Properties.AlwaysStartOnToday = AlwaysTodayBox.IsChecked == true;
-            SaveSettings?.Invoke(this, Properties);
+            DoSaveSettings();
         }
 
         private void TextBlock_SizeChanged(object sender, SizeChangedEventArgs e)
@@ -1529,5 +1371,30 @@ namespace PRSDesktop
             }
             block.Text = display;
         }
+
+        private void Left_Click(object sender, RoutedEventArgs e)
+        {
+            SelectedDate = SelectedDate.AddDays(CalendarView switch
+            {
+                CalendarViewType.Day => -1,
+                CalendarViewType.WorkWeek or CalendarViewType.Week => -7,
+                _ => throw new InvalidEnumException<CalendarViewType>(CalendarView)
+            });
+        }
+
+        private void Right_Click(object sender, RoutedEventArgs e)
+        {
+            SelectedDate = SelectedDate.AddDays(CalendarView switch
+            {
+                CalendarViewType.Day => 1,
+                CalendarViewType.WorkWeek or CalendarViewType.Week => 7,
+                _ => throw new InvalidEnumException<CalendarViewType>(CalendarView)
+            });
+        }
+
+        private void Today_Click(object sender, RoutedEventArgs e)
+        {
+            SelectedDate = DateTime.Today;
+        }
     }
 }

+ 2 - 10
prs.desktop/Components/Calendar/Events/CalendarDataEvent.cs

@@ -8,18 +8,10 @@ namespace PRSDesktop
         object? Item { get; }
     }
     
-    public interface ICalendarDataEventArgs<T>
+    public class CalendarDataEventArgs(object? item) : EventArgs, ICalendarDataEventArgs
     {
-        T? Item { get; }
-    }
-    
-    public class CalendarDataEventArgs<T>(T item) : EventArgs, ICalendarDataEventArgs<T>, ICalendarDataEventArgs
-    {
-        public T Item { get; } = item;
-
-        object? ICalendarDataEventArgs.Item => Item;
+        public object? Item { get; } = item;
     }
 
     public delegate void CalendarDataEvent(object sender, ICalendarDataEventArgs args);
-    
 }

+ 11 - 11
prs.desktop/Panels/DailyReports/DailyReport.xaml.cs

@@ -415,15 +415,15 @@ namespace PRSDesktop
             if (sender is not ContextMenu menu)
                 return;
             
-            if (args is CalendarDataEventArgs<CalendarTimeSlot> slot)
+            if (args.Item is CalendarTimeSlot slot)
             {
-                PopulateFavourites(menu, slot.Item, false);
+                PopulateFavourites(menu, slot, false);
             }
-            else if (args is CalendarDataEventArgs<Assignment> model)
+            else if (args.Item is Assignment assignment)
             {
                 menu.Items.Insert(1,new Separator());
-                var SetAsFavouriteMenu = new MenuItem { Header = GetFavourite(model.Item) == null ? "Set As Favourite" : "Update Favourite"};
-                SetAsFavouriteMenu.Click += (o, args) => SetAsFavourite(model.Item);
+                var SetAsFavouriteMenu = new MenuItem { Header = GetFavourite(assignment) == null ? "Set As Favourite" : "Update Favourite"};
+                SetAsFavouriteMenu.Click += (o, args) => SetAsFavourite(assignment);
                 menu.Items.Insert(2,SetAsFavouriteMenu);
 
                 if (_settings.Favourites.Any())
@@ -524,10 +524,10 @@ namespace PRSDesktop
 
         private void Assignments_OnSelectionChanged(object sender, ICalendarDataEventArgs args)
         {
-            if (args is CalendarDataEventArgs<Assignment> ass)
+            if (args.Item is Assignment ass)
             {
                 CheckandSaveAssignment();
-                SelectAssignment(ass.Item);
+                SelectAssignment(ass);
             }
             else
 
@@ -634,13 +634,13 @@ namespace PRSDesktop
 
         private void Assignments_OnItemChanged(object sender, ICalendarDataEventArgs args)
         {
-            if (args is CalendarDataEventArgs<Assignment> ass)
+            if (args.Item is Assignment ass)
             {
-                if (ass.Item.ID == Guid.Empty)
+                if (ass.ID == Guid.Empty)
                     using (new WaitCursor())
                     {
-                        ass.Item.Actual.Duration = new TimeSpan(0, 30, 0);
-                        new Client<Assignment>().Save(ass.Item, "Created from Daily Report Drag & Drop");
+                        ass.Actual.Duration = new TimeSpan(0, 30, 0);
+                        new Client<Assignment>().Save(ass, "Created from Daily Report Drag & Drop");
                     }
             }
         }

+ 2 - 2
prs.desktop/Panels/Delivery/DeliveryPanel.xaml.cs

@@ -269,7 +269,7 @@ namespace PRSDesktop
             if (sender is not ContextMenu menu)
                 return;
             
-            if (args is CalendarDataEventArgs<CalendarTimeSlot> slot)
+            if (args.Item is CalendarTimeSlot slot)
             {
                 menu.Items.Insert(0, new Separator());
                 bool bDeliveries = false;
@@ -300,7 +300,7 @@ namespace PRSDesktop
                         item.Header = description;
                         item.Click += (o,e) =>
                         {
-                            if (Deliveries.CreateBooking(row, slot.Item.EmployeeID, slot.Item.Date + slot.Item.Start))
+                            if (Deliveries.CreateBooking(row, slot.EmployeeID, slot.Date + slot.Start))
                                 Refresh();
                         };
                         CreateBooking.Items.Add(item);