c# wpf如何更好的使用Application程序集资源

  这一篇单独拿出来分析这个程序集资源,为的就是不想让大家把程序集资源和exe程序强关联,因为程序集资源实际上是二进制资源,后续编译过程中会被嵌入到程序集中,而为了更方便的使用资源,我们要好好梳理一下程序集资源相关的知识。(例如多语言资源,多工程、多项目使用的公共资源文件)。

1)在程序集中添加资源

我们通过向项目添加文件并尝试修改资源文件属性看有什么不同的结果。

在工程上右键=》添加=》新建文件夹=》改名为Images=》回车=》在Images文件夹上右键=》添加=》现有项=》右下角文件类别选择所有文件=》找到你想导入的图片=》点击添加,结果如下图.

这样就添加好了一个文件,我们在添加的图片上右键属性看到生成操作为Resource,我们添加代码如下:

<Window x:Class="ApplicationResources.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:ApplicationResources"
  mc:Ignorable="d"
  Title="MainWindow" Height="450" Width="800">
 <Grid>
  <Image Source="/Images/takenotes.png" Width="230" Height="130"/>
 </Grid>
</Window>

我们在工程上点击编译,生成成功后我们去看编译结果,发现文件夹下没有这个image,我们使用反编译ILSpy工具查看生成的这个exe,我们看到Png文件作为资源被放在了resources里,(这里有一个小插曲,不知道为什么,我的PNG图片在最开始反编译的时候没有。直到我添加了上面Image的代码反编译才出来。以后会分析这个问题。这里记录一下)

而修改图片的属性为Content并修改复制到输出目录为始终复制。重新编译工程。

我们看到了Debug文件夹下多了一个Images文件夹,里面包含了我们的图片。而反编译程序集集中就没有这个资源文件了,它从资源里更换到目录下了。这样的话,程序集因为没有包含了资源文件大小就发生了变化。

通过修改资源的属性,资源在编译过程中会放置在不同的地方,这是其中2种比较常用的设置属性。通过这种设置程序集资源的方式易于更新,只需要在桌面资源管理器种替换掉文件并重新编译程序就能完成资源文件的替换,非常方便。资源文件可以放在主工程文件下,也可以选择放在单独的DLL下,具体看你的设计需要啦。因为接下来我们会详细讲如何读取这些资源。即使跨了DLL。

2)在程序集中查找资源

  因为是在讲Application的资源所以我们先看Application下对资源的查找方法,在App.xaml的Application上按下F12:

我们看到了返回StreamResourceInfo类型的方法又3个。GetContentStream()、GetRemoteStream()、GetResourceStream()。这里只讲GetResourceStream()其他的可以自己在对应的方法上按F1查看文档。(记得把资源属性设置成Resouce)

我们使用这种在程序集管理所有资源的方式,我们可以自己实现很多种自己管理资源的想法。可以把代码添加到自己的工程里调试一下看看自己感兴趣的内容,代码如下:

using System;
using System.Collections;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Resources;
using System.Windows;
using System.Windows.Resources;

namespace ApplicationResources
{
 /// <summary>
 /// MainWindow.xaml 的交互逻辑
 /// </summary>
 public partial class MainWindow : Window
 {
  public MainWindow()
  {
   InitializeComponent();
   //方法1使用StreamResourceInfo接收GetResourceStream()读取到的内容。
   StreamResourceInfo sri = Application.GetResourceStream(new Uri("Images/takenotes.png", UriKind.Relative));
   string type = sri.ContentType;
   var stream = sri.Stream;
   //方法2,还记得我们反编译时候看到的资源吗?从Assembly取我们要的资源。
   Assembly assembly = Assembly.GetAssembly(this.GetType());
   string resourceName = assembly.GetName().Name + ".g";
   //单个资源
   ResourceManager rm = new ResourceManager(resourceName, assembly);
    using (ResourceSet set = rm.GetResourceSet(CultureInfo.CurrentCulture, true, true))
    {
     UnmanagedMemoryStream s = (UnmanagedMemoryStream)set.GetObject("Images/takenotes.png", true);
    }
   //遍历所有资源
   ResourceManager rmResources = new ResourceManager(resourceName, assembly);
   using (ResourceSet set = rmResources.GetResourceSet(CultureInfo.CurrentCulture, true, true))
   {
    foreach (DictionaryEntry item in set)
    {
     Debug.WriteLine($"ResourceSet DictionaryEntry as {item.Key.ToString()}");
    }
   }

  }
 }
}

WPF目前给我们封装了一些访问资源的方法,我们使用XAML和CS下传入相对和绝对路径来演示:

<Window x:Class="ApplicationResources.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:ApplicationResources"
  mc:Ignorable="d" Loaded="Window_Loaded"
  Title="MainWindow" Height="450" Width="800">
 <Grid>
  <StackPanel>
  <Image Source="Images/takenotes.png" Width="220" VerticalAlignment="Top" HorizontalAlignment="Left"/>
  <Image Source="d:\Image\takenotes.png" Width="220" VerticalAlignment="Top" HorizontalAlignment="Left"/>
   <Image x:Name="imgAbsolutePath" Width="220" VerticalAlignment="Top" HorizontalAlignment="Left"/>
   <Image x:Name="imgRelativePath" Width="220" VerticalAlignment="Top" HorizontalAlignment="Left"/>
  </StackPanel>
 </Grid>
