2021-08-29

Binding not Updating on DependencyProperty - no XAML binding failure shown

I'm creating a fairly basic calendar control - showing time slots during the day, kind of like the Outlook calendar does. The full XAML for my control is pretty simple so I'll post in full:

<UserControl x:Class="AJSoft.Controls.Diary.Calendar.CalendarTimeStrip"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:AJSoft.Controls.Diary.Calendar"
             Name="ucCalendarTimeStrip"
             >
    <ListBox ItemsSource="{Binding ElementName=ucCalendarTimeStrip, Path=TimeItemsSource, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
</UserControl>

Like I said - simple. I normally don't define all those binding properties explicitly, but am trying to eliminate problems. A ListBox displays "CalendarTimeItem" objects created dynamically. Here's the code behind:

public partial class CalendarTimeStrip : UserControl
    {
        public static readonly DependencyProperty TimeItemsSourceProperty = DependencyProperty.Register("TimeItems", typeof(IEnumerable<CalendarTimeItem>), typeof(CalendarTimeStrip));
        public static readonly DependencyProperty EndTimeProperty = DependencyProperty.Register("EndTime", typeof(DateTime), typeof(CalendarTimeStrip), 
            new(DateTime.Now.AddHours(12), TimeChangedCallback));
        public static readonly DependencyProperty StartTimeProperty = DependencyProperty.Register("StartTime", typeof(DateTime), typeof(CalendarTimeStrip),
            new(DateTime.Now, TimeChangedCallback));

        public CalendarTimeStrip()
        {
            InitializeComponent();
            TimeChangedCallback(this, new());
        }

        public DateTime EndTime
        {
            get => (DateTime)GetValue(EndTimeProperty);
            set => SetValue(EndTimeProperty, value);
        }

        public DateTime StartTime
        {
            get => (DateTime)GetValue(StartTimeProperty);
            set => SetValue(StartTimeProperty, value);
        }

        public IEnumerable<CalendarTimeItem> TimeItemsSource
        {
            get => (IEnumerable<CalendarTimeItem>)GetValue(TimeItemsSourceProperty);
            set => SetValue(TimeItemsSourceProperty, value);
        }

        public static void TimeChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            CalendarTimeStrip calendarTimeStrip = (CalendarTimeStrip)d;

            List<CalendarTimeItem> timeItems = new();

            int totalMinutes = (int)(calendarTimeStrip.EndTime - calendarTimeStrip.StartTime).TotalMinutes;

            for (int i = 0; i < totalMinutes; i += 5)
                timeItems.Add(new() { Time = calendarTimeStrip.StartTime.AddMinutes(i) });

            calendarTimeStrip.TimeItemsSource = timeItems;
        }
    }

Again - nothing too tricky. We add "CalendarTimeItem"s for each 5 minute interval between a given start and end time. When these values are updated, I recreate the entire set of CalendarTimeItems. No optimising yet, but this is early days.

For some reason, the Callback fires as expected, the TimeItemsSource setter is called, but the binding never seems to trigger the getter and I see no items in my ListBox. When I manually create CalendarTimeItem's in XAML, everything shows as expected.

What's going on here?

Edit: manually invoking the Callback after InitialiseComponent is in there for testing purposes...

Edit 2: I now have this working by using TimeItemsSource as a CLR property with INotifyPropertyChanged - but I don't like this. Although it works, I'd still like to know why it wasn't working when linked up as a DP.



from Recent Questions - Stack Overflow https://ift.tt/3ky6oxN
https://ift.tt/eA8V8J

No comments:

Post a Comment