从另一个程序集共享 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\debugbin\release 个文件夹。 在那种情况下,通往地狱的门户就打开了。 Dictionary2 抱怨它找不到资源 'PrimaryBackgroungBrush' 并在执行时崩溃 抱怨找不到 dictionary1.xaml

抛出异常:PresentationFramework.dll 中的 'System.Windows.Markup.XamlParseException' 和内部异常: {“无法加载文件或程序集 'ClassLibrary1.dll, Culture=neutral' 或其依赖项之一。 系统找不到指定的文件。":"ClassLibrary1.dll, Culture=neutral"}

问题是使用 option2 是必不可少的,因为我想在其他 wpf 项目之间共享相同的字典而不用 将 ClassLibrary1 项目作为其解决方案的一部分。

建议复现方式:

  1. 在 Visual studio 中为 WPF 应用程序创建一个新解决方案。
  2. 将 class 库项目添加到解决方案。
  3. 在 class 库项目中,添加对以下程序集的引用:PresentationCore、PresentationFramework、Systam.Xaml、windowsbase
  4. 将 Wpf-Dictionary 'Dictionary1' 添加到您的 class 库项目并复制代码。 (您可以从 wpf 项目中复制一个,因为它不会作为选项存在于 class 库的添加项中)
  5. 将 Wpf-Dictionary 'Dictionary2' 添加到您的 wpf 应用程序并复制代码。
  6. 复制 MainWindow 的代码。

现在:

  1. 添加对 class 库的引用(作为项目,从添加引用对话框中的项目选项卡) 构建一切 - 一切都应该工作。

  2. 删除对 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.: 功能是原始的(对我来说已经足够了),但应该给你一个想法。