WPF MahApps.Metro嵌入式Dll资源无法正常工作

时间:2016-08-16 19:33:36

标签: wpf xaml dll mahapps.metro

我的问题是MahApps.Metro和MahApps.Metro.IconPacks的嵌入式dll无法正常工作。

使用Visual Studio调试器检查它们是否已加载并且工作正常。如果它无法加载它们,程序将抛出一个xaml异常。

但由于某种原因,合并的ResourceDictionaries在运行时不起作用。

Should look like this

But looks like this

(正如您所看到的那样,文字颜色不同,而且缺少右侧的图标=>不会加载样式。)

如果在程序目录中提供了两个Dll,它看起来像第一张图片。

我的App.xaml

<Application x:Class="Launcher.App"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:converter="clr-namespace:Launcher.Class.Converter"
         >

<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
            <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
            <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Colors.xaml" />
            <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/Blue.xaml" />
            <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/BaseLight.xaml" />
            <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/FlatSlider.xaml" />
            <ResourceDictionary Source="pack://application:,,,/MahApps.Metro.IconPacks;component/Themes/IconPacks.xaml" />
            <ResourceDictionary Source="Controls\ColorBrushes.xaml"/>
            <ResourceDictionary Source="Controls\CustomMetroWindow.xaml"/>
            <ResourceDictionary Source="Controls\CustomListView.xaml"/>
            <ResourceDictionary Source="Controls\NewsStyle.xaml"/>
            <ResourceDictionary Source="Controls\TextImageBox.xaml"/>
            <ResourceDictionary Source="Controls\GlowMetroButton.xaml"/>
            <ResourceDictionary Source="Controls\ToggleSwitchWin10.xaml"/>
            <ResourceDictionary Source="Simple Styles.xaml"/>
        </ResourceDictionary.MergedDictionaries>
        <converter:InverseBooleanConverter x:Key="InverseBooleanConverter" />
        <converter:BooleanToVisibilityConverter x:Key="BoolToVisibilityConverter" />
        <converter:BooleanToVisibilityCollapsedConverter x:Key="BoolToVisibilityCollapsedConverter" />
        <converter:MultiObjectToBooleanConverter x:Key="MultiObjectToBooleanConverter" />
        <converter:DownloadProgressToVisibilityConverter x:Key="DownloadProgressToVisibilityConverter" />
        <converter:MultiObjectToStatusBarColorConverter x:Key="MultiObjectToStatusBarColorConverter" />
        <converter:MultiBooleanConverter x:Key="MultiBooleanConverter" />
        <converter:MultiBooleanToVisibilityConverter x:Key="MultiBooleanToVisibilityConverter" />
        <converter:OpacityToBooleanConverter x:Key="OpacityToBooleanConverter" />
    </ResourceDictionary>
</Application.Resources>

我的Program.cs

[STAThread]
    public static void Main()
    {
        AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
        {
            var resourceName = Assembly.GetExecutingAssembly().GetName().Name + ".Dll." + new AssemblyName(args.Name).Name + ".dll";
            if (!resourceName.Contains("resources"))
            {
                using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
                {
                    if (stream != null)
                    {
                        var assemblyData = new Byte[stream.Length];
                        stream.Read(assemblyData, 0, assemblyData.Length);
                        return Assembly.Load(assemblyData);
                    }
                }
            }
            else
            {
                Assembly requestedAssembly = args.RequestingAssembly;
                AssemblyName requestedAssemblyName = new AssemblyName(args.Name);
                while (true)
                {
                    // requesting name in format: %assemblyname%.resources
                    // rewrite to: %assemblyName%.%assemblyName%.%culture%.resources.dll
                    //
                    var baseName = requestedAssemblyName.Name.Substring(0, requestedAssemblyName.Name.Length - ".resources".Length);
                    var name = string.Format("{0}.Dll.Lang.{1}.{2}.resources.dll", baseName, requestedAssemblyName.CultureInfo.Name, Assembly.GetExecutingAssembly().GetName().Name);

                    // by default for resources the requestingAssembly will be null
                    Assembly asm = null;
                    var assemblies = AppDomain.CurrentDomain.GetAssemblies();

                    // resources have the same name as their belonging assembly, so find by name
                    var parentName = requestedAssemblyName.Name.Substring(0, requestedAssemblyName.Name.Length - ".resources".Length);
                    // I'd love to use linq here, but Cecil starts fucking up when I do (null reference exception on assembly.Write)
                    // without a Linq query it works fine, though

                    // ReSharper disable once LoopCanBeConvertedToQuery
                    foreach (var assembly in assemblies)
                    {
                        if (assembly.GetName().Name == parentName)
                        {
                            asm = assembly;
                        }
                    }
                    if (asm == null)
                    {
                        // cannot find assembly from which to load
                        return null;
                    }
                    using (var stream = asm.GetManifestResourceStream(name))
                    {
                        if (stream != null)
                        {
                            var bytes = new byte[stream.Length];
                            stream.Read(bytes, 0, bytes.Length);
                            return Assembly.Load(bytes);
                        }
                    }
                    // did not find the specific resource yet
                    // attempt to use the parent culture, this follows the .Net resource fallback system
                    // e.g. if sub resource de-DE is not found, then .Parent will be "de", if that is not found parent will probably be default resource
                    var fallback = requestedAssemblyName.CultureInfo.Parent.Name;
                    if (string.IsNullOrEmpty(fallback))
                    {
                        // is empty if no longer a parent
                        // return null so .Net can load the default resource
                        return null;
                    }
                    var alteredAssemblyName = requestedAssemblyName.FullName;
                    alteredAssemblyName = alteredAssemblyName.Replace(string.Format("Culture={0}", requestedAssemblyName.CultureInfo.Name), string.Format("Culture={0}", fallback));

                    requestedAssemblyName = new AssemblyName(alteredAssemblyName);
                }
            }
            return null;
        };

        App.Main();
    }

