Avoid hardcoded icon file paths in .resx file - winforms

I'm trying to get our app to handle different icon sets (make it sort of skinnable). I asked about the usual way to do it here. When I tried to apply the solution from the answer, I replaced all hardcoded icon paths in .resx files with paths using an environment variable. For example, I replaced...
<data name="btnDel" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\..\Icons\btnDel.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
...with:
<data name="btnDel" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>$(IconsFolder)\btnDel.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
Then, as an initial test, I defined a System-level environment variable called IconsFolder, applied changes, restarted Visual Studio and tried to build. But I got this error:
D:\SVN.DRA.WorkingCopy\UserControl\My Project\Resources.resx(123,5): error MSB3103: Invalid Resx file. Could not find a part of the path 'D:\SVN.DRA.WorkingCopy\UserControl\My Project\$(IconsFolder)\btnDel.png'. Line 123, position 5.
It seems like .resx files don't understand environment variables. How can I avoid hardcoding those paths, then?
EDIT: Each icon can be referenced by more than one project, so the environment variable of whatever mechanism is used to configure the paths must be available on a solution-wide basis, and I should be able to set it from inside an MSBuild script.
EDIT 2: All my forms are defined in C# or VB.NET projects

System wide environment variables may not work. You might want to try following the instructions here:
http://msdn.microsoft.com/en-us/library/ms173406.aspx

I don't think there is a way to accomplish using the resource file. One way to achieve this is to drop your bitmap on your project, right click it and change build action to "Embedded Resource". Open up the project file in text editor and change the path to your environment variable. (this is how we do it with VS 2008, they have made it easier since then). Then in code you can do this....
Assembly myAssembly = Assembly.GetExecutingAssembly();
Stream myStream = myAssembly.GetManifestResourceStream("btnDel.png");
new System.Drawing.Bitmap(myStream);

Related

WPF cannot set Build Action for added file [duplicate]

