MergedDictionaries и поиск ресурсов
У меня есть проблема со словарями ресурсов и объединенными словарями в целом, особенно когда речь идет о производительности поиска ресурсов. После некоторого тестирования производительности я обнаружил, что ResourceDictionary.get_MergedDictionaries - это вызов с наибольшим количеством обращений (проверено в профилировщике ANTS). У нас есть около 300 xamls словаря ресурсов, и многие из них используют объединенный словарь для "включения" других стилей. Что ж, get_MergedDictionaries рассчитывают на одну часть нашего приложения, где мало что происходит, было около 10 миллионов просмотров. Так что я думаю, что мы делаем что-то совершенно не так со словарями ресурсов в целом. Поэтому я попытался все изменить, и я хочу попытаться избавиться от всех объединенных словарей.
Теперь к актуальному вопросу. Я пытался избавиться от слитых фраз, но мне это не удалось. Насколько я понимаю, когда вы используете StaticResource, для поиска требуется определить ресурс перед текущим. Я сделал следующий короткий пример:
Один основной проект и одна настраиваемая библиотека управления.
библиотека пользовательских элементов управления содержит 2 xamls.
<!-- Colors.xaml -->
<ResourceDictionary [stripped namespaces] >
<SolidColorBrush x:Key="myColor" Color="Green"/>
</ResourceDictionary>
<!-- Templates.xaml -->
<ResourceDictionary [stripped namespaces]>
<ControlTemplate x:Key="myTemplate" TargetType="Button">
<Rectangle Fill="{StaticResource myColor}"/>
</ControlTemplate>
</ResourceDictionary>
Теперь в основном проекте MainWindow.xaml выглядит так
<Window x:Class="ResourceTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/ResourceTestLib;component/Themes/Colors.xaml"/>
<ResourceDictionary Source="/ResourceTestLib;component/Themes/Template.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<Button Template="{StaticResource myTemplate}"/>
</Grid>
</Window>
Это желаемая цель. но, к сожалению, это происходит сбой, потому что ресурс "myColor" не может быть найден. Я, конечно, знаю, как это исправить, добавить объединение в Templates.xaml и ссылку на Colors.xaml, но я всегда думал, что я никогда не проверял, что ресурсы ищутся в зависимости от логического дерева и ресурсов элемента. Мое понимание Кнопка создана; попробуйте поискать шаблон.. найдено; попробуйте поискать цвета, не найденные на собственных ресурсах, подойти и использовать ресурсы windows.
Кажется, я не прав. Поэтому я надеюсь, что кто-то может пролить свет на это для меня. Мы интенсивно используем WPF, и, несмотря на это, мы многого достигли с ним, но из-за некоторого неправильного поведения в начале наша производительность довольно плоха только из-за поиска ресурсов. Любая помощь будет принята с благодарностью
Заранее спасибо С наилучшими пожеланиями Нико
4 ответа
Ну, я не люблю отвечать на свой вопрос, но я думаю, что многие люди могут наткнуться на это, и я хочу дать им наше текущее решение в качестве варианта для рассмотрения.
Как я уже говорил, у нас есть много XAML, около ~300 для всех видов вещей, таких как общие ресурсы (кисти, цвета), но также много XAML, содержащих различные шаблоны данных, стили для элементов управления, а также для пользовательских элементов управления. Вначале такой подход с большим количеством XAML был для нас разумным, потому что мы делаем то же самое с нашими классами и делаем их небольшими и организованными. К сожалению, WPF это не нравится. Чем больше у вас ResourceDictionaries и чем больше вы объединяете их с помощью MergedDictionaries, тем хуже будет ваша производительность. Лучший совет, который я могу вам дать, - используйте как можно меньше XAML-файлов ResourceDictionary.
Мы укусили пулю и объединили многие из них в один гигантский XAML, фактически мы делаем это сейчас с помощью прекомпилятора, сохраняющего лучшее из обоих миров. Мы можем использовать столько XAML, сколько захотим, просто следуя нескольким ограничениям и объединяя их в компиляцию в гигантском XAML. Увеличение производительности мы получили замечательно. В своем вопросе я написал "11 миллионов обращений к getMergedDictionaries" ... просто "предварительно скомпилировав" одну из наших сборок, мы сократили до 2 миллионов обращений, и производительность во всем приложении во все времена намного лучше.
Итак, в конце концов. Ресурсы XAML не следует рассматривать как исходный код, который компилируется, вместо этого его следует понимать как реальный ресурс, который, когда он объявлен, существует, занимает пространство и производительность.
Ну, мы должны были изучить этот трудный путь. Я надеюсь, что все, кто читает это, могут улучшить свои проекты, учась на наших ошибках.
Спасибо за все комментарии и предложения.
С наилучшими пожеланиями Нико
Я склонен идти по пути использования только одного ResourceDictionary в приложении, чтобы избежать каких-либо проблем с производительностью.
Чтобы обеспечить управляемость XAML, я использую плагин Visual Studio для регионов XAML и обертываю каждую категорию ресурсов в регионе.
- Кисти
- Стили текста
- так далее...
Для этого сценария плагин является абсолютным спасателем жизни. http://visualstudiogallery.msdn.microsoft.com/3c534623-bb05-417f-afc0-c9e26bf0e177
Использование SharedResourceDictionary вместо ResourceDictionary полностью решило для меня проблему производительности MergedDictionaries: http://www.wpftutorial.net/MergedDictionaryPerformance.html
Был ли пролен свет на процедуру поиска ресурсов? Почему "myColor" не был найден?
Между прочим, я нашел способ заставить это работать - но странный и неустойчивый способ. Если Application.xaml имеет этот код, цвет должен быть найден:
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/ResourceTestLib;component/Themes/Colors.xaml"/>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/ResourceTestLib;component/Themes/Template.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
Если, с другой стороны, вы включаете этот код в другой XAML, который затем включаете в Application.xaml - он не работает, даже если структуры ресурсов идентичны (проверено с помощью Snoop).