C# WPF 类库中的程序集范围/根级样式
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/404019/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Assembly-wide / root-level styles in WPF class library
提问by WarpedBoard
I have a C# (2008/.NET 3.5) class library assembly that supports WPF (based on this article).
I've created several windows, and am now attempting to create a common style set for them. However, as it's a class library (instead of a WPF app), I don't have an app.xaml (and its contained Application & corresponding Application.Resources) in which to store these styles for global access.
我有一个支持 WPF 的 C# (2008/.NET 3.5) 类库程序集(基于本文)。
我已经创建了几个窗口,现在正在尝试为它们创建一个通用的样式集。但是,由于它是一个类库(而不是 WPF 应用程序),我没有 app.xaml(及其包含的应用程序和相应的 Application.Resources)来存储这些样式以供全局访问。
So:How can I create a top-level set of style definitions that'll be seen by all xaml files in the assembly, given that I do not have app.xaml (see above)? And/or is it possible to adda working app.xaml to a class library?
那么:鉴于我没有 app.xaml(见上文),我如何创建一组顶级样式定义,这些样式定义将由程序集中的所有 xaml 文件看到?和/或是否可以将工作 app.xaml添加到类库?
FYI, I did try creating a ResourceDictionary in a ResourceDictionary.xaml file, and include it in each window within a "Window.Resources" block. That turned out to solve the styling of Buttons, etc... but not for the enclosing Window. I can put Style="{StaticResource MyWindowStyle}"
in the Window's opening block, and it compiles and shows up in the VS Design window fine, but during actual runtime I get a parse exception (MyWindowStyle could not be found; I'm guessing Visual Studio sees the dictionary included after the line in question, but the CRL does things more sequentially and therefore hasn't loaded the ResourceDictionary yet).
仅供参考,我确实尝试在 ResourceDictionary.xaml 文件中创建一个 ResourceDictionary,并将其包含在“Window.Resources”块内的每个窗口中。结果证明解决了按钮的样式等问题......但不适用于封闭的窗口。我可以放入Style="{StaticResource MyWindowStyle}"
Window 的开始块,它可以编译并很好地显示在 VS Design 窗口中,但是在实际运行时我得到一个解析异常(找不到 MyWindowStyle;我猜 Visual Studio 看到了包含在有问题的行,但 CRL 更按顺序执行操作,因此尚未加载 ResourceDictionary)。
Thanks for the ideas, but still no go... apparently a class library does NOT support the generic.xaml usage implicitly. I added generic.xaml to my class library project and set its Build Action to "Resource". It contains:
感谢您的想法,但仍然不行...显然类库不支持 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>
The window xaml that I want to have use the theme looks like this:
我想要使用主题的窗口 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>
Although the VS Design window doesn't show the window using the MyWindow style (ie black background), it compiles fine and starts up. However, when the app containing this class library makes a call that causes this window to display, I get a XamlParseException:
尽管 VS Design 窗口没有显示使用 MyWindow 样式(即黑色背景)的窗口,但它可以正常编译并启动。但是,当包含此类库的应用程序进行调用导致此窗口显示时,我收到 XamlParseException:
Cannot find resource named '{MyWindow}'.
找不到名为“{MyWindow}”的资源。
I also tried leaving out the Style parameter, to see if the window would use the style by default (and I tried that both with the x:Key in generic.xaml included and without). No errors, but anything defined in the generic.xaml didn't show up either.
我还尝试省略 Style 参数,以查看窗口是否会默认使用该样式(并且我尝试在包含和不包含 generic.xaml 中的 x:Key 时使用该样式)。没有错误,但在 generic.xaml 中定义的任何内容也没有出现。
Am I doing something wrong here, or any other ideas on how one might allow for common custom styles to be used on a Window (ie, not have to define the styles in each Window's xaml) -- with the caveat that this is NOT an application?
我在这里做错了什么,或者关于如何允许在 Window 上使用常见的自定义样式的任何其他想法(即,不必在每个 Window 的 xaml 中定义样式)——需要注意的是,这不是一个应用?
采纳答案by Jab
Try adding
尝试添加
Style={DynamicResource MyStyle}
You cannot use a StaticResource in this case.
在这种情况下,您不能使用 StaticResource。
回答by Muad'Dib
if you load it in Window.Resource, the dictionary is only available for the children of that window. You need to have it available for the window and its children.
如果在 Window.Resource 中加载它,则字典仅可用于该窗口的子窗口。您需要让它可用于窗口及其子项。
Try loading it in your app.xaml file. That should make it an application level resource, not a window-level resource.
尝试将其加载到您的 app.xaml 文件中。这应该使它成为应用程序级资源,而不是窗口级资源。
回答by Cameron MacFarland
This sounds like a job for theming.
这听起来像是一个主题工作。
- Add a
/themes/generic.xaml
ResourceDictionary to your project. - Add the following to AssemblyInfo.cs:
[assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)]
- ?
- Profit!
- 将
/themes/generic.xaml
ResourceDictionary添加到您的项目中。 - 将以下内容添加到 AssemblyInfo.cs:
[assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)]
- ?
- 利润!
Any resources you add to generic will be used by all controls. Also you can make profile specific themes (Luna, Aero etc.) by including a ResourceDictionary file with the correct theme name in the themes
directory.
您添加到通用的任何资源都将被所有控件使用。您还可以通过在themes
目录中包含具有正确主题名称的 ResourceDictionary 文件来制作配置文件特定主题(Luna、Aero 等)。
Heres a link to more info: Create and apply custom themes
这是更多信息的链接:创建和应用自定义主题
回答by Muad'Dib
If you dont have an app.xaml, you can still load it into the appplication-level resources, but you have to write code (not xaml) to do it, similar to this...
如果你没有app.xaml,你仍然可以将它加载到应用程序级别的资源中,但是你必须编写代码(不是xaml)来做,类似于这个...
void LoadIt()
{
ResourceDictionary MyResourceDictionary = new ResourceDictionary();
MyResourceDictionary.Source = new Uri("MyResources.xaml", UriKind.Relative);
App.Current.Resources.MergedDictionaries.Add( MyResourceDictionary )
}
check out this site for an example:
http://ascendedguard.com/2007/08/one-of-nice-features-about-wpf-is-how.html
看看这个网站的例子:http:
//ascendedguard.com/2007/08/one-of-nice-features-about-wpf-is-how.html
回答by KornMuffin
Dr. WPF (or the person formerly known as Dr. WPF) has a great poston the subject.
WPF 博士(或以前称为 WPF 博士的人)在该主题上发表了一篇很棒的文章。
Here's an excerpt from the post where they create the Application object and add resources:
这是他们创建 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);
}
Since my assembly is hosted via interop I had to add setting the ShutdownMode as follows and shutdown when finished:
由于我的程序集是通过互操作托管的,因此我必须添加如下设置 ShutdownMode 并在完成后关闭:
new Application() { ShutdownMode = ShutdownMode.OnExplicitShutdown };
It worked like a charm.
它就像一个魅力。
回答by dotNET
So after spending a lot of time I finally figured this out. Here's how:
所以在花了很多时间之后,我终于明白了这一点。就是这样:
- In WPF controls library, add a new folder named
themes
. - Inside the
themes
folder, add a resource dictionary namedgeneric.xaml
. Inside
generic.xaml
, add your resource using the following syntax:<SolidColorBrush x:Key="{ComponentResourceKey {x:Type local:UserControl1}, MyEllipseBrush}" Color="Blue" />
In your control, use the following syntax to access this resource:
Background="{StaticResource {ComponentResourceKey {x:Type local:UserControl1}, MyEllipseBrush}}"
- 在 WPF 控件库中,添加一个名为
themes
. - 在
themes
文件夹内,添加一个名为 的资源字典generic.xaml
。 在里面
generic.xaml
,使用以下语法添加您的资源:<SolidColorBrush x:Key="{ComponentResourceKey {x:Type local:UserControl1}, MyEllipseBrush}" Color="Blue" />
在您的控制下,使用以下语法访问此资源:
Background="{StaticResource {ComponentResourceKey {x:Type local:UserControl1}, MyEllipseBrush}}"
Things to note:
注意事项:
- Step 1 and 2 are normally automatically done for you by Visual Studio when you create a new WPF Controls Library.
- I do not fully understand the purpose of
ComponentResourceKey
's first parameter, but it is required. Use the name of the control that will consume this resource. - Visual Studio's designer was not be able to locate the resource in my case. It could be a cache issue, I'm not sure. At runtime however it works neatly.
- You can read more details about this syntax in this MSDN article.
- 创建新的 WPF 控件库时,Visual Studio 通常会自动为您完成第 1 步和第 2 步。
- 我不完全理解 的
ComponentResourceKey
第一个参数的用途,但它是必需的。使用将消耗此资源的控件的名称。 - 在我的案例中,Visual Studio 的设计人员无法找到资源。这可能是缓存问题,我不确定。但是在运行时它工作得很好。
- 您可以在这篇 MSDN 文章中阅读有关此语法的更多详细信息。
Hope this makes some lives easier.
希望这能让一些生活更轻松。
回答by Glenn Slayden
Here is a simple solution for sharing resources"module-wide" across a .NET class library. Importantly, it seems to robustly preserve the XAML Designerdisplay capabilities and behaviors in Visual Studio.
这是一个在.NET 类库中“模块范围”共享资源的简单解决方案。重要的是,它似乎稳健地保留了Visual Studio 中的XAML 设计器显示功能和行为。
Start by adding a new C#class derived from ResourceDictionary
as follows. An instance of this class will replace the default ResourceDictionary
on each System.Windows.Control
(or other ResourceDictionary
-bearing component) in the class library that needs to see the shared resources:
首先添加一个新的C#类ResourceDictionary
,如下所示。此类的实例将替换需要查看共享资源的类库中ResourceDictionary
每个System.Windows.Control
(或其他ResourceDictionary
承载组件)的默认值:
ComponentResources.cs:
组件资源.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);
}
};
}
Be sure to replace MyNamespace
and my-class-lib
in the previous code snippet with (respectively) the namespace and assembly filename (without the '.dll' extension) from your own project:
务必更换MyNamespace
,并my-class-lib
与(分别)前面的代码片段从自己的项目命名空间和组件的文件名(不包括的.dll扩展名):
Add a new ResourceDictionary
XAMLfile to your class library project. Unlike as for 'Application' assemblies, there is no option for this in the Visual Studio UI, so you'll have to do it manually. This will contain the resources you want to share across the whole class library:
将新的ResourceDictionary
XAML文件添加到您的类库项目。与“应用程序”程序集不同,Visual Studio UI 中没有此选项,因此您必须手动执行此操作。这将包含您要在整个类库中共享的资源:
$(ProjectDirectory)\Resources.xaml:
$(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>
Set the Build Actionto "Page"and be sure the File Properties information looks like this:
将构建操作设置为“页面”并确保文件属性信息如下所示:
Finally, go to the XAMLfiles in the project that need to reference the shared resources, and replace the default ResouceDictionary
(typically, on the Resources
property of the XAML root element) with a ComponentResources
instance. This will hold each component's private resources, and as you can see in the code above, the constructor attaches the module-wide shared singleton ResourceDictionary
as a "merged dictionary." Do this for every control that should see the shared resources, even those that have no private resources of their own. Of course, you can also skip this step to exclude certain controls from sharing as needed. For example:
最后,转到项目中需要引用共享资源的XAML文件,并将默认值ResouceDictionary
(通常Resources
在 XAML 根元素的属性上)替换为一个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>
As advertised, it works great, including in the XAML Designer...
正如宣传的那样,它工作得很好,包括在XAML 设计器中......