I would like to assign a BitmapImage from my Resources.resx to an Image.
Beforehand I saved a .png image to Resources.resx. This image is now located in "/Resources/logo.png".
After reading several SO posts and other articles I have this now:
logoImage.Source = new BitmapImage(new Uri(#"pack://application:,,,/Resources/logo.png"));
Unfortunately it doesn't work.
I don't know how to solve this.
I tried to replace the Pack-URI with the fullpath and it worked but I would like to use relative paths in order to use the same source on different machines on which the absolute path would be incorrect.
Can anyone help me out with this?
In order to make that Uri work, the file logo.png must be contained in a folder named "Resources" in your VS project (see first image), and its Build Action must be set to Resource (see second image).
This Resources folder is completely unrelated to Resources.resx. You may rename it to whatever you like.

Strong named assembly reference in resouce resx files

The Scenario
The vendor of GreatLib.dll deploys a custom control. It is getting used in customers Winforms application. GreatLib is strongly named and exposes some types. When these types are used as public properties in the customers forms, they are also available in the Windows Forms designer. Here is, where the hassle starts:
The designer will create items for all those types in the resource file of the form and serialize their default(?) values in those items. Since GreatLib is strongly named, the reference will include the fully qualified name for the assembly. (I think this is what Resgen does?). Such items may look as follows:
<data name="vector3Control1.value" mimetype="application/x-microsoft.net.object.binary.base64">
<value>
AAEAAAD/////AQAAAAAAAAAMAgAAAFRJTE51bWVyaWNzLCBWZXJzaW9uPTQuMi41MzM4LjQ4MDIxLCBD
dWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPThkOWJmNTBlZjg1NDczNGQFAQAAABpJTE51bWVy
aWNzLkRyYXdpbmcuVmVjdG9yMwMAAAADbV94A21feQNtX3oAAAALCwsCAAAAAAAAAAAAAAAAAAAACw==
</value>
</data>
In the Form.Designer.cs file the designer generates some code similar to this in order to read in the resource items:
this.vector3Control1.value = ((ILNumerics.Drawing.Vector3)(resources.GetObject("vector3Control1.value")));
The Problem
Vendor provides an update to GreatLib, the customer replaces the old version with the new version. But the resource file will stay the same! Hence, we either get an InvalidCastExeption on the Designer.cs code or - if the old assembly was not found anymore - a FileNotFoundException.
What is the recommended way to handle strong names in resx files?
Preventing from them completely by using
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
? Or manually transforming the base64 data on each update as suggested by HansPassant?

How do I handle multiple App.config files in my WIX Project?

I'm trying to figure out a way to handle multiple app.config files. Each app.config file is for a different enviornment.
Currently, I have multiple .SED Files(each .SED file references to one enviornment) that in their post-command(PostInstallCmd) they execute a .bat file that looks for a certain app.config and puts in the directory needed. However, the problem with this method is that the command prompt opens up after the installation and I am unable to figure out how to suppress the command prompt.
I am not sure if my way is the best idea to go about handling multiple App.Config. That is why I am throwing it out there to see if there is a better method or if someone has a solution to the method I already have.
If you keep your config files in separate components, you can add a condition element to your components.
Component conditions are evaluated during the CostFinalize standard action (source), so you'd have to use a custom action that runs before file costing to gather info about the environment. You can use the built-in OSInfo custom actions or predefined properties to do so.
Just set the File/#Name attribute to the same across all the config files if they have different names on your build server. Unfortunately, this will set off ICE30, but if the conditions are mutually exclusive, you can safely ignore it.
Your xml would look something like:
<Component Guid="PUT-GUID-HERE">
<Condition>VersionNT = 602</Condition>
<File Name="app.config" Source="config1.config" />
</Component>
<Component Guid="PUT-GUID-HERE">
<Condition>NOT VersionNT = 602</Condition>
<File Name="app.config" Source="config2.config" />
</Component>
Note that you'll have to specify component guids, because they have the same target path. The auto generated guid would be the same for both.

Reading properties in properties file with ant not respects order

Reading properties in properties file with ant not respects order.
The order is not respected:
Example:
<property file="build.properties" prefix="prefix."/>
<propertyselector property="cases" match="prefix.project\.(.*)" select="\1"/>
<for list="${cases}" param="pr">
<sequential>
<echo message="Project: #{pr} Version: ${prefix.project.#{pr}}"/>
</sequential>
</for>
with:
build.properties
project.1 = 1.2.3
project.8 = 5.9.4
project.4 = 3.5.0
Get:
Project: 8 Version 5.9.4
Project: 1 Version 1.2.3
Project: 4 Version 3.5.0
(And the result seems to randomly change)
I have to build them in the order like they appear in the build.properties file ??
The property file is being read in the correct order. You can test this by simply putting in duplicate properties and see which one gets defined. Here's build.properties:
dup.prop = foo
dup.prop = bar
And here's my Ant script:
<project>
<property file="build.properties"/>
<echo>Dup.prop is set to "${dup.prop}".</echo>
</project>
Running this, I'll get:
Dup.prop is set to "foo".
That's because the value foo is defined first in build.properies, and once a property is defined, it cannot be (easily) changed.
What you're trying to do is to access the properties in the order they're defined. That isn't guaranteed because properties are stored in a hash.
You mention sub-projects, and those sub-projects must be built in a particular order. Unfortunately, it's hard to tell exactly what could be the issue since you didn't give us an outline of your actual issue and some sample build scripts for projects and sub-projects.
First, Ant is a build matrix language which means it has a dependency hierarchy. The biggest issue developers have is attempting to force build matrix languages to execute in a particular order. You should specify a dependency hierarchy in your build.xml files (and the fewer there are, the easier it is for Ant to get things right).
If sub-project "B" depends upon a jar file in sub-project "A", It should be in sub-project "B"'s Ant script a dependency on sub-project "A" jar build.
<project name="proj-b"/>
...
<target name="build-jar"
depends="test.if.jar.exists"
unless="jar.exist">
<ant directory="${proj.a.dir}"
target="build.depend.jar"/>
</target>
<target name="test.if.jar.exists">
<condition property="jar.exists">
<available file="${proj.a.dir}/dist/${dend.jar.file}"/>
</condition>
</target>
<target name="compile"
depends="build-jar">
....
</target>
...
</project>
In the above build.xml for Project "B", I depend upon some jar file that Project "A" builds before I can compile Project "B". Therefore, my compile task depends upon build-jar which will build Target "A"'s jar file. To prevent this task from building Project "A"'s jar over and over, I use <condition> as a test to see if this jar exists. If it already does, I don't rebuild the jar.
In this case:
Target "compile" is called. That target realizes it depends upon Target "build-jar".
Before Target "compile" is executed. Target "build-jar" is first called.
Target "build-jar" depends upon Target "test.if.jar.exists".
Before "build-jar" is executed, it calls Target "test.if.jar.exists"
In Target "test.if.jar.exists", if the jar already exists, the property jar.exists will be set.
Now, Target "build-jar" is active, and looks to see if the property jar.exists is set. If it is, the target won't execute.
Finally, control returns to Target "compile" which then executes.
Here I'm not enforcing order directly. Instead, I merely have a dependency hierarchy that I specify, and I let Ant figure out exactly what to do.
If dependent jar issues are a big issue, you can also look into Ivy. Ivy allows you to create a Jar Repository. Your projects that build jars the rest of your projects are dependent upon can fetch the needed jars from this repository. This is very similar to Maven. In fact, Ant with Ivy can use Maven repositories. We use Artifactory, a local Maven repository manager, for our Ant projects.
You can also try the <subant> task which does allow you to specify a buildpath which would allow you to say build sub-project "A" before sub-project "B". You can define the buildpath in another Ant XML file which could be dependent upon customer and then use <import> to import the build path for that project.
Indeed. Java properties are represented with a java.util.Hashtable, and as you surely know, hash tables do not preserve order. You simply cannot do what you want with a properties file.
If those "projects" that you state you want to build in order are in turn Ant projects, you may want to consider moving their tasks to your main build-file instead, and simply enforce the proper building order using normal Ant dependencies.
The below code will help to sort the list generated by propertyselector tag
<sortlist property="my.sorted.list" value="${my.list}"
delimiter="," />
<echo message="${my.sorted.list}" />

MissingManifestResourceException for EmbeddedResouce in Silverlight

I write Silverlight games using XNA-based Silverlight engines. I have a previous game where I have files (MP3s and text files) with Build Action set to Embedded Resource, and no *.resx file to be seen in my solution.
The game runs fine; you can see the production version here.
On the other hand, my current project doesn't allow this. When I try to make files Embedded Resources, I get a MissingManifestResourceException thrown in my constructor of the main UserControl instance that starts my app. The error message is:
Could not find any resources appropriate for the specified culture or the neutral culture. Make sure "DeenGames.Colosseum.Content.Audio.2.mp3.resources" was correctly embedded or linked into assembly "DeenGames.Colosseum" at compile time, or that all the satellite assemblies required are loadable and fully signed.
I'm very, very, very perplexed. Setting any item's Build Action to Embedded Resource, whether MP3, text, or image, causes this exception.
How the heck do I fix (or debug) this? I'm 99% sure I do not need a .resx file, because my previous projects don't have one.
There's this lovely MSDN page which assures the world that:
In particular, Silverlight embedded resources must always use the
Resource build action, and not the Embedded Resource build action,
which uses a format that Silverlight cannot recognize.
But there's a well-known solution using Assembly.GetExecutingAssembly().GetManifestResourceNames(). In my case, it doesn't show me the resource if it's just a Resource; if it's an Embedded Resource, I can see the file name with dot-delimited namespace (as expected).
Download and see for yourself a very simple working example here. It has two embedded files (.2dg and .map) and compiles/runs without any exceptions OR resx file.
You can also download a broken example here. Replace FlatRedBall.dll with RadiantWrench.dll and watch the working example break. (Use ScreenController.ShowScreen and remove all FRB-referenced code.)
Embedded Resource is a WinForms technology that is depreciated in the Silverlight runtime. Instead, a build action of Resource or Content should be used instead.
When you set an item to Embedded Resource, Silverlight expects this to be a .resx file because this is what happens inside the .csproj or .vbproj file when you create a .resx and add resources to it (the file is marked as Embedded Resource for MSBuild and its resources are simply a None inside of an <ItemGroup/> that are discovered by the ResXGenerator at runtime based off the relative URI folder of "Resources"). If it isn't, it removes it or sets it as Content. You can examine Microsoft.Silverlight.Common.targets (usually in your C:\Program Files (x86)\MSBuild\Microsoft\Silverlight\v4.0 folder) to see how it changes items marked as Embedded Resource - setting to content, setting to none or setting to a .resx file.
If you're looking to just query what resources you have in the project, you could try this somewhat cumbersome approach: Enumerating embedded resources
UPDATE: In looking at your project, this is not really using an Embedded Resource the way WinForms uses this Build Action type or even .resx, per say. It uses a function from the ToolsSilverlight.dll called EmbeddedResourceFileReader.ReadFile. The code for that is:
private static string ReadFile(string fileName, Assembly currentAssembly)
{
string text = EmbeddedResourceHelper.CheckAndSanitizePath(fileName);
string result = "";
using (Stream manifestResourceStream = currentAssembly.GetManifestResourceStream(text))
{
if (manifestResourceStream == null)
{
throw new ArgumentException("Couldn't open " + fileName + ". Make sure the file exists in that directory, and has Build Action set to Embedded Resource.");
}
using (StreamReader streamReader = new StreamReader(manifestResourceStream))
{
result = streamReader.ReadToEnd();
}
}
return result;
}
Your .csproject file lists your files as:
<ItemGroup>
<EmbeddedResource Include="Content\Qadar.2dg" />
<EmbeddedResource Include="Content\Maps\main.map" />
<None Include="Properties\AppManifest.xml" />
</ItemGroup>
All this does is embed, as mentioned with the Microsoft.Silverlight.Common.targets above, your files as common Resources (at a top level, not with the list of other actual resouces) and finds a way to read them. You can decompile your DLL with ILSpy to exam that these are indeed now common Resources under the Resources folder.
So how can you do this in your new project? Replicate the exact method you did in your first one - add ToolsSilverlight.dll, list your items as Embedded Resource, and call them using EmbeddedResourceFileReader.ReadFile. You may also want to ensure your .csproj file <ItemGroup/> structure is similar to original one. Not sure if <None Include="Properties\AppManifest.xml"/> is needed by EmbeddedResourceFileReader, but it may be.

Resources