Я могу только подтвердить, что MeasureOverride использует наиболее трудоемкую операцию. (И вам не нужен профилировщик, чтобы это выяснить.) Я могу только догадываться, почему это занимает так много времени.
Во-первых, MeasureOverride рекурсивно перемещается по визуальному дереву ListBoxItem. Где-то в конце этой цепочки вызовов, вероятно, есть вызов нативного кода, который возвращает фактический размер.
Следовательно, сохранение простой структуры предмета имеет первостепенное значение.
Ваша структура кажется слишком сложной. Если вы посмотрите на шаблон TextBox, вы увидите, что его структура Grid> Border> ContentPresenter. Я не могу судить, что стоит за ContentPresenter, так как я не знаю ваш код, я просто удивляюсь, почему вы не используете TextBlock (где это возможно) - он намного эффективнее.
В качестве эксперимента я бы попытался реализовать MyListBoxItem.MeasureOverride и вернуть фиксированный размер без вызова базовой реализации. Я понятия не имею, работает ли он, но может принести удивительные результаты.
Второе, что вы можете исследовать при реализации собственных элементов списка, - это частота обращений к MeasureOverride. (Или просто посмотрите на вывод профилировщика.) Если количество вызовов слишком велико, то макет может занять больше циклов, чем необходимо. Возможно, существуют противоречивые требования к размерам, для решения которых необходимо выполнить несколько шагов.
Вы упомянули 40 предметов. Это не должно играть никакой роли. ListBox состоит из трех экранов ListBoxItem, остальные элементы создаются (чаще всего используются повторно) по мере необходимости. Если время измерения увеличивается с увеличением номера элемента, ищите проблему в реализации данных.