【问题标题】:Styles, Templates, and ResourceDictionaries样式、模板和资源字典
【发布时间】:2016-03-01 22:30:18
【问题描述】:

我一直在做一些 WPF 项目,但我大部分时间都远离模板和资源字典,因为它们是一个集群#。为了更上一层楼,我做了很多谷歌搜索,试图找到使用资源字典等应用样式的最佳实践,但似乎最佳实践是基于习惯和偏好的。我遇到的大多数示例要么假设您已经了解所有内容,要么不包括所有不同的情况。最糟糕的是,有一些已知的错误和情况会影响性能,但了解这些问题是什么并纠正它们本身就是另一个麻烦。

对不起,你的咆哮。以下是我需要解决的情况:

假设我有一个默认的 rd(资源字典),它具有针对文本块、按钮、文本框等的样式。然后,我有另一个类似的资源字典,它适用于像 Expander(或 TabItem)这样的东西,它是那些的集合相同的控件(按钮、文本框、...)。

  1. 这样的设置可以吗?

  2. 如何覆盖位于特定扩展器内的控件的默认 rd?我尝试在扩展器的资源中添加对资源字典的引用;但是,默认 rd 似乎覆盖了该样式。

  3. 模板的用途是什么?您什么时候使用这些模板?

这是我目前的设置:

App.xaml:

<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="Resources/rdMainStyle.xaml" />
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Application.Resources>

rdMainStyle.xaml:

<Style x:Key="baseStyle" TargetType="FrameworkElement">
    <Setter Property="Width" Value="Auto"/>
    <Setter Property="Height" Value="Auto"/>
    <Setter Property="MinWidth" Value="70pt"/>
</Style>

<Style TargetType="{x:Type Button}" BasedOn="{StaticResource baseStyle}">
    <Setter Property="Margin" Value="5, 0, 0, 5"/>
    <Setter Property="HorizontalAlignment" Value="Center"/>
    <Setter Property="HorizontalContentAlignment" Value="Center"/>
</Style>
<Style TargetType="{x:Type TextBlock}" BasedOn="{StaticResource baseStyle}">
    <Setter Property="MinWidth" Value="65pt"/>
    <Setter Property="Margin" Value="5, 0, 0, 5"/>
</Style>

rdExpanderStyle.xaml:

<Style x:Key="expanderBaseStyle" TargetType="FrameworkElement">
    <Setter Property="Height" Value="Auto"/>
    <Setter Property="Margin" Value="5, 0, 0, 5"/>
</Style>

<Style TargetType="{x:Type Button}" BasedOn="{StaticResource expanderBaseStyle}">
    <Setter Property="Width" Value="70pt"/>
    <Setter Property="HorizontalAlignment" Value="Center"/>
    <Setter Property="HorizontalContentAlignment" Value="Center"/>
</Style>
<Style TargetType="{x:Type TextBlock}" BasedOn="{StaticResource expanderBaseStyle}">
    <Setter Property="Width" Value="65pt"/>
</Style>

主窗口内:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>

    <Expander Grid.Row="0" Name="expSync" Header="More Options">
        <Expander.Resources>
            <ResourceDictionary Source="Resources/rdExpanderStyle.xaml" />
        </Expander.Resources>
    </Expander>
    <StackPanel Margin="0,5,0,0">
        <TextBlock Text="bluh"/>
        <Button  Content="Test"  />
    </StackPanel>
</Grid>

【问题讨论】:

  • Sal 习惯上对有帮助的答案进行投票,并将对您的问题的答案标记为已接受。

标签: c# wpf templates styles resourcedictionary


【解决方案1】:
  1. 可以进行这种设置吗?

是的,没关系。事实上,样式/模板/主题/等。在 WPF 中都是关于这种层次结构的。 IE。能够在一个地方定义一个默认值,然后用具有更高优先级的新定义覆盖它。

  1. 如何覆盖位于特定扩展器内的控件的默认 rd?我尝试在扩展器的资源中添加对资源字典的引用;但是,默认的 rd 似乎覆盖了该样式。

问题与Expander.Header 属性的内容是一个简单的string 有关。这会映射到ContentPresenter 类的自定义样式(它本身是Expander 类的自定义样式的一部分),因此当您尝试使用本地声明的样式设置样式时会被忽略。您稍后尝试覆盖样式实际上在样式层次结构中太靠后了。全局样式有效,因为该样式比用于控件的自定义样式更早应用。

您可以通过为Expander 控件的内容提供更明确的定义来解决此问题。例如:

<Expander Grid.Row="0" Name="expSync">
  <Expander.Resources>
    <ResourceDictionary Source="Resources/rdExpanderStyle.xaml" />
  </Expander.Resources>
  <Expander.Header>
    <TextBlock Text="More Options"/>
  </Expander.Header>
</Expander>

