Unplanned
Last Updated: 07 May 2026 13:39 by ADMIN
Nico
Created on: 04 May 2026 12:29
Category: UI for .NET MAUI
Type: Bug Report
0
Scheduler: NullReferenceException when using AgendaView, changing the active view definition index initially with 13.1.0

After upgrading from Telerik UI for .NET MAUI 13.0.0 → 13.2.0, a NullReferenceException is thrown inside SchedulerAgendaView.Init() on Windows (WinUI3) whenever a RadScheduler with an AgendaViewDefinition is used. The scheduler never renders and the app receives an unhandled exception.


Steps to Reproduce:

  1. Create a .NET MAUI app targeting Windows (WinUI3)
  2. Add a ContentPage (or any page) with a RadScheduler in XAML, including at least one AgendaViewDefinition in ViewDefinitions:
<telerik:RadScheduler AppointmentsSource="{Binding Appointments}">
    <telerik:RadScheduler.ViewDefinitions>
        <telerik:AgendaViewDefinition />
        <telerik:DayViewDefinition />
    </telerik:RadScheduler.ViewDefinitions>
</telerik:RadScheduler>
  1. Navigate to that page
  2. Observe the unhandled exception

Expected Behavior:

The scheduler renders correctly with the AgendaViewDefinition, as it did in 13.0.0.


Actual Behavior:

An unhandled NullReferenceException is thrown:

System.NullReferenceException: Object reference not set to an instance of an object.
   at Telerik.Maui.Controls.Scheduler.SchedulerAgendaView.Init()
   at System.Threading.Tasks.Task.<>c.<ThrowAsync>b__124_0(Object state)
   at Microsoft.UI.Dispatching.DispatcherQueueSynchronizationContext.<>c__DisplayClass2_0.<Post>b__0()

Root Cause Analysis (via ILSpy decompilation):

Comparing the decompiled assemblies of 13.0.0 and 13.2.0 reveals a new override in RadScheduler introduced in 13.2.0:

// NEW in 13.2.0 — not present in 13.0.0:
protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
    base.OnPropertyChanged(propertyName);
    if (propertyName == "Parent" && Parent != null && !IsLoaded)
        UpdateActiveViewDefinition();
}

This causes the following call chain to be triggered during InitializeComponent() in the page constructor — before the page is attached to a window:

  1. XAML parser adds RadScheduler to the page → Parent property changes
  2. OnPropertyChanged("Parent") fires → UpdateActiveViewDefinition() is called
  3. → ActiveViewDefinition = ViewDefinitions[0] (the AgendaViewDefinition)
  4. → OnActiveViewDefinitionChanged() → content.Rebuild() (template already applied on WinUI3)
  5. → SchedulerAgendaView.Model setter → Init() is called as async void
  6. Inside Init(), after the first await, the continuation runs and DataBindingComplete fires
  7. The completeHandler inside Init() calls:
((BindableObject)this).Dispatcher.Dispatch(async delegate { ... });
  1. Dispatcher is null because the SchedulerAgendaView is not yet attached to a window → NullReferenceException

The exception is captured by the async void state machine and re-thrown via SynchronizationContext.ThrowAsync → DispatcherQueueSynchronizationContext.Post, which matches the observed stack trace exactly.

Why it worked in 13.0.0: UpdateActiveViewDefinition() was never called from OnPropertyChanged. It was only invoked after IsLoaded = true, at which point Dispatcher is guaranteed to be non-null.

Why the condition !IsLoaded is insufficient: Parent != null does not imply a window is present. During InitializeComponent(), the element has a parent in the logical tree but is not yet attached to any Window, making Dispatcher null on BindableObject.


Workaround:

Do not declare ViewDefinitions in XAML. Instead, add them programmatically in the page's Loaded event handler, at which point Dispatcher is guaranteed to be available:

private void OnPageLoaded(object sender, EventArgs e)
{
    if (scheduler.ViewDefinitions.Count == 0)
    {
        scheduler.ViewDefinitions.Add(new AgendaViewDefinition());
        scheduler.ViewDefinitions.Add(new DayViewDefinition());
        scheduler.ViewDefinitions.Add(new WeekViewDefinition());
        scheduler.ViewDefinitions.Add(new MonthViewDefinition());
    }
}

Suggested Fix:

Either:

Option A — Guard Dispatcher usage inside SchedulerAgendaView.Init():

// In completeHandler, before calling Dispatch:
if (((BindableObject)this).Dispatcher is { } dispatcher)
    dispatcher.Dispatch(...);

Option B — Guard UpdateActiveViewDefinition() in RadScheduler.OnPropertyChanged to only run when content (the internal RadSchedulerContent) has already been set (i.e., template was applied):

if (propertyName == "Parent" && Parent != null && !IsLoaded && content != null)
    UpdateActiveViewDefinition();

Option B is more conservative and closer to the original 13.0.0 behavior, since content being non-null implies the template has been applied and Dispatcher is available.


Environment:

Telerik UI for .NET MAUI13.2.0 (regression from 13.0.0)
.NET.NET 10
PlatformWindows (WinUI3)
MAUI10.0.60

3 comments
ADMIN
Didi
Posted on: 07 May 2026 13:39

Hi Nico,

I tested the case and the issue happens with Telerik MAUI 13.1.0 and above and also on net 9 and net 10 projects. The exception occurs when having AgendaView in the view definitions in xaml and setting the ActiveViewDefinitionIndex programmatically during page navigation. 

Yes the workaround is setting the View Definitions programmatically.

Regards,
Didi
Progress Telerik

Love the Telerik and Kendo UI products and believe more people should try them? Invite a fellow developer to become a Progress customer and each of you can get a $50 Amazon gift voucher.

