Динамический поиск ресурсов в WPF с помощью FixedPage

Имея следующий очень простой xaml:

<DocumentViewer Name="dv">
    <FixedDocument Name="fd" Loaded="fd_loaded">
        <FixedDocument.Resources>
            <Style x:Key="TestStyle">
                <Style.Setters>
                    <Setter Property="TextBlock.Foreground" Value="BlueViolet"/>
                </Style.Setters>
            </Style>
            <SolidColorBrush x:Key="foregroundBrush" Color="Orange"/>
        </FixedDocument.Resources>
        <PageContent Name="pc">
            <FixedPage Name="fp" Width="800" Height="600" Name="fp">
                <TextBlock Name="tb" Style="{DynamicResource TestStyle}">
                        Lorem ipsum
                </TextBlock>
                <TextBlock Foreground="{DynamicResource foregroundBrush}" Margin="20">
                        Lorem ipsum
                </TextBlock>
            </FixedPage>
        </PageContent>
    </FixedDocument>
</DocumentViewer>

Использование динамических ресурсов (которые мне действительно нужны в более сложной ситуации) здесь не работает. Используя Static Resources, цвета TextBlocks получают желаемыми цветами. Перемещение ресурсов на уровень FixedPage также делает свое дело. Но я хотел бы иметь один общий словарь ресурсов для элемента верхнего уровня (из-за изменений во время выполнения, которые пользователь может сделать для цветов, шрифтов и т. Д.). Размещение ресурсов на уровне приложения также работает. Но это не вариант по веским причинам.

Кто-нибудь знает, почему это не работает. Это как-то связано с логическим деревом от TextBlock и выше?

Обзор ресурсов MSDN гласит:

Процесс поиска проверяет запрошенный ключ в словаре ресурсов, заданном элементом, который устанавливает свойство.

  • Если элемент определяет свойство Style, проверяется словарь ресурсов в Style.
  • Если элемент определяет свойство Template, проверяется словарь ресурсов в FrameworkTemplate.

Затем процесс поиска переходит логическое дерево вверх к родительскому элементу и его словарю ресурсов. Это продолжается до тех пор, пока не будет достигнут корневой элемент.

Я также попытался поместить кисть и стиль в ресурсы (фиктивного) стиля в соответствии с приведенным выше объяснением MSDN. Но это тоже не сработало.

Действительно есть ощущение, что это не может быть так сложно, но, скорее всего, я что-то наблюдаю. Любая помощь приветствуется.

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

При присвоении TextBlock имени "tb" и последующем использовании tb.FindResource ("TestStyle") выдается исключение. Так что этот ресурс явно не может быть найден. Если я проверяю LogicalTreeHelper.GetParent (tb) и повторяю, что для найденных родителей я получаю ожидаемый результат: TextBlock> FixedPage> PageContent> FixedDocument...

EDIT2

Это работает отлично. Какая разница с прогнозируемым ранее XAML?

<Window x:Class="WpfDynamicStyles2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.Resources>
            <SolidColorBrush x:Key="resBrush" Color="Orange"></SolidColorBrush>
        </Grid.Resources>
            <StackPanel>
            <Button>
                <TextBlock Foreground="{DynamicResource resBrush}">Dummy text...</TextBlock>
            </Button>           
        </StackPanel>
    </Grid>
</Window>

EDIT3

private void fd_Loaded(object sender, RoutedEventArgs e)
{
    Object obj = pc.TryFindResource("foregroundBrush");
    obj = fp.TryFindResource("foregroundBrush");
    obj = tb.TryFindResource("foregroundBrush");
}

Динамический ресурс, размещенный в свойстве Foreground текстового поля, не может быть разрешен (фактический ресурс находится на уровне FixedDocument.Resources). Также использование TryFindResource в коде позади работает с pc (PageContent), но с fp (FixedPage) и tb (TextBlock) не может разрешить ресурс (obj равен null). При использовании статического ресурса в разметке XAML все работает нормально.

2 ответа

Решение

Кстати: причина этого поста была более сложная. Я действительно хотел использовать один словарь ресурсов, с помощью которого я мог бы динамически изменять цвета, шрифты, размеры шрифтов и т. Д. Во время выполнения (конечным пользователем). Я работаю над заявкой на налоговую форму. А налоговые формы, представленные на экране для пользовательского ввода, позволяют определить их ресурсы на уровне приложения. Все идет нормально.

