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

tli*_*ind 2 wpf

我有以下要求:

  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?我想我必须以声明方式执行此操作,因为我需要对自定义样式的设计时支持.

Mik*_*bel 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>
Run Code Online (Sandbox Code Playgroud)

对于用于替换内置或第三方控件样式的样式,可以将样式声明为隐式,即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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在您的应用程序中,您需要将此词典合并到您的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>
Run Code Online (Sandbox Code Playgroud)

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

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

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

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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