2

我有以下要求:

  1. 我的 WPF 应用程序由几个模块(程序集)组成,其中一些与 UI 相关。

  2. 我想为某些控件创建一个包含一组通用样式(例如自定义默认按钮样式)的单个程序集,这些控件应该自动应用于所有其他与 UI 相关的程序集,只需包含一个程序集,而无需我指定显式资源键。

  3. 我没有为每种控件提供样式,所以那些没有自定义样式的应该保留默认的 Aero 主题(包括内容模板等)。

  4. 不想编写自己的扩展 Button 类或类似的东西。

  5. 我希望它在设计时也能在 Visual Studio 中工作,无论是在最终应用程序中还是在其他与 UI 相关的模块中。

由于样式是在程序集中定义的,因此我显然不能在那里有 App.xaml。因此,我假设我必须从 Generic.xaml 中包含它们。由于 Generic.xaml 仅在标准 (Aero) 主题中未定义样式时用作后备,因此 WPF 会忽略我在 Generic.xaml 中的样式。

下一步可能是创建我自己的主题(以某种方式合并默认的 Aero 样式)。但是我如何告诉 VS 在应用程序和模块中使用该主题,而不是例如 Aero?我想我必须以声明方式执行此操作,因为我需要为我的自定义样式提供设计时支持。

4

3 回答 3

5

简单地添加对样式程序集的引用是不够的;你必须做一些事情来让 WPF 合并资源。但是我们可以这样做,你只需要在你的应用程序程序集中添加一行 C#(或几行 XAML)。

最直接的解决方案可能是在您的共享样式程序集中创建一个强类型,并在启动时ResourceDictionary将其添加到您的应用程序级别。ResourceDictionary

例如,CustomStyles.xaml在您的共享样式程序集中创建一个,并将所有样式资源拉入该文件(直接或通过MergedDictionaries)。确保 Build Action 设置为“Page”,并向元素添加x:Class指令,如下所示:ResourceDictionary

<ResourceDictionary x:Class="YourNamespace.CustomStyles"
                    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <!-- Your styles declared or imported here -->
</ResourceDictionary>

对于旨在替换内置或第三方控件样式的样式,您可以将样式声明为隐式,即x:Key完全不使用该样式,或使用控件的类型作为键,例如x:Key="{x:Type ComboBox}".

添加x:Class指令可能不足以使 Visual Studio 生成CustomStyles()实际加载 XAML 内容的构造函数,因此您可能需要CustomStyles.xaml.cs手动添加文件并为其提供调用的构造函数InitializeComponent()(VS 仍应生成此内容):

namespace YourNamespace
{
    partial class CustomStyles
    {
        public CustomStyles()
        {
            InitializeComponent();
        }
    }
}

在您的应用程序中,您需要将此字典合并到您的Application.Resources字典中。如果您愿意,可以从App.xaml文件中执行此操作:

<Application x:Class="YourNamespace.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:cs="clr-namespace:YourNamespace;assembly=YourCustomStylesAssembly">
  <Application.Resources>
    <ResourceDictionary>
      <ResourceDictionary.MergedDictionaries>
        <cs:CustomStyles />
      </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
  </Application.Resources>
</Application>

...或者您可以在 C# 端执行此操作:

public partial class App
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        this.Resources.MergedDictionaries.Add(new CustomStyles());
    }
}

现在,棘手的部分是让这些样式在 XAML 设计器中工作。想到的一种解决方案是添加一个自定义附加属性,您可以在所有视图上设置该属性,并且在您在设计器中运行时应用:

partial class CustomStyles
{
    public static readonly DependencyProperty EnableDesignTimeStylesProperty =
        DependencyProperty.RegisterAttached(
            "EnableDesignTimeStyles",
            typeof(bool),
            typeof(CustomStyles),
            new PropertyMetadata(
                default(bool),
                OnEnableDesignTimeStylesChanged));

    private static CustomStyles DesignTimeResources;

    private static void OnEnableDesignTimeStylesChanged(
        DependencyObject d,
        DependencyPropertyChangedEventArgs e)
    {
        if (!DesignerProperties.GetIsInDesignMode(d))
            return;

        var element = d as FrameworkElement;
        if (element == null)
            return;

        if (DesignTimeResources == null)
            DesignTimeResources = new CustomStyles();

        if ((bool)e.NewValue)
            element.Resources.MergedDictionaries.Add(DesignTimeResources);
        else
            element.Resources.MergedDictionaries.Remove(DesignTimeResources);
    }

    public static void SetEnableDesignTimeStyles(
        DependencyObject element,
        bool value)
    {
        element.SetValue(EnableDesignTimeStylesProperty, value);
    }

    public static bool GetEnableDesignTimeStyles(DependencyObject element)
    {
        return (bool)element.GetValue(EnableDesignTimeStylesProperty);
    }
}

然后,在您的视图上,只需设置CustomStyles.EnableDesignTimeStyles="True"强制设计器合并样式资源即可。在运行时,DesignerProperties.GetIsInDesignMode(d)将评估为false,并且您最终不会在每个视图中加载样式的新副本;您只需从应用级资源继承它们。

于 2014-10-17T15:29:32.687 回答
0

对于我一直在研究的 PRISM 应用程序,我一直在努力解决这些相同的问题。在做了一些 stack over flow 研究之后,我惊讶地发现让资源在设计时工作的最简单的答案是在我的每个基于 UI 的模块中添加一个 App.Xaml。当应用程序被编译时,它们都将被忽略。但是在设计时它们将被设计师使用。按照上述建议的其余部分,您将拥有一个 App.Xaml,其中包含一个合并的资源字典,该字典指向具有您所有样式的资源库。

这是我发现在设计期间获得样式的最简单方法。

<Application x:Class="ProjHydraulics.App"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

<Application.Resources>

    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary  Source= "pack://application:,,,/Infrastructure;component/ResourceDictionaries/ResourceLibrary.xaml"/>
        </ResourceDictionary.MergedDictionaries>
        <Style TargetType="{x:Type Rectangle}"/>
    </ResourceDictionary>

</Application.Resources>

建立我之前的知识,我整理了一篇博客文章,详细介绍了我在 PRISM 中使用资源字典的经验。

于 2014-10-20T04:55:14.003 回答
0

我不知道如何自动应用它们。事实上,我认为“自动、设计师支持和多个组件”的组合是不可能的。但是,为每个控件添加标题引用很容易:

第 1 步:将所有样式合并或添加到所有其他项目引用的“样式”项目中的字典中。

第 2 步:在您的每个控件和其他资源字典 XAML 文件中包含对此字典的引用。它看起来像这样:

<ResourceDictionary.MergedDictionaries>
   <SharedResourceDictionary Source="pack://application:,,,/My.Ui.Resources;component/Themes/ColorSkins/LightTheme.xaml" />
...

请注意使用 SharedResourceDictionary 来不重复实例。请参阅 http://blogs.msdn.com/b/wpfsdk/archive/2007/06/08/defining-and-using-shared-resources-in-a-custom-control-library.aspx

SharedResourceDictionary:在 Blend 中编辑资源

如果您的所有控件都继承自同一个基类,则创建您自己的以编程方式包含它们的基类可能会很有用。

于 2014-10-17T15:42:22.453 回答