WPF类库中的程序集级/根级样式

51
我有一个 C# (2008/.NET 3.5) 类库程序集,支持 WPF (基于这篇文章)。
我创建了几个窗口,现在尝试为它们创建一个共同的样式集。然而,由于它是一个类库(而不是WPF应用程序),我没有一个 app.xaml(和它所包含的 Application 和相应的 Application.Resources)来存储这些样式以供全局访问。 因此: 如何创建一个顶级的样式定义集,使所有 xaml 文件都能看到(考虑到我没有 app.xaml,请参见上文)? 或者是否可以向类库中添加一个可用的 app.xaml?
FYI, 我尝试在 ResourceDictionary.xaml 文件中创建一个 ResourceDictionary,并将其包含在每个窗口中的 "Window.Resources" 块中。这解决了按钮等控件的样式问题...但对于包含的窗口却不行。我可以在窗口的开头块中放置 Style="{StaticResource MyWindowStyle}",它可以编译并在 VS 设计窗口中正常显示,但在实际运行时,我会得到一个解析异常(MyWindowStyle 找不到; 我猜测 Visual Studio 在该行后面看到了包含的字典,但CRL按顺序执行,因此还没有加载 ResourceDictionary)。
感谢您的想法,但仍然不行... 显然,类库不支持隐式使用generic.xaml。 我将generic.xaml添加到我的类库项目中,并将其构建操作设置为“资源”。 它包含:
<ResourceDictionary 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Style TargetType="{x:Type Window}" x:Key="MyWindow">
        <Setter Property="Background" Value="Black"/>
    </Style>
</ResourceDictionary>

我想要使用主题的窗口 XAML 如下所示:
<Window x:Class="MyAssembly.ConfigureGenericButtons"
    x:ClassModifier="internal"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Style="{StaticResource MyWindow}"
    Title="ConfigureGenericButtons">
...Buttons, etc...
</Window>

虽然VS设计窗口不显示使用MyWindow样式的窗口(即黑色背景),但编译正常并启动。但是,当包含此类库的应用程序调用导致此窗口显示的调用时,我会收到XamlParseException:

找不到名为"{MyWindow}"的资源。

我还尝试省略Style参数,以查看窗口是否默认使用该样式(我尝试了在通用.xaml中包含和不包含x:Key)。没有错误,但是在generic.xaml中定义的任何内容也没有显示出来。
我在这里做错了吗,或者有哪些其他想法可以允许在Window上使用常见的自定义样式(即不必在每个Window的xaml中定义样式)--但是需要注意,这不是一个应用程序?
7个回答

15

听起来需要使用主题化技术。

  1. 在您的项目中添加一个 /themes/generic.xaml 资源字典。
  2. 在 AssemblyInfo.cs 中添加以下内容:[assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)]
  3. ?
  4. 利润!

您添加到 generic 中的任何资源将被所有控件使用。此外,您可以通过在 themes 目录中包含具有正确主题名称的 ResourceDictionary 文件来创建特定于配置文件的主题(如 Luna、Aero 等)。

这里是更多信息的链接:创建和应用自定义主题


我想https://dev59.com/1nRC5IYBdhLWcg3wG9Xp#405279是楼主对你的回答的回应。 - Pat
它是如何知道要查找/themes/generic.xaml的?这是一个标准吗? - DLeh
1
@DLeh 是的,/themes/generic.xml 是一个标准。请参见 http://msdn.microsoft.com/en-us/library/system.windows.themeinfoattribute - Cameron MacFarland
1
只是一个更正(它抓住了我)/themes/generic.xaml(注意有个a)。 - user230910

15
尝试添加
Style={DynamicResource MyStyle}

在这种情况下,您无法使用静态资源。


3
如果您没有app.xaml文件,仍然可以将其加载到应用程序级别的资源中,但您需要编写代码(而不是xaml)来完成此操作,类似于以下内容...
void LoadIt()
{
     ResourceDictionary MyResourceDictionary = new ResourceDictionary();
     MyResourceDictionary.Source = new Uri("MyResources.xaml", UriKind.Relative);
     App.Current.Resources.MergedDictionaries.Add(  MyResourceDictionary )
}

请查看以下网站的示例: http://ascendedguard.com/2007/08/one-of-nice-features-about-wpf-is-how.html


1

Dr. WPF(或者之前被称为 Dr. WPF 的人)在该主题上有一篇很棒的帖子

这是来自该帖子的摘录,其中他们创建了 Application 对象并添加资源:

if (Application.Current == null)
{
    // create the Application object
    new Application();

    // merge in your application resources
    Application.Current.Resources.MergedDictionaries.Add(
        Application.LoadComponent(
            new Uri("MyLibrary;component/Resources/MyResourceDictionary.xaml",
            UriKind.Relative)) as ResourceDictionary);
}

由于我的程序集是通过互操作性托管的,所以我不得不添加以下设置ShutdownMode,并在完成后关闭:

