从另一个程序集共享 WPF-Dictionary
Sharing WPF-Dictionary from another assembly
好的,我为此苦思冥想了几个小时,仍然找不到解决方案。
首先我将解释我创建的简单测试用例:
解决方案
- ClassLibrary1
- Dictionary1.xaml
- WpfApplication3
- App.config
- App.xaml
- Dictionary2.xaml
- MainWindows.xaml
类库1:
该项目具有允许我添加 wpf-dictionary 所需的参考资料:
PresentationCore、PresentationFramework、Systam.Xaml、windowsbase
(以及任何常规 class 库的所有标准程序集)
这是Dictionary1.xaml
:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Color x:Key="PrimaryBackgroundColor">#FF030010</Color>
<SolidColorBrush x:Key="PrimaryBackgroundBrush" Color="{StaticResource PrimaryBackgroundColor}" />
</ResourceDictionary>
WpfApplication3:
这个项目只是在 wpf 表单上显示一个按钮。
Dictionary2.xaml
:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication3">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/ClassLibrary1.dll;component/Dictionary1.xaml"/>
</ResourceDictionary.MergedDictionaries>
<Style TargetType="Button">
<Setter Property="Background" Value="{StaticResource PrimaryBackgroundBrush}" />
</Style>
</ResourceDictionary>
MainWindow.xaml
:
<Window x:Class="WpfApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication3"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Dictionary2.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<Button Content="aaa" Width="100" Height="40" />
</Grid>
</Window>
就是这样 - 如您所见,非常简单。
这里唯一的问题是 dictionary2 需要使用 dictionary1 中的资源。
因此有两种方法可以引用另一个程序集:
选项 1:
class-library 是您的解决方案中的一个项目,您的 WpfApplication 添加了对同一解决方案中的 class 库项目的引用。这是通过 Add-Reference/Projects 完成的,在那种情况下一切都很好。
选项 2:
class-library 不是您的解决方案。 (实际上它可以像我的例子一样)
但是,您可以通过添加对驻留在您的 ClassLibrary1.Dll
中的引用来添加引用
bin\debug
或 bin\release
个文件夹。
在那种情况下,通往地狱的门户就打开了。
Dictionary2 抱怨它找不到资源 'PrimaryBackgroungBrush' 并在执行时崩溃
抱怨找不到 dictionary1.xaml
抛出异常:PresentationFramework.dll 中的 'System.Windows.Markup.XamlParseException'
和内部异常:
{“无法加载文件或程序集 'ClassLibrary1.dll, Culture=neutral' 或其依赖项之一。
系统找不到指定的文件。":"ClassLibrary1.dll, Culture=neutral"}
问题是使用 option2 是必不可少的,因为我想在其他 wpf 项目之间共享相同的字典而不用
将 ClassLibrary1 项目作为其解决方案的一部分。
建议复现方式:
- 在 Visual studio 中为 WPF 应用程序创建一个新解决方案。
- 将 class 库项目添加到解决方案。
- 在 class 库项目中,添加对以下程序集的引用:PresentationCore、PresentationFramework、Systam.Xaml、windowsbase
- 将 Wpf-Dictionary 'Dictionary1' 添加到您的 class 库项目并复制代码。 (您可以从 wpf 项目中复制一个,因为它不会作为选项存在于 class 库的添加项中)
- 将 Wpf-Dictionary 'Dictionary2' 添加到您的 wpf 应用程序并复制代码。
- 复制 MainWindow 的代码。
现在:
添加对 class 库的引用(作为项目,从添加引用对话框中的项目选项卡)
构建一切 - 一切都应该工作。
删除对 class 库的引用。
添加对 class 库的引用(作为 dll,从浏览选项卡并在 classlibrary/bin/debug 或发布文件夹中找到它)
构建一切 - 你会注意到我的问题。
这个问题有什么解决办法吗?
更新 1
我将 dictionary2.xaml 中的行更改为:
<ResourceDictionary Source="pack://application:,,,/ClassLibrary1.dll;component/Dictionary1.xaml"/>
收件人:
<ResourceDictionary Source="pack://application:,,,/ClassLibrary1;component/Dictionary1.xaml"/>
现在项目编译和执行没有错误,但是在设计时 - dictionary2
的 xaml 视图表明它找不到资源:'PrimaryBackgroundBrush` 并把丑陋的它下面的卷曲下划线。
所以这是一个进步——但我仍然对此不满意。
有什么解决办法吗?
更新 2
如前所述 - 现在一切都可以编译和执行。
但是你在下面的图片中看到的让我很烦,
我只是想确保其他将 class 库添加为 .Dll 文件而不是项目的人 100% 确定他们不会遇到图片中可以看到的问题,这意味着他们的 xaml智能感知可以在设计时识别资源。
我可以想象有关该 dll 的文档会是什么样子:
reference dll in the project
add this to resource dictionary in the project:
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/ClassLibrary1.dll;component/Dictionary1.xaml"/>
</ResourceDictionary.MergedDictionaries>
add this to each window/usercontrol:
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Dictionary2.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
看起来很糟糕。
如何在您的库中创建管理器,它必须被每个 window/usercontrol 引用并且它会自动执行操作?
这是我在评论中提到的主题管理器的剪辑(它会自动合并),考虑使用方便。
xaml(在design/run时间必须支持主题切换的每个window/usercontrol加上这个):
local:Theme.Theme=""
cs(这部分必须是库的一部分):
public static class Theme
{
public static readonly DependencyProperty ThemeProperty =
DependencyProperty.RegisterAttached("Theme", typeof(string), typeof(Theme), new PropertyMetadata(null, (d, e) =>
{
var theme = (string)e.NewValue;
// in run-time set theme to specified during init
if (!DesignerProperties.GetIsInDesignMode(d))
theme = _theme;
var element = d as FrameworkElement;
element.Resources.MergedDictionaries.Clear();
if (!string.IsNullOrEmpty(theme))
{
var uri = new Uri($"/MyPorject;component/Themes/{theme}.xaml", UriKind.Relative);
element.Resources.MergedDictionaries.Add(new ResourceDictionary() { Source = uri });
}
}));
public static string GetTheme(DependencyObject obj) => (string)obj.GetValue(ThemeProperty);
public static void SetTheme(DependencyObject obj, string value) => obj.SetValue(ThemeProperty, value);
static string _theme = "Generic";
static string[] _themes = new[]
{
"Test",
};
/// <summary>
/// Init themes
/// </summary>
/// <param name="theme">Theme to use</param>
public static void Init(string theme)
{
if (_themes.Contains(theme))
_theme = theme;
}
}
P.S.: 功能是原始的(对我来说已经足够了),但应该给你一个想法。
好的,我为此苦思冥想了几个小时,仍然找不到解决方案。
首先我将解释我创建的简单测试用例: 解决方案
- ClassLibrary1
- Dictionary1.xaml
- WpfApplication3
- App.config
- App.xaml
- Dictionary2.xaml
- MainWindows.xaml
类库1:
该项目具有允许我添加 wpf-dictionary 所需的参考资料:
PresentationCore、PresentationFramework、Systam.Xaml、windowsbase
(以及任何常规 class 库的所有标准程序集)
这是Dictionary1.xaml
:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Color x:Key="PrimaryBackgroundColor">#FF030010</Color>
<SolidColorBrush x:Key="PrimaryBackgroundBrush" Color="{StaticResource PrimaryBackgroundColor}" />
</ResourceDictionary>
WpfApplication3:
这个项目只是在 wpf 表单上显示一个按钮。
Dictionary2.xaml
:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication3">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/ClassLibrary1.dll;component/Dictionary1.xaml"/>
</ResourceDictionary.MergedDictionaries>
<Style TargetType="Button">
<Setter Property="Background" Value="{StaticResource PrimaryBackgroundBrush}" />
</Style>
</ResourceDictionary>
MainWindow.xaml
:
<Window x:Class="WpfApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication3"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Dictionary2.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<Button Content="aaa" Width="100" Height="40" />
</Grid>
</Window>
就是这样 - 如您所见,非常简单。
这里唯一的问题是 dictionary2 需要使用 dictionary1 中的资源。
因此有两种方法可以引用另一个程序集:
选项 1: class-library 是您的解决方案中的一个项目,您的 WpfApplication 添加了对同一解决方案中的 class 库项目的引用。这是通过 Add-Reference/Projects 完成的,在那种情况下一切都很好。
选项 2:
class-library 不是您的解决方案。 (实际上它可以像我的例子一样)
但是,您可以通过添加对驻留在您的 ClassLibrary1.Dll
中的引用来添加引用
bin\debug
或 bin\release
个文件夹。
在那种情况下,通往地狱的门户就打开了。
Dictionary2 抱怨它找不到资源 'PrimaryBackgroungBrush' 并在执行时崩溃
抱怨找不到 dictionary1.xaml
抛出异常:PresentationFramework.dll 中的 'System.Windows.Markup.XamlParseException' 和内部异常: {“无法加载文件或程序集 'ClassLibrary1.dll, Culture=neutral' 或其依赖项之一。 系统找不到指定的文件。":"ClassLibrary1.dll, Culture=neutral"}
问题是使用 option2 是必不可少的,因为我想在其他 wpf 项目之间共享相同的字典而不用 将 ClassLibrary1 项目作为其解决方案的一部分。
建议复现方式:
- 在 Visual studio 中为 WPF 应用程序创建一个新解决方案。
- 将 class 库项目添加到解决方案。
- 在 class 库项目中,添加对以下程序集的引用:PresentationCore、PresentationFramework、Systam.Xaml、windowsbase
- 将 Wpf-Dictionary 'Dictionary1' 添加到您的 class 库项目并复制代码。 (您可以从 wpf 项目中复制一个,因为它不会作为选项存在于 class 库的添加项中)
- 将 Wpf-Dictionary 'Dictionary2' 添加到您的 wpf 应用程序并复制代码。
- 复制 MainWindow 的代码。
现在:
添加对 class 库的引用(作为项目,从添加引用对话框中的项目选项卡) 构建一切 - 一切都应该工作。
删除对 class 库的引用。
添加对 class 库的引用(作为 dll,从浏览选项卡并在 classlibrary/bin/debug 或发布文件夹中找到它) 构建一切 - 你会注意到我的问题。
这个问题有什么解决办法吗?
更新 1
我将 dictionary2.xaml 中的行更改为:
<ResourceDictionary Source="pack://application:,,,/ClassLibrary1.dll;component/Dictionary1.xaml"/>
收件人:
<ResourceDictionary Source="pack://application:,,,/ClassLibrary1;component/Dictionary1.xaml"/>
现在项目编译和执行没有错误,但是在设计时 - dictionary2
的 xaml 视图表明它找不到资源:'PrimaryBackgroundBrush` 并把丑陋的它下面的卷曲下划线。
所以这是一个进步——但我仍然对此不满意。
有什么解决办法吗?
更新 2
如前所述 - 现在一切都可以编译和执行。
但是你在下面的图片中看到的让我很烦,
我只是想确保其他将 class 库添加为 .Dll 文件而不是项目的人 100% 确定他们不会遇到图片中可以看到的问题,这意味着他们的 xaml智能感知可以在设计时识别资源。
我可以想象有关该 dll 的文档会是什么样子:
reference dll in the project
add this to resource dictionary in the project:
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/ClassLibrary1.dll;component/Dictionary1.xaml"/>
</ResourceDictionary.MergedDictionaries>
add this to each window/usercontrol:
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Dictionary2.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
看起来很糟糕。
如何在您的库中创建管理器,它必须被每个 window/usercontrol 引用并且它会自动执行操作?
这是我在评论中提到的主题管理器的剪辑(它会自动合并),考虑使用方便。
xaml(在design/run时间必须支持主题切换的每个window/usercontrol加上这个):
local:Theme.Theme=""
cs(这部分必须是库的一部分):
public static class Theme
{
public static readonly DependencyProperty ThemeProperty =
DependencyProperty.RegisterAttached("Theme", typeof(string), typeof(Theme), new PropertyMetadata(null, (d, e) =>
{
var theme = (string)e.NewValue;
// in run-time set theme to specified during init
if (!DesignerProperties.GetIsInDesignMode(d))
theme = _theme;
var element = d as FrameworkElement;
element.Resources.MergedDictionaries.Clear();
if (!string.IsNullOrEmpty(theme))
{
var uri = new Uri($"/MyPorject;component/Themes/{theme}.xaml", UriKind.Relative);
element.Resources.MergedDictionaries.Add(new ResourceDictionary() { Source = uri });
}
}));
public static string GetTheme(DependencyObject obj) => (string)obj.GetValue(ThemeProperty);
public static void SetTheme(DependencyObject obj, string value) => obj.SetValue(ThemeProperty, value);
static string _theme = "Generic";
static string[] _themes = new[]
{
"Test",
};
/// <summary>
/// Init themes
/// </summary>
/// <param name="theme">Theme to use</param>
public static void Init(string theme)
{
if (_themes.Contains(theme))
_theme = theme;
}
}
P.S.: 功能是原始的(对我来说已经足够了),但应该给你一个想法。