Почему (Style)Application.Current.Resources[FrameBorder] не позволяет мне получить доступ к ресурсу в моем ResourceDirectory

Я создал ресурс в файле FrameRes.xaml следующим образом:

<ResourceDictionary xmlns="http://xamarin.com/schemas/2014/forms" 
                    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
                    x:Class="Japanese.Resources.FrameRes">
    <Style x:Key="FrameBorder" TargetType="Frame">
        <Setter Property="CornerRadius" Value="2" />
        <Setter Property="HasShadow" Value="false" />
        <Setter Property="Margin" Value="10,0" />
        <Setter Property="BorderColor" Value="{DynamicResource LineColor}" />
        <Setter Property="Padding" Value="0" />
        <Setter Property="VerticalOptions" Value="Start" />
    </Style>
</ResourceDictionary>

и это содержится внутри:

<?xml version="1.0" encoding="utf-8"?>
<Application xmlns:converters="clr-namespace:Japanese" 
             xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             x:Class="Japanese.App">
    <Application.Resources>
        <ResourceDictionary Source="/Resources/FooterRes.xaml" />
        <ResourceDictionary Source="/Resources/FrameRes.xaml" />
    </Application.Resources>
</Application>

Когда я пытаюсь получить доступ к этому на С#, как это, он терпит неудачу и говорит, что нет ссылки на FrameBorder:

var fb2 = (Style)Application.Current.Resources["FrameBorder"];

Когда я пытаюсь получить доступ к этому на С#, он работает:

Application.Current.Resources.TryGetValue("FrameBorder", out object frameBorder);
var fb = (Style)frameBorder;

Кто-нибудь знает, почему первый способ не работает. Мне он кажется таким же.


person Alan2    schedule 24.11.2018    source источник
comment
Я столкнулся с этой проблемой и разместил решение здесь: stackoverflow.com/a/67992365/6846888   -  person Ângelo Polotto    schedule 15.06.2021


Ответы (2)


То, как вы это настроили, у вас есть ResourceDictionary внутри другого ResourceDictionary без ключа/имени/ссылки. Когда вы вызываете Application.Current.Resources["FrameBorder"];, вы обращаетесь к самому верхнему уровню словаря и ищете "FrameBorder", а не его подуровни. Однако вызов TryGetValue проходит через все уровни Словаря.

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


Итак, я поиграл с тегами и файлами ResourceDictionary, и я вижу, где вы ошибаетесь.

Согласно Xamarin Forms Docs на ResourceDictionary, вы можете добавить автономные файлы ResourceDictionary XAML в свой App.xaml следующим образом:

<App ...>
    <App.Resources>

            <!-- Add more resources here -->

            <ResourceDictionary Source="MyResourceDictionary.xaml" />

            <!-- Add more resources here -->

    </App.Resources>
    ...
</App>

Вы можете заменить App на ContentPage или ContentView, в зависимости от вашего использования.

Что это делает?

Проще говоря, это создает новый объект ResourceDictionary и объединяет другие файлы ResourceDictionary в него. Давайте углубимся в детали.

Начнем со свойства Application.Current.Resources, которое имеет тип ResourceDictionary и реализует ICollection, IDictionary и IResourceDictionary. Учитывая интерфейсы, которые он реализует, вы можете получить значения по числовому или строковому идентификатору (например: App.Current.Resources[0] или App.Current.Resources["LabelStyle"]). Это обеспечивает доступ к содержимому словаря верхнего уровня, то есть только к тем, которые были созданы в теге <[App/ContentPage/ContentView].Resources>.

У вас также есть свойство MergedDictionaries, которое реализует ICollection. Это означает, что вы можете получить доступ к элементам списка только по числовому идентификатору (например: MyMergedDictionary[0]).

Когда вы добавляете файл ResourceDictionary, например:

<ResourceDictionary Source="MyResourceDictionary.xaml" />

На самом деле вы объединяете этот файл с текущим ResourceDictionary. Чтобы затем получить доступ к содержимому MyResourceDictionary, вы должны позвонить (в C#):

App.Current.Resources.MergedDictionaries[0]["InternalKeyName"]

Вероятно, это лучше объяснить так:

<App ...>
    <App.Resources>

            <!-- Called using App.Current.Resources["PrimaryColor"] -->
            <Color x:Key="PrimaryColor">#2196F3</Color>

            <!-- Called using App.Current.Resources.MergedDictionaries[0]["InternalKeyName"] -->
            <ResourceDictionary Source="MyResourceDictionary.xaml" />

    </App.Resources>
    ...
</App>

Ваш случай

В вашем случае у вас есть два словаря, объединенных в ваши ресурсы приложения: FooterRes и FrameRes.

Для лучшего управления я бы создал enum вроде:

public enum MergedResourcesEnum
{
    Footer,
    Frame
}

и используйте это перечисление при вызове ресурсов на С# следующим образом:

Application
    .Current
    .Resources
    .MergedDictionaries[(int)MergedResourcesEnum.Frame]["FrameBorder"];
person Tom    schedule 26.11.2018
comment
Я проверил документы, но до сих пор не уверен, как получить доступ к значениям из файла, который я объединил в свой каталог ресурсов. Обратите внимание, что, поскольку я разместил вопрос, я попытался удалить самый внешний ResourceDirectory, как вы предложили, но он все еще не работает. - person Alan2; 29.11.2018
comment
Хорошо, я перешел к документам Xamarin Forms и изменил свой ответ, добавив немного больше информации. - person Tom; 29.11.2018
comment
Это должен быть принятый ответ. Я инженер службы поддержки MSFT Xamarin. - person jgoldberger - MSFT; 01.12.2018

Вы можете попробовать удалить внешний тег ResourceDictionary и оба

var fb2 = (Style)Application.Current.Resources["FrameBorder"];

и

Application.Current.Resources.TryGetValue("FrameBorder", out object frameBorder);
var fb = (Style)frameBorder;

работать правильно.

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

(Пожалуйста, игнорируйте несвязанное имя пакета и FooterRes.xaml, потому что вы не предоставили общий доступ к файлу FooterRes.xaml)

<Application xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="App75.App">
    <Application.Resources>
        <ResourceDictionary Source="/Resources/FooterRes.xaml" />
    </Application.Resources>
</Application>
person AbbyWang - MSFT    schedule 28.11.2018
comment
Я пытался сделать это, и он все еще не может найти ресурс - person Alan2; 29.11.2018
comment
@Alan2 Alan2 Поскольку у меня нет полного кода, я только что создал простую демонстрацию, и она работала нормально, используя мое решение. Не могли бы вы поделиться своим проектом, чтобы мне было проще решить эту проблему. - person AbbyWang - MSFT; 29.11.2018