Динамический поиск ресурсов в 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 об этой проблеме
Хорошо придерживаться стиля - хороший дизайн, но не обязательно. Для создания динамических цветных кистей с одинаковыми именами (ключами) мы можем использовать словари динамических цветов (НЕ словари кистей)
Решение может быть как ниже...
- Создайте единый словарь ресурсов кисти.
- Ссылайтесь на цвет кисти
DynamicResource
приписывать. - Создайте несколько словарей ресурсов с одним и тем же ключевым ресурсом Color в каждом из них.
- В зависимости от требований пользователя очистите и добавьте в
Application.Current.resources.MergedDictionaries
,
пример
Сделайте проект 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
В
Window11Resources.xaml
В словарь добавить динамическую кисть цвета.<SolidColorBrush x:Key="LabelColorBrush" Color="{DynamicResource DynamicColor}"/>
Добавьте в свою папку следующие 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
,
Теперь очистите и воссоздайте цветовые словари в
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) }); }
Теперь, когда вы выбираете цвет из выпадающего списка, динамический цвет изменяется на кисти статического ресурса. Обратите внимание, что здесь нет стиля.
Я надеюсь, что это отвечает на ваши вопросы.