</Window>
 private void Window_Loaded(object sender, RoutedEventArgs e)
  {
   imgAbsolutePath.Source = new BitmapImage(new Uri(@"d:\Image\takenotes.png", UriKind.Absolute));
   imgRelativePath.Source = new BitmapImage(new Uri("Images/takenotes.png",UriKind.Relative));
  }

在这个基础上WPF支持pack URI。pack URI语法可以寻址包含在编译过的程序中的资源,(从名字上理解package URI?)使用pack URI可以让我们更加规范的使用资源文件,也更加方便。

如下这2段代码是等效的因为没有跨程序集。

 <Image Source="Images/takenotes.png" Width="220" VerticalAlignment="Top" HorizontalAlignment="Left"/>
 <Image Source="pack://application:,,,/Images/takenotes.png" Width="220" VerticalAlignment="Top" HorizontalAlignment="Left"/>

我新建了一个程序集,放置在ImageLibrary程序集下同样目录的资源。

Xaml写法:

   <Image Source="Images/takenotes.png" Width="120" VerticalAlignment="Top" HorizontalAlignment="Left"/>
   <Image Source="pack://application:,,,/Images/takenotes.png" Width="120" VerticalAlignment="Top" HorizontalAlignment="Left"/>
   <Image Source="pack://application:,,,/ImageLibrary;component/Images/takenotes.png" Width="120" VerticalAlignment="Top" HorizontalAlignment="Left"/> 

CS写法:

private void Window_Loaded(object sender, RoutedEventArgs e)
  {
   //绝对路径
   imgAbsolutePath.Source = new BitmapImage(new Uri(@"d:\Image\takenotes.png", UriKind.Absolute));
   //相对路径
   imgRelativePath.Source = new BitmapImage(new Uri("Images/takenotes.png",UriKind.Relative));
   //当前程序集pack URI 语法
   imgRelativePath.Source = new BitmapImage(new Uri("pack://application:,,,/Images/takenotes.png"));
   //跨程序集使用资源 pack URI 语法
   imgCrossAssemblyPath.Source = new BitmapImage(new Uri("pack://application:,,,/ImageLibrary;component/Images/takenotes.png"));
  }

这样就可以把资源单独存放在一个DLL里。然后剩下的就看自己怎么使用拉。

我们主工程下一般会保持当前App.xaml的当前资源结构。我们把资源都放在 Application.Current.Resources.MergedDictionaries内。通过MergedDictionaries更好的动态替换同类型的资源(比如多语言的一种实现方式)。

var targetResource = Application.Current.Resources.MergedDictionaries.FirstOrDefault(x => x.Source != null && x.Source.OriginalString.Contains(uri));
 if (targetResource != null)
 {
   Application.Current.Resources.MergedDictionaries.Remove(targetResource);
 }
 //添加与系统语言对应的语言包
 Application.Current.Resources.MergedDictionaries.Add(new ResourceDictionary
 {
  Source = new Uri($"pack://application:,,,/ApplicationResources;component/Lang/{rcName}.xaml", UriKind.RelativeOrAbsolute)
 });

这样的话,就能辅助我们更好的使用资源拉,是不是发现写代码如果保持正确的解耦,写代码会就越来越简单?

以上就是c# wpf如何更好的使用Application程序集资源的详细内容,更多关于wpf使用Application程序集资源的资料请关注我们其它相关文章!

(0)

