资源文件
开发专题 · 图片、JSON 与 .resx 的添加、生成操作与代码读取
本页说明如何在 .NET Framework 4.8 窗体项目中加入图片、JSON 等文件,如何设置「成功行为」(Build Action)与「复制到输出目录」,以及两种常用读取方式:嵌入到程序集(含 Properties.Resources)与随 EXE 落地的内容文件。操作文案以 LCode 中文界面为准;凡有代码均同时给出 C# 5.0 与 VB.NET。新建文件通用步骤见 文件与编辑器。
向项目添加图片 / JSON
推荐先把资源放到项目目录下的固定文件夹(例如 Images\、Data\),再纳入项目树,避免绝对路径散落。
- 在项目浏览器中选中目标项目或子文件夹。
- 右键 → 添加 → 添加已存在的文件(对话框标题同名;可多选)。筛选器含「图片文件」「资源文件」「所有文件」等。
- 若所选文件不在当前目录内,会询问「您是想把文件复制到目标目录还是想创建指向原文件的链接?」:日常选复制;仅当共享磁盘上的同一份原件时再选链接。
- 确认后文件出现在项目树中。图片 / JSON 默认「成功行为」为
None(见下一节),需按用途改成EmbeddedResource或Content。
也可新建空白 .resx:菜单 文件 → 新建 → 文件…,或右键项目 → 添加 → 新建项…,在分类其它中选空资源文件(默认名形如 ResourceN.resx)。建议放到 Properties\ 并命名为 Resources.resx,以便生成 Properties.Resources 强类型类。
Properties\AssemblyInfo,不自带 Properties\Resources.resx。需要 Properties.Resources 时请自行添加空资源文件,并在属性面板把「自定义工具」设为 ResXFileCodeGenerator(或 PublicResXFileCodeGenerator),保存后生成 Resources.Designer.cs / .vb。
| 界面文案 | 含义 |
|---|---|
| 添加 | 项目树右键菜单分组 |
| 添加已存在的文件 | 把磁盘上的图片、JSON、.resx 等纳入项目 |
| 新建项… | 从模板创建新文件(含「空资源文件」) |
| 空资源文件 | 模板名;分类「其它」;生成空 .resx |
| 复制 / 链接 | 添加项目外文件时的两种纳入方式 |
生成操作与项目树位置
选中项目树中的文件后,在属性面板查看:
- 成功行为:对应 MSBuild 的 ItemType / Build Action;下拉值为英文:
Compile、EmbeddedResource、None、Content。 - 复制到输出目录:
Never(默认)、Always、PreserveNewest。 - 自定义工具:对
.resx常用ResXFileCodeGenerator,用于生成强类型资源类。
默认规则(以 LCode / .csproj 实际为准):
.resx/.resources→ 默认为EmbeddedResource。- 图片(
.png/.jpg/.bmp/.ico等)、JSON、文本等 → 默认为None(进项目树但不嵌入、也不自动复制到输出目录)。 - 源码
.cs/.vb→Compile。
| 成功行为(ItemType) | 写入 .csproj | 运行时如何拿到 | 典型用途 |
|---|---|---|---|
None |
<None Include="…" /> |
默认不进输出目录;除非改「复制到输出目录」 | 仅纳入源码管理、构建时不需要的文件 |
Content |
<Content Include="…" /> |
配合「复制到输出目录」= Always / PreserveNewest,相对 EXE 路径读取 | 可替换的配置 JSON、外部图片、运行时可改文件 |
EmbeddedResource |
<EmbeddedResource Include="…" /> |
嵌入程序集;可用清单流或 .resx 强类型属性 |
必须随程序发布、不希望被轻易改掉的资源 |
Compile |
<Compile Include="…" /> |
参与编译,不是数据资源 | 源码文件 |
Resources.resx(嵌入)。需要用户或运维改配置的 JSON,用 Content + 复制到输出目录。改完属性后执行 编译 → 编译解决方案(F8),再核对 bin\Debug(或当前配置)是否出现预期文件。
代码中使用图片
方式 A:在资源编辑器中打开 Properties\Resources.resx,右键 → 添加文件…,选入图片(键名如 Logo)。确认「自定义工具」为 ResXFileCodeGenerator 并已生成 Designer 文件后,在代码中用强类型属性:
using System;
using System.Windows.Forms;
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
// 键名 Logo 对应 Resources.resx 中的图片项
this.pictureBox1.Image = Properties.Resources.Logo;
}
}
Imports System
Imports System.Windows.Forms
Public Class MainForm
Inherits Form
Public Sub New()
InitializeComponent()
' 键名 Logo 对应 Resources.resx 中的图片项
Me.PictureBox1.Image = Properties.Resources.Logo
End Sub
End Class
命名空间以 Designer 生成为准(Properties\Resources.resx 一般为 Properties.Resources)。若资源类为 Friend/internal,请在同一程序集内访问;需要跨程序集时把自定义工具改为 PublicResXFileCodeGenerator。
方式 B:把图片设为 Content,并将「复制到输出目录」设为 PreserveNewest 或 Always,再按相对 EXE 的路径加载:
using System;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
public partial class MainForm : Form
{
private void LoadLogoFromDisk()
{
string path = Path.Combine(Application.StartupPath, "Images", "logo.png");
if (File.Exists(path))
{
this.pictureBox1.Image = Image.FromFile(path);
}
}
}
Imports System
Imports System.Drawing
Imports System.IO
Imports System.Windows.Forms
Public Class MainForm
Inherits Form
Private Sub LoadLogoFromDisk()
Dim path As String = Path.Combine(Application.StartupPath, "Images", "logo.png")
If File.Exists(path) Then
Me.PictureBox1.Image = Image.FromFile(path)
End If
End Sub
End Class
项目树中路径为 Images\logo.png 时,复制到输出后应为 bin\…\Images\logo.png(相对结构一般会保留)。若仍找不到文件,先确认「成功行为」与「复制到输出目录」,再看输出目录实际内容。
代码中读取 JSON 资源
同样可走「落地文件」或「嵌入」两条路。下面以 Content + 复制到输出为例:项目中有 Data\appsettings.json,内容为 {"Title":"Demo"}。本页只演示读入文本并取一个字段;完整解析见后续专题 JSON 与 XML 解析。
using System;
using System.Collections.Generic;
using System.IO;
using System.Web.Script.Serialization;
using System.Windows.Forms;
public partial class MainForm : Form
{
private void LoadTitleFromJson()
{
string path = Path.Combine(Application.StartupPath, "Data", "appsettings.json");
string json = File.ReadAllText(path);
JavaScriptSerializer serializer = new JavaScriptSerializer();
Dictionary<string, object> map =
serializer.Deserialize<Dictionary<string, object>>(json);
object titleObj;
if (map.TryGetValue("Title", out titleObj) && titleObj != null)
{
this.Text = titleObj.ToString();
}
}
}
Imports System
Imports System.Collections.Generic
Imports System.IO
Imports System.Web.Script.Serialization
Imports System.Windows.Forms
Public Class MainForm
Inherits Form
Private Sub LoadTitleFromJson()
Dim path As String = Path.Combine(Application.StartupPath, "Data", "appsettings.json")
Dim json As String = File.ReadAllText(path)
Dim serializer As New JavaScriptSerializer()
Dim map As Dictionary(Of String, Object) =
serializer.Deserialize(Of Dictionary(Of String, Object))(json)
Dim titleObj As Object = Nothing
If map.TryGetValue("Title", titleObj) AndAlso titleObj IsNot Nothing Then
Me.Text = titleObj.ToString()
End If
End Sub
End Class
JavaScriptSerializer 位于程序集 System.Web.Extensions。若项目尚无该引用:右键项目 → 添加引用 → GAC 或浏览框架程序集,加入 System.Web.Extensions(详见 引用 DLL)。
若把 JSON 设为 EmbeddedResource,可用清单流读取(资源名通常为「默认命名空间 + 相对路径,点号分隔」;在项目属性中确认默认命名空间):
using System;
using System.IO;
using System.Reflection;
// 假设默认命名空间为 MyApp,文件为 Data\appsettings.json
string resourceName = "MyApp.Data.appsettings.json";
Assembly asm = Assembly.GetExecutingAssembly();
using (Stream stream = asm.GetManifestResourceStream(resourceName))
{
if (stream == null)
{
throw new InvalidOperationException("找不到嵌入资源: " + resourceName);
}
using (StreamReader reader = new StreamReader(stream))
{
string json = reader.ReadToEnd();
// 再按上文方式反序列化
}
}
Imports System
Imports System.IO
Imports System.Reflection
' 假设默认命名空间为 MyApp,文件为 Data\appsettings.json
Dim resourceName As String = "MyApp.Data.appsettings.json"
Dim asm As Assembly = Assembly.GetExecutingAssembly()
Using stream As Stream = asm.GetManifestResourceStream(resourceName)
If stream Is Nothing Then
Throw New InvalidOperationException("找不到嵌入资源: " & resourceName)
End If
Using reader As New StreamReader(stream)
Dim json As String = reader.ReadToEnd()
' 再按上文方式反序列化
End Using
End Using
注意事项与暂不支持
| 事项 | 说明 |
|---|---|
| 默认行为易踩坑 | 图片 / JSON 加入后多为 None,既不嵌入也不复制;运行时「找不到文件」时先查属性面板。 |
| 「成功行为」文案 | 属性名中文为「成功行为」(英源为 Build action);下拉选项仍是 None / Content / EmbeddedResource 等英文 ItemType。 |
| 强类型资源类 | 未设置 ResXFileCodeGenerator 时不会出现 Properties.Resources;改完自定义工具后需保存并编译。 |
| 清单资源名 | 嵌入非 .resx 文件时,名称依赖默认命名空间与文件夹;可用反射列出 GetManifestResourceNames() 核对。 |
| Win32 / .rc 资源 | 本手册不展开本机资源脚本工作流;相关可视化编辑能力暂不支持,待更新完善。 |
| 卫星程序集 / 多语言 .resx | 按区域性拆分本地化资源的完整流程暂不支持,待更新完善;当前以单语言嵌入与 Content 落地为主。 |
常见资源类型对照
| 类型 | 推荐成功行为 | 复制到输出目录 | 代码入口(示例) |
|---|---|---|---|
| 图片(嵌入) | 放入 .resx(自身为 EmbeddedResource) |
Never | Properties.Resources.键名 |
| 图片(落地) | Content | PreserveNewest 或 Always | Image.FromFile(…) |
| JSON(可改配置) | Content | PreserveNewest 或 Always | File.ReadAllText + 反序列化 |
| JSON(随程序固定) | EmbeddedResource | Never | GetManifestResourceStream |
| 空 / 项目 .resx | EmbeddedResource(默认) | Never | 自定义工具生成 Designer 后强类型访问 |
| 图标 .ico(窗体 Icon) | 可进 .resx,或 Content + 复制 | 按所选方式 | 设计器属性或 new Icon(path) |
| 仅归档、不参与运行 | None | Never | (运行时不使用) |
相关阅读
- 文件与编辑器 — 新建项、打开与项目树操作
- 运行与编译 — 配置、输出目录
- 引用 DLL — 添加
System.Web.Extensions等引用 - JSON 与 XML 解析 — Newtonsoft / DataContract / XmlSerializer
- 文件与流读写 — 更广的文件 IO
- C# / VB.NET 窗体应用入门 — 窗体项目起步