src/Uno.UI/Uno/TemplateManager.md
TemplateManager is a development tooling feature that enables runtime updates of DataTemplate instances and automatic refresh of controls that use them. This is primarily designed for debugging and development scenarios where you need to modify templates dynamically without restarting the application.
DataTemplate instancesTemplateManager.EnableUpdateSubscriptions() at application startupTemplateManager.SubscribeToTemplate() to listen for template changesTemplateManager.UpdateDataTemplate() to modify template factories// Enable the dynamic template update system
// WARNING: This is a debugging tool - do not use in production
Uno.UI.TemplateManager.EnableUpdateSubscriptions();
⚠️ WARNING: The dynamic template update system is designed as a development/debugging tool. However, if you absolutely need to enable it in a production release build, you must configure both the MSBuild property and runtime activation:
Add this to your application's .csproj file:
<PropertyGroup>
<!-- DANGER: Only enable this if you absolutely need dynamic templates in production -->
<!-- This ensures the code survives compilation and linking -->
<UnoEnableDynamicDataTemplateUpdate>true</UnoEnableDynamicDataTemplateUpdate>
</PropertyGroup>
Even with the MSBuild flag enabled, you still need to explicitly activate the system at application startup:
public partial class App : Application
{
protected override void OnLaunched(LaunchActivatedEventArgs args)
{
// DANGER: Only call this if you absolutely need dynamic templates in production
// This has performance implications and should only be used for debugging
Uno.UI.TemplateManager.EnableUpdateSubscriptions();
// Rest of your application initialization...
}
}
MSBuild Flag (UnoEnableDynamicDataTemplateUpdate):
false to exclude the feature code from release buildsfalse, the linker removes all dynamic template update codetrue to include the necessary infrastructureRuntime Activation (EnableUpdateSubscriptions()):
Recommended Alternative: Instead of enabling this in production, consider using conditional compilation symbols to enable it only in debug builds:
#if DEBUG
Uno.UI.TemplateManager.EnableUpdateSubscriptions();
#endif
// Update a template's factory function (simple view factory)
if (Resources["MyTemplate"] is DataTemplate template)
{
Uno.UI.TemplateManager.UpdateDataTemplate(template, () =>
{
// Return new UI element - must be compatible with View type
return new TextBlock { Text = "Updated Content" };
});
}
// Alternative: Update using factory updater (advanced)
if (Resources["MyTemplate"] is DataTemplate template)
{
Uno.UI.TemplateManager.UpdateDataTemplate(template, oldFactory =>
{
// Return a new factory that creates different content
return (NewFrameworkTemplateBuilder)((_, _) => new TextBlock { Text = "Advanced Update" });
});
}
If you're developing a custom control that uses DataTemplate.LoadContent() and want it to react to template changes:
public class MyCustomControl : FrameworkElement
{
public static readonly DependencyProperty ItemTemplateProperty =
DependencyProperty.Register(
nameof(ItemTemplate),
typeof(DataTemplate),
typeof(MyCustomControl),
new PropertyMetadata(null, OnItemTemplateChanged));
public DataTemplate ItemTemplate
{
get => (DataTemplate)GetValue(ItemTemplateProperty);
set => SetValue(ItemTemplateProperty, value);
}
private static void OnItemTemplateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is MyCustomControl c)
{
// Subscribe to template updates using the owner-based API
// No need to keep a member IDisposable anymore
Uno.UI.TemplateManager.SubscribeToTemplate(
c,
(DataTemplate) e.NewValue,
() => c.RefreshContent());
c.RefreshContent();
}
}
private void RefreshContent()
{
var currentTemplate = ItemTemplate;
if (currentTemplate != null)
{
// Clear existing content
Children.Clear();
// Materialize new content
var content = currentTemplate.LoadContent() as UIElement;
if (content != null)
{
Children.Add(content);
}
}
}
}
The following Uno Platform controls already support dynamic template updates when the feature is enabled:
ContentPresenter and ContentControl are fully supportedItemsRepeater - Full support with comprehensive reentrancy protectionItemsControl - Basic support using internal TemplateUpdateSubscription.AttachListView and GridView (through inheritance from ItemsControl)This feature is intended exclusively for development and debugging scenarios. It should not be enabled in production applications as it:
Important: Template subscriptions should NOT be disposed in OnUnloaded as this would prevent them from working when the control is loaded again. Instead:
public class MyControl : FrameworkElement
{
// ✅ CORRECT: Subscribe in DependencyProperty callback (no member field required)
private static void OnItemTemplateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is MyControl c)
{
Uno.UI.TemplateManager.SubscribeToTemplate(
c,
(DataTemplate)e.NewValue,
() => c.RefreshContent());
}
}
// ✅ OPTIONAL: Clean up all subscriptions associated with this control on final disposal
protected override void Dispose(bool disposing)
{
if (disposing)
{
Uno.UI.TemplateManager.UnsubscribeFromTemplate(this);
}
base.Dispose(disposing);
}
// ❌ INCORRECT: Don't unsubscribe in OnUnloaded
// protected override void OnUnloaded()
// {
// Uno.UI.TemplateManager.UnsubscribeFromTemplate(this); // DON'T DO THIS
// }
}
Everything is happening in the UI Thread, so no need to shield against concurrent access.
EnableUpdateSubscriptions() only once at app startupUpdateDataTemplate() from the UI threadFor custom controls, the subscription should be managed in the DependencyProperty change callback:
// ✅ CORRECT: Subscribe in DependencyProperty callback (no member field required)
private static void OnItemTemplateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is MyControl c)
{
Uno.UI.TemplateManager.SubscribeToTemplate(
c,
(DataTemplate)e.NewValue,
() => c.RefreshContent());
}
}
// ❌ INCORRECT: Don't subscribe in Loaded event
// protected override void OnLoaded()
// {
// TemplateManager.SubscribeToTemplate(...); // Wrong place
// }
EnableUpdateSubscriptions(): Enables the dynamic template update systemIsDataTemplateDynamicUpdateEnabled: Gets whether the feature is enabled via MSBuild configurationIsUpdateSubscriptionsEnabled: Gets whether the system is enabled (requires EnableUpdateSubscriptions() call)UpdateDataTemplate(DataTemplate, Func<NewFrameworkTemplateBuilder?, NewFrameworkTemplateBuilder?>): Updates a template with a factory updater functionUpdateDataTemplate(DataTemplate, Func<View?>): Updates a template with a simple view factory functionSubscribeToTemplate(DependencyObject owner, DataTemplate? template, Action onUpdated): Preferred owner-based subscription; returns bool indicating successSubscribeToTemplate(DependencyObject owner, string slotKey, DataTemplate? template, Action onUpdated): Owner-based subscription with a named slot for multiple subscriptions per control; returns bool indicating successUnsubscribeFromTemplate(DependencyObject owner): Unsubscribes all owner-associated subscriptionsUnsubscribeFromTemplate(DependencyObject owner, string slotKey): Unsubscribes the specified slot subscription for the ownerAttach(DependencyObject owner, DataTemplate? template, Action onUpdated): Owner-based internal subscriptionAttach(DependencyObject owner, string slotKey, DataTemplate? template, Action onUpdated): Owner-based internal subscription with slotDetach(DependencyObject owner): Detach all subscriptions for ownerDetach(DependencyObject owner, string slotKey): Detach slot subscription for ownertrue if subscriptions are enabled and the subscription was createdNote: Built-in Uno Platform controls use internal methods directly for performance reasons. The actual implementation in ItemsControl uses TemplateUpdateSubscription.Attach directly rather than the public TemplateManager.SubscribeToTemplate() API. User code should still use the public API.
// Development tool that updates templates based on file changes
public class TemplateHotReloader
{
public void Initialize()
{
Uno.UI.TemplateManager.EnableUpdateSubscriptions();
// Watch for XAML file changes (pseudo-code)
FileWatcher.OnFileChanged += (file) =>
{
if (file.EndsWith(".xaml"))
{
var template = FindTemplateForFile(file);
if (template != null)
{
// Use the simple view factory overload
Uno.UI.TemplateManager.UpdateDataTemplate(template, () =>
{
var updatedElement = LoadUpdatedTemplate(file);
return updatedElement; // Must return View? type
});
}
}
};
}
private View? LoadUpdatedTemplate(string filePath)
{
// Implementation to load and parse XAML file
// Returns the root UI element
return null; // Implementation specific
}
private DataTemplate? FindTemplateForFile(string filePath)
{
// Implementation to find the DataTemplate associated with the file
return null; // Implementation specific
}
}
This enables scenarios like XAML hot-reload where template changes can be reflected immediately in the running application without restart.