相关推荐

  • 详解WPF的InkCanvas选择模式

    InkCanvas是WPF中进行墨迹绘制的控件,本文介绍下InkCanvas控件是如何进行选择操作的.文中有误的地方希望大家进行批评指正. InkCanvas的选择效果 使用WPF可以轻松实现白板功能,只需要添加一个InkCanvas控件.修改InkCanvas的EditingMode属性可以控制InkCanvas的操作模式,如书写.选择.擦除等模式. 如下demo在窗口中添加一个InkCanvas,然后添加一个Button实现书写与选择模式的切换. // xaml <Grid> <In

  • c# wpf如何使用Blend工具绘制Control样式

    本文通过设计一个RadioButton,分享下使用Blend绘制Path的方法.待绘制的RadioButton样式如下文所示,如有更好的方法实现该样式,欢迎交流. 实现效果 将要实现的RadioButton样式如下图,可以看出按钮的笔尖和笔身的填充色,以及选中时右上方圆形的填充色一致,代表笔的颜色. 实现方式 笔身使用矩形,填充色绑定按钮背景色:笔头部分使用闭合的Path,其中笔尖的颜色同样绑定按钮背景色:右上方的圆形使用Ellipse,填充色同样绑定按钮背景色. 实现步骤 1.打开Blend,

  • C# WPF实现的语音播放自定义控件

    原理很简单,利用Path画一个图,然后用动画进行播放,播放时间由依赖属性输入赋值与控件内部维护的一个计时器进行控制. 控件基本是玩具,无法作为真实项目使用. 因为没有设置播放源,所以编写异步播放源或者实际播放时候要将事件引发,是否播放等属性,事件移到真实播放事件 非专业UI,即使知道怎么画图也是画的不如意,到底是眼睛会了,手不行啊. 主界面xaml <local:VoiceAnimeButton Height="40" Width="200" IconMarg

  • 详解WPF中的隧道路由和冒泡路由事件

    WPF中使用路由事件升级了传统应用开发中的事件,在WPF中使用路由事件能更好的处理事件相关的逻辑,我们从这篇开始整理事件的用法和什么是直接路由,什么是冒泡路由,以及什么是隧道路由. 事件最基本的用法 在基于事件驱动的开发中,把代码放在响应注册的事件的处理函数内,比如Click事件.MouseDown事件.MouseUp事件等等.每个控件响应自己的注册事件,有很多如果在事件上有相互关联和影响的事件,就要在一个业务逻辑里写比较多的代码.而路由事件主要的优势就是路由事件可以在元素树上进行传递,并且沿着

  • 通过App.xaml理解wpf中的Application类

    这个章节来了解Application类,我考虑了一晚上决定跳过控件类相关的学习,因为控件如果只是入门的话每个控件F12跳过去看一下属性.事件就能大致了解的差不多,而且控件比较多,每个都这样看一遍,感觉意义不大.同时控件的使用一般又同时包含了,资源.样式.触发器.模板.绑定.列表控件的话,可能还包含列表虚拟化和数据虚拟化.所以想了一下.打算先讲Application类. 教程的第一篇我们从hello world开始了解什么是程序.它是如何编译.生成和运行的.现在这一个篇从App.xaml讲解App

  • 详解WPF中的对象资源

    在WPF中,所有继承自FrameworkElement的元素都包含一个Resources属性,这个属性就是我们这篇要讲的资源. 这一篇讲解的资源是不是上一篇的程序集资源(那个是在编译过程中打包到程序集中),这个是资源是我们想在公共的地方写一个对象让其他元素重复使用. 先贴个例子: <Window x:Class="NETResource.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/pre

  • c# WPF中的TreeView使用详解

    在wpf中实现treeview的功能,可能看到很多分享的都是简单的绑定,仅此记录自己完成的功能. 前台 <TreeView x:Name="chapterTree" Grid.Column="0" SelectedItemChanged="chapterTree_SelectedItemChanged" PreviewMouseRightButtonDown="chapterTree_PreviewMouseRightButton

  • c# 基于GMap.NET实现电子围栏功能(WPF版)

    前言 GMap.NET是一个强大.免费.跨平台.开源的.NET控件.分为WPF和winform版.GMap.NET的基本知识不做过多介绍,本文主要介绍如何使用该控件实现电子围栏功能. 电子围栏主要有两个功能模块:界面展示围栏区域,判断人员出入围栏的逻辑.GMap.NET的WPF版本功能并不强大,实现一些复杂的功能就只能发掘WPF的潜力了.GMap.NET给我们提供了一个基本的平台,必须熟练掌握WPF才能开发出复杂gis产品. 围栏区域界面显示 1 认识 GMapMarker GMapContro

  • C# WPF如何反射加载Geometry几何图形数据图标

    相信大家在阅读WPF相关GitHub开源项目源码时都会看见一串串这种数据 这种Geometry数据就是几何图形数据 为什么要用Geometry数据做图标? 有一种做法是使用ttf字体文件代替,不过使用ttf字体文件会出现下面几个缺点: 1.团队协作不便于管理 2.需要依赖特定平台 3.无法灵活使用 而使用Geometry的话,我们可以将这些几何图形数据存入资源字典ResourceDictionary 通过反射进行灵活使用,团队开发可共同维护 怎么获取Geometry数据? 我们进入https:/

  • c# wpf使用GMap.NET类库,实现地图轨迹回放

    前言 实现轨迹回放,GMap.NET有对应的类GMapRoute.这个类函数很少,功能有限,只能实现简单的轨迹回放.要实现更复杂的轨迹回放,就需要自己动手了. 本文介绍一种方法,可以实现复杂的轨迹回放.有句话"功夫在诗外",GMap.NET给你提供了基本地图处理功能:但是不要让CMap.NET束缚了手脚.你需要有深刻理解地图实现原理,深入理解WPF动画的原理,才能到达随心所欲.最终的效果如下: GMap.NET 显示原理 地图就是由许多方格"瓦片"组合而来.当你移动

  • c# wpf如何附加依赖项属性

    附加依赖项属性是一个属性本来不属于对象自己,但是某些特定场景其他的对象要使用该对象在这种场景下的值.这个值只在这个场景下使用.基于这个需求设计出来的属性.这里主要涉及到一个解耦问题.最大的优势是在特定场景下使用的属性,可以在特定场景下定义.这样业务上不会导致代码全部混在某个模块里.提升代码可维护性. 我们举例一段代码.假设有个类Person.包含了身份ID(IdentityID),姓名(Name),出生年月(Birth date),性别(gender),民族(Nation). 有一个School

随机推荐