Подтвердить что ты не робот

Wpf объединенный словарь ресурсов не распознается из app.xaml

У меня есть приложение WPF.net 4.5, где мне сложно сменить ресурсные словари.

У меня есть такая же проблема, как Этот вопрос SO и Этот вопрос, но принятое решение не работа для меня.

У меня есть ресурсные словари, объявленные в моем приложении .xaml следующим образом (упрощен для ясности):

<Application.Resources>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="Skin/ResourceLibrary.xaml" />              
            <ResourceDictionary Source="Skin/Brushes/ColorStyles.xaml" />               
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>               
</Application.Resources>

Проблема: Приложение может "ПОСМОТРЕТЬ" диктатор ColorStyles, если он указан в app.xaml, но если я перемещаю его/гнездо внутри ResourceLibrary.xaml, то ColorStyles.xaml не будут "замечены" приложением, и появятся ошибки в отношении отсутствия статических ресурсов.

Вот как я создаю словарь ResourceLibrary.xaml(упрощенный):

<ResourceDictionary 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <ResourceDictionary.MergedDictionaries>

        <!--  BRUSHES AND COLORS  -->
        <ResourceDictionary Source="Brushes/ColorStyles.xaml" />

    </ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

Причина изменения: Моя текущая организация моих ресурсных словарей ужасна, и мне нужно ее изменить (поскольку я создаю объекты более одного раза). Я хотел иметь один словарь ресурсов в папке "Кожа", а затем вложенных папок для организации оставшихся словарей стиля, которые будут объединены в файле ResourceLibrary.xaml, который, в свою очередь, будет вызван в app.xaml.

Что я пробовал: Да, я попытался использовать решение по ссылке выше:

<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="Skin/ResourceLibrary.xaml"/>
        </ResourceDictionary.MergedDictionaries>
        <!-- Dummy Style, anything you won't use goes -->
        <Style TargetType="{x:Type Rectangle}" />
    </ResourceDictionary>
</Application.Resources>

но я получаю следующую ошибку в строке фиктивного стиля:

Ошибка 2 Элементы свойств не могут находиться в середине элемента содержание. Они должны быть до или после содержимого.

РЕДАКТИРОВАТЬ 1:

изменение кода на следующее устранено выше, благодаря комментарию lisp:

<Application.Resources>
    <ResourceDictionary>
        <!--Global View Model Locator-->
        <vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" />

        <!-- Dummy Style, anything you won't use goes -->
        <Style TargetType="{x:Type Rectangle}" />

        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="Skin/ResourceLibrary.xaml"></ResourceDictionary>             
            <ResourceDictionary Source="Skin/Brushes/ColorStyles.xaml" />
            <ResourceDictionary Source="Skin/NamedStyles/AlertStyles.xaml" />

        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>               
</Application.Resources>

но библиотека ресурсов по-прежнему не вызывается.

END EDIT 1:

Я также попытался изменить все пути к файлам, чтобы упаковать URI, но это также не решило проблему.

ИЗМЕНИТЬ 2:

Я попытался переместить ресурсLibrary.xaml и другие ресурсные словари в другой проект библиотеки классов (используя ту же структуру папок и файлы, что и выше). Затем я использовал следующий URI, но я до сих пор не могу получить доступ к ресурсам, объявленным в файле ResourceLibrary.xaml.

<ResourceDictionary Source="pack://application:,,,/FTC.Style;component/ResourceLibrary.xaml" />

Но опять же, если я добавлю каждый ресурсный словарь в файл App.Xaml, используя вышеприведенный формат UIR, ресурсы можно использовать.

END EDIT 2

После редактирования 1 ошибка исчезла, но я все еще не могу использовать ресурсы, которые являются частью объединенного словаря в файле ResourceLibrary.xaml. Я склонен согласиться с комментарием dowhile относительно того, следует ли мне использовать этот подход, но я хочу понять это, потому что наиболее распространенное решение этой проблемы (см. Ссылки в верхней части этого сообщения) не работает и, возможно, это решение может помочь кому-то другому.

Вопрос: Почему файл ResourceLibrary.xaml игнорируется?

4b9b3361

Ответ 1

У меня большая проблема с MergedDictionaries, и я считаю, что ваша проблема такая же. Я хочу, чтобы мои ResourceDictionaries были правильно организованы, что означает для меня, что есть, например, отдельные Buttons.xaml, TextBoxes.xaml, Colors.xaml и т.д. Я объединять их в Theme.xaml, часто все стили находятся в отдельной сборке (так что я могу легко переключать Темы). Мои ApplicationResources следующие:

<Application.Resources>
  <ResourceDictionary>
    <ResourceDictionary.MergedDictionaries>
      <ResourceDictionary Source="/DefaultTheme;component/Theme.xaml" />
    </ResourceDictionary.MergedDictionaries>
    <Style TargetType="{x:Type Ellipse}"/>
  </ResourceDictionary>
</Application.Resources>

И каждый StaticResource в .xamls, определенный в основной работе сборки приложения, стили по умолчанию работают благодаря фиктивному стилю. Что не работает, это StaticResources между .xamls внутри темы. Если я определяю стиль в Buttons.xaml, который использует StaticResource из Colors.xaml, я получаю сообщение об ошибке StaticResources и UnsetValue. Он работает, если я добавляю Colors.xaml в приложение MergedDictionaries.

Решение 0

Отказ от организации. Поместите все в один .xaml. Я считаю, что именно так, как правило, должны были использоваться ResourceDictionaries из-за всех "проблем" с MergedDictionaries (для меня это был бы кошмар).

Решение 1

Измените все ссылки на StaticResource для перекрестных ссылок внутри темы на DynamicResource. Он работает, но поставляется с ценой, поскольку DynamicResources "тяжелее", чем StaticResources.

