Use Image without adding it in WPF Project/Solution Resources - c#

I am building a Gallery for Floors in WPF application, I am reading Image Names from csv file and User will directly copy Images into Resources folder.
StreamReader streamReader = new StreamReader(
(Application.Current as PlayOnSurface.App).ProjectAmenitiesCSVPath);
// browse the csv file line by line until the end of the file
while (!streamReader.EndOfStream)
{
// for each line, split it with the split caractere (that may no be ';')
var splitLine = streamReader.ReadLine().Split('~');
if (int.Parse(splitLine[0].Trim())
== (Application.Current as PlayOnSurface.App).SelectedFloorID)
{
for (int i = 2; i < splitLine.Length; i++)
{
ImageList.Add(splitLine[i].Trim());
}
}
// map the splitted line with an entity
}
streamReader.Close();
btnPrev.IsEnabled = false;
if (ImageList.Count <= 1)
btnNext.IsEnabled = false;
imgGallery.Source = Utilities.LoadBitmapFromResource(
"Resources/Amenities/" + ImageList[0],
null);
Utilities code
public static BitmapImage LoadBitmapFromResource(string pathInApplication, Assembly assembly = null)
{
if (assembly == null)
{
assembly = Assembly.GetCallingAssembly();
}
if (pathInApplication[0] == '/')
{
pathInApplication = pathInApplication.Substring(1);
}
return new BitmapImage(new Uri(#"pack://application:,,,/" + assembly.GetName().Name + ";component/" + pathInApplication, UriKind.Absolute));
}
I can't add it in Resources folder in project and I can't embed resources because I want user to copy their file in Resources folder and update csv file after deployment.
My code will read it and display gallery but in that case WPF throws "Cannot locate resource" exception when loading the image on imgGallery.Source line in above code.
my csv file format:
1~FirstFloor~img1.png~img2.png~img3.png
2~SecondFloor~img1.png~img2.png~img3.png
3~ThirdFloor~img1.png~img2.png~img3.png
4~FourthFloor~img1.png~img2.png~img3.png

Maybe use the file absolute path, simply change the last line of the LoadBitmapFromResource function to:
return new BitmapImage(new Uri(assembly.GetName().CodeBase + pathInApplication, UriKind.Absolute));
this should do the fix.
Also you can try using the UriKind.Relative.
Good day

You're using an incorrect authority in your URI scheme. The application authority is to be used for data files which are known at compile time (aka static resources). You have to replace it with the siteoforigin authority.
I'd also recommend replacing your home-rolled text file format with a standard CSV file. Many programs such as Excel support generating comma-separated fields by default. While custom separators can be put, they usually require additional effort. Reading and parsing comma-separated files is also easier because many libraries are available to do these tasks.

Related

c# Novacode.Picture to System.Drawing.Image

I'm reading in a .docx file using the Novacode API, and am unable to create or display any images within the file to a WinForm app due to not being able to convert from a Novacode Picture (pic) or Image to a system image. I've noticed that there's very little info inside the pic itself, with no way to get any pixel data that I can see. So I have been unable to utilize any of the usual conversion ideas.
I've also looked up how Word saves images inside the files as well as Novacode source for any hints and I've come up with nothing.
My question then is is there a way to convert a Novacode Picture to a system one, or should I use something different to gather the image data like OpenXML? If so, would Novacode and OpenXML conflict in any way?
There's also this answer that might be another place to start.
Any help is much appreciated.
Okay. This is what I ended up doing. Thanks to gattsbr for the advice. This only works if you can grab all the images in order, and have descending names for all the images.
using System.IO.Compression; // Had to add an assembly for this
using Novacode;
// Have to specify to remove ambiguous error from Novacode
Dictionary<string, System.Drawing.Image> images = new Dictionary<string, System.Drawing.Image>();
void LoadTree()
{
// In case of previous exception
if(File.Exists("Images.zip")) { File.Delete("Images.zip"); }
// Allow the file to be open while parsing
using(FileStream stream = File.Open("Images.docx", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
using(DocX doc = DocX.Load(stream))
{
// Work rest of document
// Still parse here to get the names of the images
// Might have to drag and drop images into the file, rather than insert through Word
foreach(Picture pic in doc.Pictures)
{
string name = pic.Description;
if(null == name) { continue; }
name = name.Substring(name.LastIndexOf("\\") + 1);
name = name.Substring(0, name.Length - 4);
images[name] = null;
}
// Save while still open
doc.SaveAs("Images.zip");
}
}
// Use temp zip directory to extract images
using(ZipArchive zip = ZipFile.OpenRead("Images.zip"))
{
// Gather all image names, in order
// They're retrieved from the bottom up, so reverse
string[] keys = images.Keys.OrderByDescending(o => o).Reverse().ToArray();
for(int i = 1; ; i++)
{
// Also had to add an assembly for ZipArchiveEntry
ZipArchiveEntry entry = zip.GetEntry(String.Format("word/media/image{0}.png", i));
if(null == entry) { break; }
Stream stream = entry.Open();
images[keys[i - 1]] = new Bitmap(stream);
}
}
// Remove temp directory
File.Delete("Images.zip");
}

Loop through Embedded Resources and copy to local path

I have a simple WinForms application, but it has some Embedded Resources (in a subfolder under "Resources") that I would like to copy out to a folder on the computer. Currently, I have the latter working (with a explicit method naming the Embedded Resource and where it should go):
string path = #"C:\Users\derek.antrican\";
using (Stream input = Assembly.GetExecutingAssembly().GetManifestResourceStream("WINFORMSAPP.Resources.SUBFOLDER.FILE.txt"))
using (Stream output = File.Create(path + "FILE.txt"))
{
input.CopyTo(output);
}
But I'm still trying to figure out how to get the former working: looping through all the resources in the "WINFORMSAPP.Resources.SUBFOLDER" folder and moving them. I've done quite a bit of Googling, but I'm still not sure how to get a list of each Embedded Resource in this subfolder.
Any help would be GREATLY appreciated!
Start by getting all resources embedded in your assembly:
Assembly.GetExecutingAssembly().GetManifestResourceNames()
You can check these names against the name of your desired subfolder to see if they are inside or outside it with a simple call to StartsWith.
Now loop through the names, and get the corresponding resource stream:
const string subfolder = "WINFORMSAPP.Resources.SUBFOLDER.";
var assembly = Assembly.GetExecutingAssembly();
foreach (var name in assembly.GetManifestResourceNames()) {
// Skip names outside of your desired subfolder
if (!name.StartsWith(subfolder)) {
continue;
}
using (Stream input = assembly.GetManifestResourceStream(name))
using (Stream output = File.Create(path + name.Substring(subfolder.Length))) {
input.CopyTo(output);
}
}

Programmatically Load Embedded Resource File

i recently implemented the following code in order to programatically build a project/exe. In this exe build, i wanted to store a bunch of "actual files" inside of resources, as streams.
Here's how i'm adding the files into the resource file (singular), and then embedding that resource file into the compiler parameters:
List<string> UserFiles = new List<string>();
UserFiles.AddRange(Helpers.GetFilesInFolder(this.txt_Publish_Folder.Text));
string folder = this.txt_Package_Location.Text;
folder = folder + "\\Package_" + DateTime.Now.ToLongTimeString().Replace(":", "_").Replace(".", "_");
Directory.CreateDirectory(folder);
CompilerParameters parameters = new CompilerParameters();
parameters.GenerateExecutable = true;
parameters.IncludeDebugInformation = true;
parameters.GenerateInMemory = false;
parameters.WarningLevel = 3;
parameters.CompilerOptions = "/optimize";
parameters.OutputAssembly = folder + "\\Install_" + this.txt_AppName.Text + ".exe";
parameters.ReferencedAssemblies.Add("Microsoft.CSharp.dll");
parameters.ReferencedAssemblies.Add("System.dll");
parameters.ReferencedAssemblies.Add("System.Core.dll");
parameters.ReferencedAssemblies.Add("System.Data.dll");
parameters.ReferencedAssemblies.Add("System.Data.DataSetExtensions.dll");
parameters.ReferencedAssemblies.Add("System.Xml.dll");
parameters.ReferencedAssemblies.Add("System.Xml.Linq.dll");
CodeDomProvider codeProvider = CodeDomProvider.CreateProvider("CSharp");
if (codeProvider.Supports(GeneratorSupport.Resources))
{
string temp = Path.Combine(Path.GetTempPath(), Path.GetTempFileName());
//create temp file first, because we want to append to it so as to have a single resource file with multiple stream entries...
File.WriteAllText(temp, null);
for (int i = 0; i < UserFiles.Count; i++ )
{
byte[] FileBytes = File.ReadAllBytes(UserFiles[i]);
using (FileStream stream = new FileStream(temp, FileMode.Append))
{
using (ResourceWriter writer = new ResourceWriter(stream))
{
writer.AddResource(Path.GetFileName(UserFiles[i]), FileBytes);
}
}
}
parameters.EmbeddedResources.Add(temp);
}
CompilerResults res = codeProvider.CompileAssemblyFromFile(parameters, #"C:\HIDDENPATH\Program.cs");
This works great, doesn't yield any errors, and i can actually get the embedded resource file out in the Console Application i built (the one referenced above as Prorgam.cs) through the code below. The problem i am having however is with "Loading" this resource file into the application assembly / getting its values out somehow... here's the code i have so far to do that:
static void Main(string[] args)
{
Console.WriteLine("Checking for resources... please wait...");
Assembly thisExe;
thisExe = Assembly.GetExecutingAssembly();
List<string> resources = thisExe.GetManifestResourceNames().ToList();
if(resources.Count >= 1)
{
try
{
string baseName = resources[0];
ResourceManager mgr = new ResourceManager(baseName, thisExe);
Console.WriteLine("retrieved manager...");
Console.ReadLine();
ResourceSet set = mgr.GetResourceSet(Thread.CurrentThread.CurrentCulture, true, true);
int count = set.Cast<object>().Count();
Console.WriteLine("Found [" + count.ToString() + "] embedded resources. Would you like to enumerate them?");
ConsoleKeyInfo input = Console.ReadKey();
if (input.Key == ConsoleKey.Y)
{
// Build the string of resources.
foreach (string resource in resources)
Console.WriteLine(resource);
}
}
catch (Exception ex)
{
Console.Write(ex.Message);
Console.ReadLine();
}
}
Console.ReadLine();
}
Whenever i run the built exe, i get the following result:
Checking for resources... please wait...
retrieved manager...
Could not find any resources appropriate for the specified culture or the neutral culture. Make sure "tmpCC59.tmp.resources" was correctly embedded or linked into assembly "Install_testing" at compile time, or that all the satellite assemblies required are loadable and fully signed.
Can someone please tell me why this is happenning? i've tried all different culture sets i could think of and still nothing comes out. Is the problem related to how i'm "Embedding" or how i'm "Loading" ?
Ok folks, so this is what i ended up with, which actually fixed all my problems.
Instead of trying to add "Resource Entries" into a single "Resource File", i'm now adding every file as its own resource, directly from the source file (don't know why i thought making a temp copy of the original and using a stream was necessary but it's not) like this:
for (int i = 0; i < WebAppFiles.Count; i++)
{
parameters.EmbeddedResources.Add(WebAppFiles[i]);
}
Then, when comes time to extract the actual files, instead of trying to load a "set" from a single "resource file", i simply extract the data from each embedded resource, like this:
for (int i = 0; i < resources.Count; i++)
{
Console.WriteLine("Extracting file: " + resources[i] + "...");
Stream stream = thisExe.GetManifestResourceStream(resources[i]);
byte[] bytes = new byte[(int)stream.Length];
stream.Read(bytes, 0, bytes.Length);
File.WriteAllBytes(dir + resources[i], bytes);
}
This new way basically means that you tell the compiler that "these files are necessary for the exe and are embedded", so when you call the "build" command, it actually does all the work for you and reads the files in as byte arrays and embeds those into the executable file.
Then, at the other hand, you simply tell the program that you want to save the embedded resources as files, using their own file names.
Hopefully this helps someone else who is trying to do this... as every other post for this type of question had some over complicated answers that were either incomplete (e.g.: how to embed, but not retrieve) or didn't actually work.

Write from Resource to file where resource could be text or image

I'm having a problem trying to write my resource files to disk (all resource files part of the same project and assembly).
If I add
var temp = Assembly.GetExecutingAssembly().GetManifestResourceNames();
This returns a string[] in the following format
Gener.OptionsDialogForm.resources
Gener.ProgressDialog.resources
Gener.Properties.Resources.resources
Gener.g.resources
Gener.Resources.reusable.css
Gener.Resources.other.jpg
The last 2 of the array are the only 2 files I want but I assume it's not a guarantee that this will always be the case. The array could come through in another order as code is changed so I cannot explicity call the item at a given index (temp[4])
So, I could do
foreach (string item in Assembly
.GetExecutingAssembly()
.GetManifestResourceNames())
{
if (!item.Contains("Gener.Resources."))
continue;
//Do whatever I need to do
}
But this is just horrible! I face another problem with this approach; This doesn't return the file name with the extension, just the Name and as such, I have no idea what the extension is.
This is the code as it currently is
public void CopyAllFiles()
{
var files = Resources.ResourceManager.GetResourceSet(System.Globalization.CultureInfo.CurrentUICulture, true, true);
//var temp = Assembly.GetExecutingAssembly().GetManifestResourceNames();
foreach (DictionaryEntry item in files)
{
using (var resourceFileStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Gener.Resources." + item.Key.ToString() + ".css")) // this won't work, I can't hard code .css as the extension could be different
{
Stream stream = new FileStream(this.DirPath, FileMode.Create, FileAccess.Write);
resourceFileStream.CopyTo(stream);
stream.Dispose();
}
}
files.Dispose();
}
But this seems... wrong... Is this how any one else would do this, I'm sure I'm missing something and such a task is common that there is a better solution?
The resource names are predictable, you could just pass the name to the Assembly.GetManifestResourceStream() method.
More productively, Visual Studio supports a designer for this so you don't have to guess at the string you need to pass. Use Project + Properties, Resources tab. Click on the dropdown arrow of the Add Resource button and select your file. You can now refer to the resource in your code with a variable name. Like:
File.WriteAllText(path, Properties.Resources.reusable);
Do consider the so-so wisdom of copying resources to files at runtime. You get the exact same outcome by just using an installer or XCopy to copy the files just once. With the significant advantage is that those resources won't eat memory address space anymore and that you won't get in trouble when you don't have write access to the directory. Which is common with UAC enabled.
This was what I used! Hopefully it will help others. It feels some what hacking, but it works!
/// <summary>
/// Copies all the files from the Resource Manifest to the relevant folders.
/// </summary>
internal void CopyAllFiles()
{
var resourceFiles = Assembly.GetExecutingAssembly().GetManifestResourceNames();
foreach (var item in resourceFiles)
{
string basePath = Resources.ResourceManager.BaseName.Replace("Properties.", "");
if (!item.Contains(basePath))
continue;
var destination = this._rootFolder + "\\" + this._javaScriptFolder + "\\" + item.Replace(basePath + ".", "");
using (Stream resouceFile = Assembly.GetExecutingAssembly().GetManifestResourceStream(item))
using (Stream output = File.Create(destination))
{
resouceFile.CopyTo(output);
}
}
}

How can I list the contents of a .zip folder in c#?

How can I list the contents of a zipped folder in C#? For example how to know how many items are contained within a zipped folder, and what is their name?
.NET 4.5 or newer finally has built-in capability to handle generic zip files with the System.IO.Compression.ZipArchive class (http://msdn.microsoft.com/en-us/library/system.io.compression.ziparchive%28v=vs.110%29.aspx) in assembly System.IO.Compression. No need for any 3rd party library.
string zipPath = #"c:\example\start.zip";
using (ZipArchive archive = ZipFile.OpenRead(zipPath))
{
foreach (ZipArchiveEntry entry in archive.Entries)
{
Console.WriteLine(entry.FullName);
}
}
DotNetZip - Zip file manipulation in .NET languages
DotNetZip is a small, easy-to-use class library for manipulating .zip files. It can enable .NET applications written in VB.NET, C#, any .NET language, to easily create, read, and update zip files.
sample code to read a zip:
using (var zip = ZipFile.Read(PathToZipFolder))
{
int totalEntries = zip.Entries.Count;
foreach (ZipEntry e in zip.Entries)
{
e.FileName ...
e.CompressedSize ...
e.LastModified...
}
}
If you are using .Net Framework 3.0 or later, check out the System.IO.Packaging Namespace. This will remove your dependancy on an external library.
Specifically check out the ZipPackage Class.
Check into SharpZipLib
ZipInputStream inStream = new ZipInputStream(File.OpenRead(fileName));
while (inStream.GetNextEntry())
{
ZipEntry entry = inStream.GetNextEntry();
//write out your entry's filename
}
Ick - that code using the J# runtime is hideous! And I don't agree that it is the best way - J# is out of support now. And it is a HUGE runtime, if all you want is ZIP support.
How about this - it uses DotNetZip (Free, MS-Public license)
using (ZipFile zip = ZipFile.Read(zipfile) )
{
bool header = true;
foreach (ZipEntry e in zip)
{
if (header)
{
System.Console.WriteLine("Zipfile: {0}", zip.Name);
if ((zip.Comment != null) && (zip.Comment != ""))
System.Console.WriteLine("Comment: {0}", zip.Comment);
System.Console.WriteLine("\n{1,-22} {2,9} {3,5} {4,9} {5,3} {6,8} {0}",
"Filename", "Modified", "Size", "Ratio", "Packed", "pw?", "CRC");
System.Console.WriteLine(new System.String('-', 80));
header = false;
}
System.Console.WriteLine("{1,-22} {2,9} {3,5:F0}% {4,9} {5,3} {6:X8} {0}",
e.FileName,
e.LastModified.ToString("yyyy-MM-dd HH:mm:ss"),
e.UncompressedSize,
e.CompressionRatio,
e.CompressedSize,
(e.UsesEncryption) ? "Y" : "N",
e.Crc32);
if ((e.Comment != null) && (e.Comment != ""))
System.Console.WriteLine(" Comment: {0}", e.Comment);
}
}
I'm relatively new here so maybe I'm not understanding what's going on. :-)
There are currently 4 answers on this thread where the two best answers have been voted down. (Pearcewg's and cxfx's) The article pointed to by pearcewg is important because it clarifies some licensing issues with SharpZipLib.
We recently evaluated several .Net compression libraries, and found that DotNetZip is currently the best aleternative.
Very short summary:
System.IO.Packaging is significantly slower than DotNetZip.
SharpZipLib is GPL - see article.
So for starters, I voted those two answers up.
Kim.
If you are like me and do not want to use an external component, here is some code I developed last night using .NET's ZipPackage class.
var zipFilePath = "c:\\myfile.zip";
var tempFolderPath = "c:\\unzipped";
using (Package package = ZipPackage.Open(zipFilePath, FileMode.Open, FileAccess.Read))
{
foreach (PackagePart part in package.GetParts())
{
var target = Path.GetFullPath(Path.Combine(tempFolderPath, part.Uri.OriginalString.TrimStart('/')));
var targetDir = target.Remove(target.LastIndexOf('\\'));
if (!Directory.Exists(targetDir))
Directory.CreateDirectory(targetDir);
using (Stream source = part.GetStream(FileMode.Open, FileAccess.Read))
{
source.CopyTo(File.OpenWrite(target));
}
}
}
Things to note:
The ZIP archive MUST have a [Content_Types].xml file in its root. This was a non-issue for my requirements as I will control the zipping of any ZIP files that get extracted through this code. For more information on the [Content_Types].xml file, please refer to: A New Standard For Packaging Your Data There is an example file below Figure 13 of the article.
This code uses the Stream.CopyTo method in .NET 4.0
The best way is to use the .NET built in J# zip functionality, as shown in MSDN: http://msdn.microsoft.com/en-us/magazine/cc164129.aspx. In this link there is a complete working example of an application reading and writing to zip files. For the concrete example of listing the contents of a zip file (in this case a Silverlight .xap application package), the code could look like this:
ZipFile package = new ZipFile(packagePath);
java.util.Enumeration entries = package.entries();
//We have to use Java enumerators because we
//use java.util.zip for reading the .zip files
while ( entries.hasMoreElements() )
{
ZipEntry entry = (ZipEntry) entries.nextElement();
if (!entry.isDirectory())
{
string name = entry.getName();
Console.WriteLine("File: " + name + ", size: " + entry.getSize() + ", compressed size: " + entry.getCompressedSize());
}
else
{
// Handle directories...
}
}
Aydsman had a right pointer, but there are problems. Specifically, you might find issues opening zip files, but is a valid solution if you intend to only create pacakges. ZipPackage implements the abstract Package class and allows manipulation of zip files. There is a sample of how to do it in MSDN: http://msdn.microsoft.com/en-us/library/ms771414.aspx. Roughly the code would look like this:
string packageRelationshipType = #"http://schemas.microsoft.com/opc/2006/sample/document";
string resourceRelationshipType = #"http://schemas.microsoft.com/opc/2006/sample/required-resource";
// Open the Package.
// ('using' statement insures that 'package' is
// closed and disposed when it goes out of scope.)
foreach (string packagePath in downloadedFiles)
{
Logger.Warning("Analyzing " + packagePath);
using (Package package = Package.Open(packagePath, FileMode.Open, FileAccess.Read))
{
Logger.OutPut("package opened");
PackagePart documentPart = null;
PackagePart resourcePart = null;
// Get the Package Relationships and look for
// the Document part based on the RelationshipType
Uri uriDocumentTarget = null;
foreach (PackageRelationship relationship in
package.GetRelationshipsByType(packageRelationshipType))
{
// Resolve the Relationship Target Uri
// so the Document Part can be retrieved.
uriDocumentTarget = PackUriHelper.ResolvePartUri(
new Uri("/", UriKind.Relative), relationship.TargetUri);
// Open the Document Part, write the contents to a file.
documentPart = package.GetPart(uriDocumentTarget);
//ExtractPart(documentPart, targetDirectory);
string stringPart = documentPart.Uri.ToString().TrimStart('/');
Logger.OutPut(" Got: " + stringPart);
}
// Get the Document part's Relationships,
// and look for required resources.
Uri uriResourceTarget = null;
foreach (PackageRelationship relationship in
documentPart.GetRelationshipsByType(
resourceRelationshipType))
{
// Resolve the Relationship Target Uri
// so the Resource Part can be retrieved.
uriResourceTarget = PackUriHelper.ResolvePartUri(
documentPart.Uri, relationship.TargetUri);
// Open the Resource Part and write the contents to a file.
resourcePart = package.GetPart(uriResourceTarget);
//ExtractPart(resourcePart, targetDirectory);
string stringPart = resourcePart.Uri.ToString().TrimStart('/');
Logger.OutPut(" Got: " + stringPart);
}
}
}
The best way seems to use J#, as shown in MSDN: http://msdn.microsoft.com/en-us/magazine/cc164129.aspx
There are pointers to more c# .zip libraries with different licenses, like SharpNetZip and DotNetZip in this article: how to read files from uncompressed zip in c#?. They might be unsuitable because of the license requirements.

Categories