{"record":{"id":"360851da02e4b7a0","repo":"dotnet/maui","slug":"itemtemplate-count-has-exceeded-the-limit-of-view","errorCode":null,"errorMessage":"ItemTemplate count has exceeded the limit of {ViewTypeCount}\nPlease make sure to reuse DataTemplate objects","messagePattern":"ItemTemplate count has exceeded the limit of (.+?)\nPlease make sure to reuse DataTemplate objects","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/Compatibility/Core.LegacyRenderers/Android/ListViewAdapter.cs","lineNumber":197,"sourceCode":"\t\t\t\t}\n\n\t\t\t\titemTemplate = selector.SelectTemplate(item, _listView);\n\t\t\t}\n\n\t\t\t// check again to guard against DataTemplateSelectors that return null\n\t\t\tif (itemTemplate == null)\n\t\t\t\treturn DefaultItemTemplateId;\n\n\t\t\tif (!_templateToId.TryGetValue(itemTemplate, out int key))\n\t\t\t{\n\t\t\t\t_dataTemplateIncrementer++;\n\t\t\t\tkey = _dataTemplateIncrementer;\n\t\t\t\t_templateToId[itemTemplate] = key;\n\t\t\t}\n\n\t\t\tif (key >= ViewTypeCount)\n\t\t\t{\n\t\t\t\tthrow new Exception($\"ItemTemplate count has exceeded the limit of {ViewTypeCount}\" + Environment.NewLine +\n\t\t\t\t\t\t\t\t\t \"Please make sure to reuse DataTemplate objects\");\n\t\t\t}\n\n\t\t\treturn key;\n\t\t}\n\n\t\tpublic override AView GetView(int position, AView convertView, ViewGroup parent)\n\t\t{\n\t\t\tCell cell = null;\n\n\t\t\tPerformance.Start(out string reference);\n\n\t\t\tListViewCachingStrategy cachingStrategy = Controller.CachingStrategy;\n\t\t\tvar nextCellIsHeader = false;\n\t\t\tif (cachingStrategy == ListViewCachingStrategy.RetainElement || convertView == null)\n\t\t\t{\n\t\t\t\tif (_listView.IsGroupingEnabled)\n\t\t\t\t{","sourceCodeStart":179,"sourceCodeEnd":215,"githubUrl":"https://github.com/dotnet/maui/blob/f377ff1c5ee04d334d8a925f50c83a6b7afddf03/src/Compatibility/Core.LegacyRenderers/Android/ListViewAdapter.cs#L179-L215","documentation":"The ListViewAdapter assigns a unique integer key to each distinct DataTemplate instance it encounters via the _templateToId dictionary and _dataTemplateIncrementer (starting at 2). The ViewTypeCount property returns 23 (ListViewAdapter.cs:125), so the effective limit is approximately 21 unique DataTemplate instances (incrementer starts at 2, and keys must stay below 23). When a new DataTemplate instance pushes the key beyond ViewTypeCount, Android's adapter contract is violated and the exception is thrown. The most common cause is a DataTemplateSelector that creates new DataTemplate instances per call to OnSelectTemplate instead of caching and reusing them.","triggerScenarios":"GetItemViewType assigns a new incrementing key for each unique DataTemplate reference. If a DataTemplateSelector's SelectTemplate method returns 'new DataTemplate(() => new View())' each time it is called for different data items, every distinct return creates a new dictionary entry, and the counter eventually exceeds 23. The exception fires on the item that pushes it over.","commonSituations":"DataTemplateSelector returning new DataTemplate instances in OnSelectTemplate without caching; creating DataTemplate objects inside a data-binding converter or inside a cell factory that runs per-item; binding a ListView's ItemTemplate to a property that returns a new DataTemplate each getter call; using a lambda-based DataTemplate constructor inside a hot path; large heterogeneous lists where each item type gets a distinct template and there are more than ~20 types.","solutions":["Cache DataTemplate instances in the DataTemplateSelector as static or instance fields and return the cached references from OnSelectTemplate.","If using a DataTemplateSelector, pre-create all templates once and reuse them: 'static readonly DataTemplate _templateA = new DataTemplate(typeof(ViewA));'","Reduce the number of distinct templates to stay within the 20-template documented limit.","If you truly need more view types, use a CollectionView with a custom handler or a third-party virtualized list that does not have the Android AdapterView ViewTypeCount constraint.","Switch from ListView to CollectionView (Microsoft.Maui.Controls.CollectionView), which uses RecyclerView and has no hard ViewTypeCount limit."],"exampleFix":"// before — creates new DataTemplate per call (causes the error)\npublic class MyTemplateSelector : DataTemplateSelector\n{\n    protected override DataTemplate OnSelectTemplate(object item, BindableObject container)\n    {\n        var type = (item as MyModel)?.Type;\n        return type switch\n        {\n            \"A\" => new DataTemplate(() => new ViewA()),\n            \"B\" => new DataTemplate(() => new ViewB()),\n            _ => new DataTemplate(() => new ViewDefault())\n        };\n    }\n}\n// after — cached and reused (fixes the error)\npublic class MyTemplateSelector : DataTemplateSelector\n{\n    readonly DataTemplate _a = new DataTemplate(() => new ViewA());\n    readonly DataTemplate _b = new DataTemplate(() => new ViewB());\n    readonly DataTemplate _default = new DataTemplate(() => new ViewDefault());\n\n    protected override DataTemplate OnSelectTemplate(object item, BindableObject container)\n    {\n        var type = (item as MyModel)?.Type;\n        return type switch\n        {\n            \"A\" => _a,\n            \"B\" => _b,\n            _ => _default\n        };\n    }\n}","handlingStrategy":"validation","validationCode":"// Pre-validate: count unique DataTemplate instances your selector can return\nvar uniqueTemplates = new HashSet<DataTemplate>();\nforeach (var item in sampleItems)\n{\n    var template = selector.SelectTemplate(item, listView);\n    uniqueTemplates.Add(template);\n}\nif (uniqueTemplates.Count > 20)\n{\n    throw new InvalidOperationException($\"DataTemplateSelector returns {uniqueTemplates.Count} unique DataTemplate instances; limit is ~20. Cache and reuse templates.\");\n}","typeGuard":"// Verify the DataTemplateSelector caches its templates (no per-call allocation)\nstatic bool SelectorCachesTemplates(DataTemplateSelector selector)\n{\n    // Call SelectTemplate twice with equivalent items and check reference equality\n    var dummy = new object();\n    var t1 = selector.SelectTemplate(dummy, null);\n    var t2 = selector.SelectTemplate(dummy, null);\n    return ReferenceEquals(t1, t2);\n}","tryCatchPattern":null,"preventionTips":["Always cache DataTemplate instances as fields in DataTemplateSelector subclasses; never allocate new ones in OnSelectTemplate.","Migrate from ListView to CollectionView to escape the Android AdapterView ViewTypeCount constraint entirely.","Unit test DataTemplateSelector implementations to verify the number of unique DataTemplate references returned does not exceed 20.","Static-analysis or code review: flag any 'new DataTemplate(' inside a method that runs per-item.","If using lambda-based DataTemplate constructors, extract them to static fields."],"tags":["maui","android","listview","datatemplate","datatemplateselector","legacy-renderers","viewtypecount"],"backgroundTag":null,"analyzedSha":"f377ff1c5ee04d334d8a925f50c83a6b7afddf03","analyzedAt":"2026-08-13T14:26:18.069Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}