new Application() { ShutdownMode = ShutdownMode.OnExplicitShutdown };

它运行得很顺利。

1
这里有一个简单的解决方案,可以在 .NET 类库中“模块范围”共享资源。重要的是,它似乎能够稳健地保留 XAML 设计器在 Visual Studio 中的显示功能和行为。
首先,添加一个派生自 ResourceDictionary 的新的 C# 类,如下所示。该类的实例将取代每个需要查看共享资源的 System.Windows.Control(或其他 ResourceDictionary-bearing 组件)上的默认 ResourceDictionary:

ComponentResources.cs:

using System;
using System.Reflection;
using System.Windows;
using System.Windows.Markup;

namespace MyNamespace
{
    [UsableDuringInitialization(true), Ambient, DefaultMember("Item")]
    public class ComponentResources : ResourceDictionary
    {
        static ResourceDictionary _inst;

        public ComponentResources()
        {
            if (_inst == null)
            {
                var uri = new Uri("/my-class-lib;component/resources.xaml", UriKind.Relative);
                _inst = (ResourceDictionary)Application.LoadComponent(uri);
            }
            base.MergedDictionaries.Add(_inst);    //  <--  !
        }
    };
}

请务必将上一个代码片段中的MyNamespacemy-class-lib分别替换为自己项目中的命名空间和程序集文件名(不包括“.dll”扩展名):
在您的类库项目中添加一个新的ResourceDictionary XAML文件。与“Application”程序集不同,Visual Studio UI 中没有此选项,因此您需要手动完成。这将包含您想要在整个类库中共享的资源:

$(ProjectDirectory)\Resources.xaml:

<ResourceDictionary xmlns="http://schemas.microsoft.com/netfx/2009/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:local="clr-namespace:MyNamespace">

    <!-- define the resources to be shared across the whole class library here -->

    <!-- example for demonstration -->
    <Style TargetType="Rectangle">
        <Setter Property="Fill" Value="Red" />
    </Style>

<ResourceDictionary>

构建操作设置为"页面",并确保文件属性信息如下:

enter image description here

最后,进入项目中需要引用共享资源的XAML文件,并将默认的ResouceDictionary(通常位于XAML根元素的Resources属性上)替换为ComponentResources实例。这将保存每个组件的私有资源,正如您在顶部的C#代码(ComponentResources构造函数)中所看到的那样,每个这些不同的实例都会将相同的模块级共享单例ResourceDictionary作为“合并字典”附加。对于每个应该看到共享资源的控件都要进行此操作,即使它们没有自己的私有资源。当然,您也可以跳过此步骤,根据需要排除某些控件的共享。例如:

<UserControl x:Class="MyNamespace.UserControl1"
             xmlns="http://schemas.microsoft.com/netfx/2009/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:MyNamespace">

    <UserControl.Resources>
        <local:ComponentResources>
            <!-- Keep any existing non-shared resources here from before -->
            <!-- Can be empty if this control has no private resources -->
        </local:ComponentResources>
    </UserControl.Resources>

    <!-- to demonstrate that the Style in the above example is effective... -->
    <Grid>
        <Rectangle Width="10" Height="10" />
    </Grid>

</UserControl>

正如广告所述,它非常好用,包括在XAML Designer中...

enter image description here


1

经过花费大量时间,我终于弄清楚了。以下是方法:

  1. In WPF controls library, add a new folder named themes.
  2. Inside the themes folder, add a resource dictionary named generic.xaml.
  3. Inside generic.xaml, add your resource using the following syntax:

    <SolidColorBrush x:Key="{ComponentResourceKey {x:Type local:UserControl1}, MyEllipseBrush}" Color="Blue" />
    
  4. In your control, use the following syntax to access this resource:

    Background="{StaticResource {ComponentResourceKey {x:Type local:UserControl1}, MyEllipseBrush}}"
    

需要注意的事项:

  1. 当您创建一个新的WPF控件库时,通常Visual Studio会自动完成步骤1和2。
  2. 我不完全理解ComponentResourceKey的第一个参数的目的,但它是必需的。请使用将使用此资源的控件的名称。
  3. 在我的情况下,Visual Studio的设计师无法找到资源。这可能是缓存问题,我不确定。但在运行时,它可以很好地工作。
  4. 您可以在此MSDN文章中阅读更多有关此语法的详细信息。

希望这能使一些人的生活更轻松。


0
如果您将它加载到 Window.Resource 中,那么该字典只对该窗口的子级可用。您需要让它对窗口及其子级都可用。
尝试将其加载到 app.xaml 文件中。这将使它成为应用程序级别的资源,而不是窗口级别的资源。

抱歉,我想我最初没有表达清楚(我只是修改了描述以修复) - 我没有app.xaml,因为这是一个类库。 - WarpedBoard
3
抱歉,我想我最初没有表达清楚。这是在stackoverflow上,@WarpedBoard。你肯定不指望人们真正阅读问题吧。;) - mackenir

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接