控件

<TextBox x:Name="Username"
            Controls:TextBoxHelper.Watermark="{lex:Loc Key=LoginWindow.YourUsername}" Margin="0,20,0,9"
            Text="{Binding Config.AuthUsername}"
            IsEnabled="{Binding LoggingIn, Converter={StaticResource InverseBooleanConverter}}" TextAlignment="Justify"
            >
            <TextBox.Resources>
                <Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource MetroTextImageBox}">
                    <Setter Property="Controls:TextBoxHelper.ButtonTemplate">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type ButtonBase}">
                                <Grid Background="{TemplateBinding Background}">
                                    <Rectangle>
                                        <Rectangle.Fill>
                                            <VisualBrush>
                                                <VisualBrush.Visual>
                                                    <iconPacks:PackIconModern Kind="User" Foreground="{StaticResource MainIconBrush}" />
                                                </VisualBrush.Visual>
                                            </VisualBrush>
                                        </Rectangle.Fill>
                                    </Rectangle>
                                </Grid>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </TextBox.Resources>
        </TextBox>

3 个答案:

答案 0 :(得分:1)

此代码适用于我:(将所有库添加为嵌入式资源并将启动对象设置为此类)

 public class Program
    {
        private static Assembly ExecutingAssembly = Assembly.GetExecutingAssembly();
        private static string[] EmbeddedLibraries =
            ExecutingAssembly.GetManifestResourceNames().Where(x => x.EndsWith(".dll")).ToArray();
        [STAThreadAttribute]

        public static void Main()
        {

            AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;

            App.Main();

        }
        private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            // Get assembly name
            var assemblyName = new AssemblyName(args.Name).Name + ".dll";

            // Get resource name
            var resourceName = EmbeddedLibraries.FirstOrDefault(x => x.EndsWith(assemblyName));
            if (resourceName == null)
            {
                return null;
            }

            // Load assembly from resource
            using (var stream = ExecutingAssembly.GetManifestResourceStream(resourceName))
            {
                var bytes = new byte[stream.Length];
                stream.Read(bytes, 0, bytes.Length);
                return Assembly.Load(bytes);
            }
        }
    }

答案 1 :(得分:0)

项目文件部分已从前一个答案中删除。 将其添加到项目文件

      <!--Then add this is project file.-->
<Target Name="EmbedReferencedAssemblies" AfterTargets="ResolveAssemblyReferences">
    <ItemGroup>
      <!-- get list of assemblies marked as CopyToLocal -->
      <AssembliesToEmbed Include="@(ReferenceCopyLocalPaths)" Condition="'%(Extension)' == '.dll'" />
      <!-- add these assemblies to the list of embedded resources -->
      <EmbeddedResource Include="@(AssembliesToEmbed)">
        <LogicalName>%(AssembliesToEmbed.DestinationSubDirectory)%(AssembliesToEmbed.Filename)%(AssembliesToEmbed.Extension)</LogicalName>
      </EmbeddedResource>
    </ItemGroup>
    <Message Importance="high" Text="Embedding: @(AssembliesToEmbed->'%(DestinationSubDirectory)%(Filename)%(Extension)', ', ')" />
  </Target>
  <!-- no need to copy the assemblies locally anymore -->
  <Target Name="_CopyFilesMarkedCopyLocal" />

答案 2 :(得分:-1)

我遇到了您描述的相同问题并遇到了解决此问题的修复程序。

public partial class App : Application {
    public App() {
        AppDomain.CurrentDomain.AssemblyResolve += OnResolveAssembly;
    }

    private static Assembly OnResolveAssembly(object sender, ResolveEventArgs args) {
        var executingAssembly = Assembly.GetExecutingAssembly();
        var assemblyName = new AssemblyName(args.Name);

        var path = assemblyName.Name + ".dll";
        if (assemblyName.CultureInfo.Equals(CultureInfo.InvariantCulture) == false) {
            path = String.Format(@"{0}\{1}", assemblyName.CultureInfo, path);
        }

        using (var stream = executingAssembly.GetManifestResourceStream(path)) {
            if (stream == null)
                return null;

            var assemblyRawBytes = new byte[stream.Length];
            stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length);
            return Assembly.Load(assemblyRawBytes);
        }
    }
}
  

                                                     %(AssembliesToEmbed.DestinationSubDirectory)%(AssembliesToEmbed.Filename)%(AssembliesToEmbed.Extension)                       '%(DestinationSubDirectory)%(文件名)%(扩展部分)',   ',')“/&gt;