Но в то же время я хочу предоставить пользователю предварительный просмотр налоговой формы, в которой тот же ресурсный ресурс (как тип объекта, а не экземпляр объекта) используется для определения цветовых схем, шрифтов, размеров шрифтов и т. Д., Которые будут использоваться для печати., Который может отличаться от стиля, используемого для пользовательского ввода на экране. Вот почему я хотел "прикрепить" словарь ресурсов к (например) уровню FixedDocument, чтобы все страницы документа ссылались на него. И при изменении цветов, шрифтов и т. Д. Все страницы с общими элементами (TextBlocks, TextEditors и т. Д.) Будут реагировать на изменения.

И тогда я застрял... как описано в этом посте.

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

Как программист, мы должны жить с обходными путями;) Если у вас есть какие-либо дополнительные предложения, я открыт для их получения. Спасибо за вашу поддержку до сих пор.

Привет, Джоуп.

Также смотрите: MSDN Forum Post об этой проблеме

Хорошо придерживаться стиля - хороший дизайн, но не обязательно. Для создания динамических цветных кистей с одинаковыми именами (ключами) мы можем использовать словари динамических цветов (НЕ словари кистей)

Решение может быть как ниже...

  1. Создайте единый словарь ресурсов кисти.
  2. Ссылайтесь на цвет кисти DynamicResource приписывать.
  3. Создайте несколько словарей ресурсов с одним и тем же ключевым ресурсом Color в каждом из них.
  4. В зависимости от требований пользователя очистите и добавьте в Application.Current.resources.MergedDictionaries,

пример

  1. Сделайте проект WPF с окном, которое имеет следующий XAML....

     <Window.Resources>
       <ResourceDictionary>
         <ResourceDictionary.MergedDictionaries>                
            <ResourceDictionary Source="Resources/Window11Resources.xaml"/>
         </ResourceDictionary.MergedDictionaries>
       </ResourceDictionary>
     </Window.Resources>
     <DockPanel LastChildFill="True">
       <ComboBox DockPanel.Dock="Top" VerticalAlignment="Top"
              SelectionChanged="ComboBox_SelectionChanged"
              SelectedIndex="0">
        <ComboBox.ItemsSource>
            <Collections:ArrayList>
                <System:String>Orange</System:String>
                <System:String>Red</System:String>
                <System:String>Blue</System:String>
            </Collections:ArrayList>
        </ComboBox.ItemsSource>
    </ComboBox>
    
    <DocumentViewer>
        <FixedDocument>
            <PageContent>
                <FixedPage Width="793.76" Height="1122.56">
                    <TextBlock
                          FontSize="30"
                          Foreground="{StaticResource LabelColorBrush}"
                          Text="Test"/>
                </FixedPage>
            </PageContent>                
        </FixedDocument>
    </DocumentViewer>        
    

Если вы наблюдаете, что окно не должно использовать что-то динамическое. Все ссылки могут оставаться статическими. Так LabelColorBrush можно найти в словаре Window11Resources.xaml

  1. В Window11Resources.xaml В словарь добавить динамическую кисть цвета.

     <SolidColorBrush x:Key="LabelColorBrush"
                  Color="{DynamicResource DynamicColor}"/>
    
  2. Добавьте в свою папку следующие 3 словаря цветных кистей...

     <!-- Name = OrangeColorResource.xaml -->
     <ResourceDictionary 
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
         <Color x:Key="DynamicColor">Orange</Color>
    </ResourceDictionary>
    
     <!-- Name = BlueColorResource.xaml -->
     <ResourceDictionary 
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
         <Color x:Key="DynamicColor">Blue</Color>
    </ResourceDictionary>
    
     <!-- Name = RedColorResource.xaml -->
     <ResourceDictionary 
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
         <Color x:Key="DynamicColor">Red</Color>
    </ResourceDictionary>
    

Обратите внимание, что ключ остается прежним, т.е. DynamicColor,

  1. Теперь очистите и воссоздайте цветовые словари в App.Resources, Я сделал это в коде позади Window.xaml.cs

    private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        Application.Current.Resources.MergedDictionaries.Clear();
        Application.Current.Resources.MergedDictionaries.Add(
           new ResourceDictionary()
           { 
              Source = new Uri("Resources\\"
                               + ((ComboBox)sender).SelectedItem.ToString()
                               + "ColorResource.xaml",
                               UriKind.RelativeOrAbsolute) });
    }
    

Теперь, когда вы выбираете цвет из выпадающего списка, динамический цвет изменяется на кисти статического ресурса. Обратите внимание, что здесь нет стиля.

Я надеюсь, что это отвечает на ваши вопросы.

Другие вопросы по тегам