使用固定页面的 WPF 动态资源查找行为
有以下非常简单的 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>
在这里使用动态资源(我在更复杂的情况下实际上需要它)是行不通的。使用静态资源将 TextBlock 着色为所需的颜色。将资源移动到固定页面的级别也可以达到目的。但我希望在顶级元素上有一个通用资源字典(因为用户可以对颜色、字体等进行运行时更改)。将资源放置在应用程序级别也确实有效。但出于充分的理由,这不是一个选择。
任何人都知道为什么这不起作用。它与从 TextBlock 向上的逻辑树有什么关系吗?
MSDN 资源概述 指出:
查找过程会检查所请求的密钥由设置属性的元素定义的资源字典。
- 如果元素定义了 Style 属性,则检查 Style 中的资源字典。
- 如果元素定义了 Template 属性,则检查 FrameworkTemplate 内的 Resources 字典。
然后,查找过程向上遍历逻辑树,到达父元素及其资源字典。这一直持续到到达根元素。
我还尝试根据MSDN的上述解释将画笔和样式放入(虚拟)样式的资源中。但这也不起作用。
确实感觉这不会那么复杂,但很可能是我监督了一些事情。任何帮助表示赞赏。
编辑
将 TextBlock 命名为“tb”,然后使用 tb.FindResource("TestStyle") 会引发异常。所以这个资源显然是找不到的。如果我检查 LogicalTreeHelper.GetParent(tb) 并为找到的父母重复该操作,我会得到预期的结果: TextBlock >固定页>页面内容>固定文档...
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 标记中使用静态资源时,一切正常。
Having the following very simple 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>
The use of Dynamic Resources (which I actually need in a more complex situation) here doesn't work. Using Static Resources colors the TextBlocks in the desired colors. Moving the Resources to the level of the FixedPage also does the trick. But I would like to have one generic resource dictionary on a top level element (because of runtime changes the user can make for colours, fonts, etc.). Placing the resources on Application level also does work. But it's not an option for good reasons.
Anybody have any clue why this doesn't work. Does it have something to do with the Logical Tree from the TextBlock upwards?
MSDN Resources Overview states that:
The lookup process checks for the requested key within the resource dictionary defined by the element that sets the property.
- If the element defines a Style property, the Resources dictionary within the Style is checked.
- If the element defines a Template property, the Resources dictionary within the FrameworkTemplate is checked.
The lookup process then traverses the logical tree upward, to the parent element and its resource dictionary. This continues until the root element is reached.
I also tried putting the Brush and the Style into the Resources of a (dummy) Style according to the above explanation of MSDN. But that didn't work either.
Really have the feeling that this can not be that complex, but most probably I oversee something. Any help is appreciated.
EDIT
When naming the TextBlock to "tb" and then using tb.FindResource("TestStyle") throws an exception. So that resource clearly can't be found. If I check out LogicalTreeHelper.GetParent(tb) and repeat that for the parents found I get the expected result: TextBlock > FixedPage > PageContent > FixedDocument ...
EDIT2
This works perfect. What's the difference with the XAML projected earlier?
<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");
}
The dynamic resource placed on the Foreground property of the textbox cannot be resolved (the actual resource is at the FixedDocument.Resources level). Also using the TryFindResource in code behind works from pc (PageContent) but from fp (FixedPage) and tb (TextBlock) it cannot resolve the resource (obj is null). When using a Static Resource in the XAML Markup everything works fine.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
走好风格之路是一个很好的设计,但不是必需的。要制作基于动态颜色的同名(键控)画笔,我们可以使用动态颜色字典(NOT画笔字典)。
解决方案如下...
DynamicResource
属性引用画笔中的颜色。Application.Current.resources.MergedDictionaries
中。示例
使用具有以下 XAML 的窗口创建一个 WPF 项目...
如果您观察到该窗口不需要使用动态的任何内容。所有引用都可以保持静态。所以
LabelColorBrush
可以在字典Window11Resources.xaml
中找到在
Window11Resources.xaml
字典中添加动态颜色画笔。在项目的某个文件夹中添加以下 3 个颜色画笔字典...
请注意,键保持不变,即
DynamicColor
。现在在
App.Resources
中清除并重新创建颜色字典。我已经在Window.xaml.cs
的代码后面做到了这一点
现在,当您从下拉列表中选择颜色时,静态资源画笔上的动态颜色会发生变化。请注意,不涉及样式。
我希望这能回答您的问题。
Well going the style way is a good design but not necessary. To make dynamic color based same named (keyed) brushes, we can use as dynamic color dictionaries (NOT brush dictionaries)
The solution can be as below ...
DynamicResource
attribute.Application.Current.resources.MergedDictionaries
.Example
Make a WPF project with a window that has following XAML....
If you observe the window doesnt need to use anything which is dynamic. All refernces can remain static. So
LabelColorBrush
can be found in dictionaryWindow11Resources.xaml
In
Window11Resources.xaml
dictionary add a dynamic color brush.Add following 3 color brush dictionaries in some folder from your project...
Note that the key remains the same i.e.
DynamicColor
.Now clear and recreate color dictionaries in
App.Resources
. I have done that in the code behind ofWindow.xaml.cs
Now as and when you select a color from the drop down, the dynamic color changes on the static resource brush. Note that there is no Style involved.
I hope this answers what you are asking.
顺便说一句:写这篇文章的原因有一些更复杂的背景。我真的很想使用一个资源字典,这样我就可以在运行时(由最终用户)动态更改颜色、字体、字体大小等。我正在处理税务表格申请。屏幕上显示的供用户输入的税表正在应用程序级别解析其资源。到目前为止,一切都很好。
但同时我想向用户呈现税表的打印预览,其中使用相同的资源字典(作为对象类型,而不是对象实例)来定义用于打印的配色方案、字体、字体大小等。这可能与屏幕用户输入所使用的样式不同。这就是为什么我想将资源字典“附加”到(例如)FixedDocument 级别,以便文档中的所有页面都可以引用它。当更改颜色、字体等时,所有具有公共元素(文本块、文本编辑器等)的页面都会响应更改。
然后我陷入困境......正如这篇文章中所描述的那样。
现在,我有一个很好的解决方法,通过创建资源字典的特定实例,将其存储为私有变量并将其附加到我放入固定文档中的每个单独页面。它有效并且已经让我很高兴。但我的程序员心还是有点痛,因为我更喜欢使用资源字典的单个实例,并将其放在某个顶级控件中,以便其中的所有控件都可以使用它。
作为一名程序员,我们也必须接受解决方法;)如果您有任何进一步的建议,我很乐意接受。感谢您迄今为止的支持。
再见,乔普。
另请参阅:MSDN 论坛帖子这个问题
By the way: the reason for this post had some more complex background. I really wanted to use a single resource dictionary whereby I could change colors, fonts, fontsizes, etc. dynamically during runtime (by an end-user). I am working on a tax form application. And the tax forms presented on screen for user input are resolving their resources at the Application level. So far so good.
But at the same time I want to present the user a print preview of the tax form where the same resourcedictionary (as object type, not as object instance) is used to define color schemes, fonts, fontsizes, etc. to be used for printing. Which can differ from the styling used for on screen user input. That's why I wanted to "attach" the resource dictionary on (for example) the FixedDocument level so that all pages in the document would refer to it. And when changing colors, fonts etc. all pages with common elements (TextBlocks, TextEditors, etc.) would respond to the change.
And then I became stuck...as described in this post.
For now I have a nice workaround by creating a specific instance of the resourcedictionary, storing it as a private variable and attach it to every individual page I put in the fixed document. It works and that already pleases me. But my programmers heart is still a little ached, because I would prefer to use a single instance of the resource dictionary and put it at some top level control so all controls in it can use it.
As a programmer we have to live with workarounds as well ;) If you have any further suggestions, I'm open to receive them. Thanx for your support thus far.
Greetz, Joep.
Also see: MSDN Forum Post about this issue