Решение 2

В каждой теме .xaml, которая использует StaticResources из другого .xaml, добавьте этот другой ResourceDictionary в MergedDictionaries. Это означает, что Buttons.xaml, TextBoxes.xaml и другие будут иметь Colors.xaml в своих MergedDictionaries. Это приведет к тому, что Colors ResourceDictionary будет храниться в памяти в нескольких экземплярах. Чтобы избежать этого, вы можете захотеть заглянуть в SharedResourceDictionary.

Решение 3

По разным установкам ResourceDictionaries, разные вложения я придумал теорию:

Если StaticResource не найден выше в том же .xaml или в MergedDictionaries этого ResourceDictionary, он выполняется поиск в других MergedDictionaries верхнего уровня.

Я бы предпочел добавить в ApplicationResources только один .xaml, но обычно я использую два. Вам не нужно добавлять к ApplicationResources каждый .xaml, который у вас есть в Theme, просто - например, Controls.xaml(с любым вложением MergedDictionaries, но перекрестные ссылки между словарями Controls.xaml не разрешены) и Common.xaml который содержит все общие ресурсы управления. В случае, если Common.xaml также допускается вложенность, но не имеет перекрестных ссылок, не может быть отдельного Colors.xaml и Brushes.xaml, который использует Colors как StaticResources - тогда вам нужно будет добавить 3.xamls в Application MergedDictionaries.

Теперь я всегда использую третье решение, но я не считаю его идеальным и все еще хотел бы знать, есть ли лучший способ. Надеюсь, я правильно интерпретировал то, что вы назвали той же проблемой, что и моя.

Ответ 2

Мне пришлось вводить темы в наше приложение и сталкиваться с этими точными проблемами.

Короткий ответ таков: Словари ресурсов могут "видеть" другие словари ресурсов, если они появляются в них в App.xaml. Если вы попытаетесь использовать MergedDictiories в файле, который не является App.xaml, словари ресурсов не будут "видеть" друг друга.

Для ресурсов по умолчанию в Generic.xaml: вы можете использовать ресурсы, определенные в App.xaml, или объединенный словарь из App.xaml только как DynamicResource. Вы можете использовать ресурсы, определенные в Generic.xaml как StaticResource, но только если ваш стиль определен в Generic.xaml сам, а не в объединенном словаре внутри Generic.xaml

Для полного ответа У меня есть подробный пост в моем блоге об этой проблеме

Мое предлагаемое решение: Создайте любую иерархию XAML, которую вы хотите, и поместите свои файлы в папку с расширениями .txaml. Я создал небольшую простую программу (представленную ниже в GitHub), которая будет запускаться как событие предварительной сборки и объединить ваши .txaml файлы в один длинный файл .XAML.

Это позволяет структурировать ресурсы папок и файлов, но вы хотите, без ограничений WPF. StaticResource и дизайнер будут работать всегда. Это единственное решение, в котором вы можете иметь стили CustomControl в нескольких файлах, а не только один длинный Generic.xaml.

Это также решит любые проблемы с производительностью, создаваемые несколькими файлами XAML.

Программа слияния Xaml в GitHub

Ответ 3

В дополнение к @ lisp answer, я написал шаблон tt, который принимает все файлы из Default.xaml, находит их и присоединяется к одному файлу, что мы можем использовать в app.xaml

Итак, мы можем структурировать файлы, иметь производительность, статический ресурс будет работать...

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="System.Xml" #>
<#@ assembly name="System.Xml.Linq" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Xml.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".xaml" #>

<#
    IDictionary<string, XNamespace> GetNamespaces(XDocument doc)
    {
        return doc.Root.Attributes()
                    .Where(a => a.IsNamespaceDeclaration)
                    .GroupBy(a => a.Name.Namespace == XNamespace.None ? string.Empty : a.Name.LocalName, a=>XNamespace.Get(a.Value))
                    .ToDictionary(g => g.Key, g => g.First());
    }

    XDocument GetFlattenResourceDocument(string path)
    {
        var xFilePath = this.Host.ResolvePath(path);
        var doc = XDocument.Load(xFilePath);

        var defaultNs = doc.Root.GetDefaultNamespace();

        var mergedDictElement = doc.Root.Elements(defaultNs + "ResourceDictionary.MergedDictionaries").SingleOrDefault();
        if (mergedDictElement == null)
            return doc;

        var rootNamespaces = GetNamespaces(doc);

        var mergedResourceDictionaries = mergedDictElement.Elements(defaultNs + "ResourceDictionary");
        var addAfterElement = mergedDictElement as XNode;

        foreach(var resourceDict in mergedResourceDictionaries)
        {
            var sourcePath = resourceDict.Attribute("Source").Value;
            var flattenDoc = GetFlattenResourceDocument(sourcePath);

            var flatNamespaces = GetNamespaces(flattenDoc);

            foreach(var key in flatNamespaces.Keys)
            {
                if(!rootNamespaces.ContainsKey(key))
                {
                    var curNamespace = flatNamespaces[key];
                    doc.Root.Add(new XAttribute(XNamespace.Xmlns + key, curNamespace.ToString()));
                    rootNamespaces.Add(key, curNamespace);
                }
            }

            var startComment = new XComment($"Merged from file {sourcePath}");
            var endComment = new XComment($"");

            var list = new List<XNode>();
            list.Add(startComment);
            list.AddRange(flattenDoc.Root.Elements());
            list.Add(endComment);
            addAfterElement.AddAfterSelf(list);

            addAfterElement = endComment;

        }

        mergedDictElement.Remove();

        return doc;
    }
#>
<#= GetFlattenResourceDocument("Default.xaml").ToString() #>