通过显式提供TextBlock 作为内容,您会在rdExpanderStyle.xaml 文件导入的样式之后获得一个控件实例,并且它们将根据您的需要应用.

  1. 模板的用途是什么?您何时会使用这些模板?

对此的全面讨论超出了合理的 Stack Overflow 答案的范围。 MSDN、Stack Overflow 和其他地方提供了大量资源来描述如何在 WPF 中使用模板。也就是说……

模板的目的是为控件提供在实例化时遵循的模式。在某些情况下,您正在为特定控件声明模板,在其他情况下,您正在为“视图模型”数据类型声明模板。前一种情况是关于自定义现有控件类型的外观和行为;后一种情况是以抽象、解耦、可重用的方式将您的业务逻辑数据结构映射到视觉外观。

您什么时候使用它们?好吧,我会说目的回答了这个问题。如果您需要自定义模板化控件,您将使用模板来执行此操作。如果您需要将视图模型对象呈现为控件的组合,则可以使用模板来执行此操作(例如,当您的视图模型表示 ItemsControl 中的项目时)。我会说视图模型场景比通过模板自定义控件更普遍,但这不是基于确凿的证据;这只是我对情况的一般感觉。


最后,一点建议,我无意冒犯:

虽然您的咆哮是可以理解的,但它并不富有成效,而且更多的是您自己的经验不足,而不是 WPF 的实际质量。我知道,因为我自己也去过那里。是的,WPF 复杂且难学。是的,它确实有很多地方的行为不是立即直观或明显的。

但是:a)WPF 中模板和资源的状态甚至还没有接近(用你的话来说……我相信我明白了预期的意思)“一个集群#”;相反,这些是 WPF 的基本元素,如果非常复杂,设计也合乎逻辑,并且在正确使用时效果很好,并且 b) 实际上存在合理、客观的“最佳实践”用于处理样式和资源字典,在很大程度上基于支持 OOP 设计的同一类哲学(即尽可能广泛地定义,必要时专门化以避免基本定义中的特殊情况代码)。

(我应该就此打住,但不请自来的建议还伴随着一些不请自来的故事……:))

多年来,我发现自己在抱怨大量 API、平台等。但随着年龄的增长,抱怨越来越少,我不得不承认有很多我的咆哮实际上是关于我对我还没有完全理解的事情感到沮丧。是的,有例外……偶尔,我的一个咆哮仍然击中了一个公平的目标。但我主要发现学习一个新的编程环境需要调整态度,寻找驱动该环境设计的思维模式,并重新训练自己遵循这些相同的模式,这样我才能以正确的方式看待环境设计师看到了。

别担心。你还有机会吐槽。在我编程生涯的早期,我刚刚吸取了教训,如果你的程序不工作,它总是你的程序中的一个错误,而不是操作系统中的错误,当我发现自己为一台实验性计算机编写代码时仍然需要硬件工程师几乎每天都在摆弄它以保持其正常运行。我花了几天的时间来适应这样一个想法,即我刚刚学到的非常重要的一课需要更改为“它几乎总是你的程序中的一个错误”。那是几十年前的事了,从那以后计算机变得更加复杂,所以编程环境而不是你自己的程序有更多的机会出现缺陷。

但不要忘记:编写应用程序端代码的人比编写系统端的人犯错误的情况要普遍得多。如果不出意外,系统端代码的测试会更好很多,只是被大量像你一样的人使用。 :)

【讨论】:

  • 非常感谢您的详细回复。为了使代码易于阅读,我删除了扩展器上可用的大部分控件。我编辑了代码部分以显示其中的一些项目,以指出除了标题文本之外,所有控件也无法使用扩展器的样式。
  • 您添加到代码示例中的StackPanel 及其子项不应受到您包含在Expander.Resources 中的资源字典的影响。它们不是Expander 的可视子树的一部分。如果您希望这些资源应用于Grid 中的所有元素,则需要将资源加载到Grid.Resources 而不是Expander.Resources
  • 至于我的咆哮,我的问题主要是缺乏微软的组织和文档。我对 WPF 没有太多反对意见。它绝对是 Windows 开发所需要的,并且是一个巨大的飞跃。
  • 问题是,MS 懒于教育应该使用该技术的开发人员,如果它未能从开发社区获得足够的支持和牵引,MS 会简单地放弃对它的支持,例如多年来,他们拥有许多其他产品。我看起来像下面这样的@文章并变得更加偏执和自我意识到我没有遵循相同类型的做法,但是话又说回来,这不是来自 MS 的东西,并且缺乏经验不足的开发人员需要了解背后的整个逻辑的细节它。 projekt202.com/blog/2010/xaml-organization
  • 感谢您的快速回复;我会试试你的建议。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-18
  • 1970-01-01
  • 2011-04-06
相关资源
最近更新 更多