【问题标题】:WPF Dynamic Resource Lookup Behavior with FixedPage使用 FixedPage 的 WPF 动态资源查找行为
【发布时间】:2012-01-02 10:40:53
【问题描述】:

拥有以下非常简单的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>

在这里使用动态资源(我在更复杂的情况下实际上需要它)不起作用。使用静态资源将 TextBlocks 着色为所需的颜色。将资源移动到 FixedPage 的级别也可以解决问题。但我想在顶级元素上有一个通用资源字典(因为用户可以对颜色、字体等进行运行时更改)。将资源放在应用程序级别也确实有效。但这不是一个很好的选择。

任何人都知道为什么这不起作用。它与 TextBlock 向上的逻辑树有关吗?

MSDN Resources Overview 声明:

查找过程在设置属性的元素定义的资源字典中检查请求的键。

  • 如果元素定义了 Style 属性,则检查 Style 中的 Resources 字典。
  • 如果元素定义了 Template 属性,则检查 FrameworkTemplate 中的 Resources 字典。

然后查找过程向上遍历逻辑树,到达父元素及其资源字典。这一直持续到到达根元素为止。

我还尝试根据上面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 为空)。在 XAML 标记中使用静态资源时,一切正常。

【问题讨论】:

    标签: wpf resourcedictionary staticresource dynamicresource


    【解决方案1】:

    走好风格是一个好的设计,但不是必需的。为了制作基于动态颜色的同名(键控)画笔,我们可以使用动态颜色字典(NOT画笔字典)

    解决办法如下...

    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) });
      }
      

    现在,当您从下拉列表中选择颜色时,静态资源画笔上的动态颜色会发生变化。请注意,不涉及样式

    我希望这能回答你的问题。

    【讨论】:

    • 感谢您的详细回答。但是,这实际上并不是我想要的。我只想将资源放在顶级元素上。但是当使用 FixedDocument、FixedPage 等时,这似乎不适用于动态资源。我怀疑它与遍历逻辑父母以寻找资源有关。当我使用像 Window、Grid、StackPanel、Button、Textblock 这样的嵌套结构时,很容易在网格级别定义资源并使用 DynamicResource 引用它们。但在提供的 XAML 结构中(请参阅问题),由于某种原因,这不起作用。
    • 如前所述,我不想在应用程序级别使用资源。已经在该级别上定义了用于屏幕颜色设置的运行时资源。现在我想进行打印预览,用户可以在其中使用其他颜色设置来打印输出......当我在应用程序级别更改资源时,打印预览窗口之外的所有内容都会相应更改。这是不希望的。
    • 是的,这就是我试图回答的问题......在我的示例中,发生动态颜色变化是因为画笔保持引用,颜色按值变化。
    • 嗯。我感觉我们不太了解对方。如果您查看我发布的两个 XAML 示例。一个资源解析成功而另一个不成功的原因是什么?和FixedDocument、DocumentViewer 有关系吗?它是一个错误吗?这些示例本质上非常简单。
    【解决方案2】:

    顺便说一句:这篇文章的原因有一些更复杂的背景。我真的很想使用单个资源字典,从而可以在运行时(由最终用户)动态更改颜色、字体、字体大小等。我正在处理税表申请。屏幕上显示的供用户输入的税表正在应用程序级别解决他们的资源。到目前为止一切顺利。

    但同时我想向用户展示税表的打印预览,其中使用相同的资源字典(作为对象类型,而不是作为对象实例)来定义配色方案、字体、字体大小等用于打印。这可能与用于屏幕用户输入的样式不同。这就是为什么我想在(例如)FixedDocument 级别上“附加”资源字典,以便文档中的所有页面都可以引用它。当更改颜色、字体等时,所有具有公共元素(TextBlocks、TextEditors 等)的页面都会响应更改。

    然后我被卡住了……如本文所述。

    现在我有一个很好的解决方法,方法是创建资源字典的特定实例,将其存储为私有变量并将其附加到我放入固定文档中的每个单独页面。它有效,这已经让我高兴了。但是我的程序员的心还是有点痛,因为我宁愿使用资源字典的单个实例,并将它放在某个顶级控件中,以便其中的所有控件都可以使用它。

    作为一名程序员,我们也必须接受解决方法;)如果您有任何进一步的建议,我愿意接受。感谢您迄今为止的支持。

    问候,乔普。

    另见:MSDN Forum Post about this issue

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-10-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-16
      相关资源
      最近更新 更多