Nico
Posted on: 06 May 2026 14:37

Update: Minimal Repro + Corrected Root Cause (Analysis and repro created with assistance from GitHub Copilot)

After deeper investigation (IL decompilation + step-by-step reproduction), I can provide both a reliable minimal repro and a corrected root cause analysis.


The NRE location is not Dispatcher — it is agendaViewModelCache

The original analysis was incorrect. Decompiling SchedulerAgendaView.Init() in 13.2.0 reveals two code paths based on IsInitialized:

// PATH 1 (IsInitialized=true) — safe, always returns early:
obj = await agendaViewModelCache.GetItemsAsync();
if (collectionView.ItemsSource == obj) return;  // ← safe early-return

// PATH 2 (IsInitialized=false — fresh SchedulerAgendaViewModel) — vulnerable:
obj3 = await agendaViewModelCache.GetItemsAsync();  // ← suspends here
collectionView.ItemsSource = obj3;
agendaViewModelCache.GetVisibleRangeStartFunc = ...;  // ← NRE if agendaViewModelCache is null!

PATH 2 has no null-check on agendaViewModelCache after the await.


How agendaViewModelCache becomes null while Init() is suspended

agendaViewModelCache is set to null whenever SchedulerAgendaView.Model is set to a non-AgendaViewModel value — which happens when ActiveViewDefinition changes away from AgendaViewDefinition:

ActiveViewDefinition = DayViewDefinition
  → OnActiveViewDefinitionChanged(AgendaViewDef, DayViewDef)
  → SchedulerAgendaView.Model = null
  → agendaViewModelCache = null

Corrected crash sequence

1. new SchedulerPage()  →  InitializeComponent():
   XAML populates ViewDefinitions, then adds RadScheduler to Grid.
   13.2.0 OnPropertyChanged("Parent") → UpdateActiveViewDefinition()
   → ActiveViewDefinition = AgendaViewDef (fresh VM, IsInitialized=false)
   → SchedulerAgendaView.Init() PATH 2 starts
   → 'await GetItemsAsync()' suspends; continuation queued on dispatcher.
   UI thread is still running — continuation cannot execute yet.

2. page.Scheduler.ActiveViewDefinitionIndex = 1  (still synchronous on UI thread)
   → ActiveViewDefinition = DayViewDefinition
   → SchedulerAgendaView.Model = null → agendaViewModelCache = null

3. await Navigation.PushAsync(page)  — first yield of the UI thread
   → dispatcher processes queued Init() PATH 2 continuation
   → agendaViewModelCache.GetVisibleRangeStartFunc = ...  → NullReferenceException!

This is exactly what happens in real apps: new SchedulerPage() { BindingContext = vm } where vm.ActiveViewDefinitionIndex is a persisted non-zero value (user previously navigated to Day/Week/Month view).


Minimal repro (self-contained, no AppointmentsSource needed)

MainPage.xaml — launcher:

<ContentPage ...>
    <Button Text="Open Scheduler (triggers NRE)"
            VerticalOptions="Center" HorizontalOptions="Center"
            Clicked="OnOpenSchedulerClicked" />
</ContentPage>

MainPage.xaml.cs:

private async void OnOpenSchedulerClicked(object sender, EventArgs e)
{
    var page = new SchedulerPage();   // Init() PATH 2 starts + suspends in CTOR
    page.Scheduler.ActiveViewDefinitionIndex = 1;  // agendaViewModelCache = null (sync!)
    await Navigation.PushAsync(page); // first yield → Init() continuation → NRE!
}

SchedulerPage.xaml:

<ContentPage ...>
    <telerik:RadScheduler x:Name="Scheduler">
        <telerik:RadScheduler.ViewDefinitions>
            <telerik:AgendaViewDefinition />
            <telerik:DayViewDefinition />
        </telerik:RadScheduler.ViewDefinitions>
    </telerik:RadScheduler>
</ContentPage>

App.xaml.cs — use NavigationPage (required for PushAsync):

protected override Window CreateWindow(IActivationState? activationState)
    => new Window(new NavigationPage(new MainPage()));

Note: On Windows (WinUI3), unhandled exceptions from async void are silently swallowed unless you add a handler in Platforms/Windows/App.xaml.cs:

UnhandledException += (s, e) => {
    System.Diagnostics.Debug.WriteLine($"CRASH: {e.Exception}");
    e.Handled = true;
};

Tested on: Telerik 13.2.0 / .NET 10 / Windows (WinUI3). Crash is immediate and 100% reproducible.


Corrected fix

The correct fix is a null-check in SchedulerAgendaView.Init() after the await in PATH 2:

// After: obj3 = await agendaViewModelCache.GetItemsAsync();
if (agendaViewModelCache == null) return;  // ← guard needed here

The original suggestion (Option B: content != null guard in OnPropertyChanged) would prevent Init() from starting too early, but would not protect against the race where ActiveViewDefinition changes while Init() is already suspended.

Attached Files:
ADMIN
Didi
Posted on: 05 May 2026 11:40

Hello Nico,

I am afraid I cannot reproduce the exact exception reported here. The exception I got is the reported here: https://feedback.telerik.com/maui/1713770-border-winui-exception-with-maui-10-0-60

I am a bit confused that the AgendaView throws null ref exception, as the agenda uses RadCollectionView. And based on the logged issue here: https://feedback.telerik.com/maui/1713770-border-winui-exception-with-maui-10-0-60 the exception is in the RadBorder and it is a COM Exception, not null ref exception. 

I have attached my test app. Modify it so the null ref exception to be presented in it.

Regards,
Didi
Progress Telerik

Attached Files: