dotnet/maui · error · Exception

ItemTemplate count has exceeded the limit of {ViewTypeCount}

Error message

ItemTemplate count has exceeded the limit of {ViewTypeCount}{Environment.NewLine}Please make sure to reuse DataTemplate objects

What it means

ListViewAdapter.GetViewTypeForItemPath assigns each distinct DataTemplate a monotonically increasing integer key (stored in _templateToId, via _dataTemplateIncrementer) and throws if the key reaches ViewTypeCount. ViewTypeCount is Android's adapter view-type budget; exceeding it means the app is creating a new DataTemplate instance per item instead of reusing one.

Source

Thrown at src/Compatibility/Core/src/Android/Renderers/ListViewAdapter.cs:198

				}

				itemTemplate = selector.SelectTemplate(item, _listView);
			}

			// check again to guard against DataTemplateSelectors that return null
			if (itemTemplate == null)
				return DefaultItemTemplateId;

			if (!_templateToId.TryGetValue(itemTemplate, out int key))
			{
				_dataTemplateIncrementer++;
				key = _dataTemplateIncrementer;
				_templateToId[itemTemplate] = key;
			}

			if (key >= ViewTypeCount)
			{
				throw new Exception($"ItemTemplate count has exceeded the limit of {ViewTypeCount}" + Environment.NewLine +
									 "Please make sure to reuse DataTemplate objects");
			}

			return key;
		}

		public override AView GetView(int position, AView convertView, ViewGroup parent)
		{
			Cell cell = null;

			Performance.Start(out string reference);

			ListViewCachingStrategy cachingStrategy = Controller.CachingStrategy;
			var nextCellIsHeader = false;
			if (cachingStrategy == ListViewCachingStrategy.RetainElement || convertView == null)
			{
				if (_listView.IsGroupingEnabled)
				{

View on GitHub (pinned to f377ff1c5e)

Solutions

  1. Cache DataTemplate instances — create them once (static fields, constructor) and return the same instance from the selector.
  2. Make DataTemplateSelector.OnSelectTemplate idempotent per type: store templates as fields and reuse them.
  3. Audit any code path that returns `new DataTemplate(...)` on the hot path and hoist it out.

Example fix

// before
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
    return item is Header ? new DataTemplate(typeof(HeaderViewCell)) : new DataTemplate(typeof(RowViewCell));
}
// after — reuse instances
private readonly DataTemplate _header = new DataTemplate(typeof(HeaderViewCell));
private readonly DataTemplate _row = new DataTemplate(typeof(RowViewCell));
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
    => item is Header ? _header : _row;
Defensive patterns

Strategy: validation

Validate before calling

class SafeTemplateSelector : DataTemplateSelector
{
    readonly Dictionary<Type, DataTemplate> _cache = new();
    DataTemplate GetOrCreate<TCell>() where TCell : Cell
        => _cache.GetOrAdd(typeof(TCell), _ => new DataTemplate(typeof(TCell)));
    protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
        => item is Header ? GetOrCreate<HeaderCell>() : GetOrCreate<RowCell>();
}

Prevention

When it happens

Trigger: The app supplies a per-item DataTemplate factory that returns a NEW DataTemplate instance each call (e.g. inside a DataTemplateSelector returning `new DataTemplate(...)` per invocation), so _dataTemplateIncrementer climbs past ViewTypeCount.

Common situations: A DataTemplateSelector whose OnSelectTemplate returns `new DataTemplate(...)` rather than caching instances; building templates inside a loop or binding; using a lambda-based DataTemplate created per item.

Related errors


AI-assisted analysis of dotnet/maui@f377ff1c5e (2026-08-13). Data as JSON: /api/errors/08cf5c8a305dab46. Report an issue: GitHub.