自动应用在单独的程序集中定义的默认样式

时间:2012-11-21 18:18:02

标签: wpf

我有以下要求:

  1. 我的WPF应用程序包含多个模块(程序集),其中一些与UI相关。

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

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

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

  5. 我希望在最终应用程序和其他与UI相关的模块中,也可以在Visual Studio 设计时中使用它。

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

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

3 个答案:

答案 0 :(得分: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生成实际加载XAML内容的CustomStyles()构造函数,因此您可能需要添加{ {1}}手动文件,并为其提供一个调用CustomStyles.xaml.cs的构造函数(VS仍应生成此代码):

InitializeComponent()

在您的应用程序中,您需要将此词典合并到namespace YourNamespace { partial class CustomStyles { public CustomStyles() { InitializeComponent(); } } } 词典中。如果您愿意,可以从Application.Resources文件执行此操作:

App.xaml

... 您可以在C#端进行此操作:

<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>

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

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

然后,在您的视图上,只需设置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),并且您不会在每个视图中最终加载样式的新副本;您只需从应用级资源继承它们。

答案 1 :(得分:0)

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

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

步骤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: edit resource in Blend

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

答案 2 :(得分:0)

对于我一直在努力的PRISM应用程序,我一直在努力解决这些问题。在做了一些堆栈流量研究之后,我惊讶地发现在设计时让资源工作的最简单的答案是为我的每个基于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>

建立我之前的知识,我整理了blog post详细介绍了我在PRISM中使用资源词典的经验。