Access Resources By Name, Not ID

Does anyone know of a way to programmaticaly access resources by name?

Wanted:
Resources.GetString(“MyResource”);

Problem 1:
The GetString() method requires an enum for the hard coded reference to the resource.

There is no Enum.Parse() so that sure doesn’t help.

Problem 2:
NETMF ResourceManager seems to be lacking ALL methods to access resources. Example:

ResourceManager.GetString(“MyResource”) and ResourceManager.GetObject(“MyResource”) exist in the full framework

Why?
I want to access resources by name so I can make a small web server serve out html pages and files that are loaded in the device/app. A query will come in such as http://192.168.1.5:81/default.htm and I want to parse “default.htm” and grab the default.htm resource file dynamically and serve it out.

Ideas? I think I am going about this all wrong. This should be easy, and all too common a thing to do.

The common practice is to have a helper method that will give you ID for the string. But you have to implement it yourself.

This has been discussed several times here on the forum.

For example:
http://www.tinyclr.com/forum/topic?id=2213

The only method is to use a switch/case but it require to know all the names.

U can Also use the default case to implement the unfound url feedback.

I’d suggest taking a look at how jasdev’s web Server handles this… http://www.tinyclr.com/codeshare/entry/363 is the main project to look into

Thank you all.

I think I am still on the hunt for a better solution. A Switch statement is what I am trying to avoid, and jasdev’s web server is pretty slick, but uses an SDCard (which of course is a great way to store/pull files). I need to pull embedded files (no card), and I might just have to tuck my tail under and start writing a big ugly switch. But… really? :frowning:

@ neoscoob - Here is a quick and dirty t4 template that will at least generate the big case statement. If you have not used t4 before, just save the test to a file called Resources.template.tt and add the file to your project. Make sure that when you add the ‘.tt’ file that the ‘Custom Tool’ property is set to ‘TextTemplatingFileGenerator’ it should automatically be set.

The template will generate a partial class which extends the Resources class with a method to get string resources by name. This could be extended to work for other resource types as well, but for now anything but strings will just be skipped by the template.

So for example, if you have three string resources resources
Index
Default
String1

Where Index and Default are HTML files added to the resources and String1 is a straight string resource, the following will be generated by the template automatically. Note, the namespace will default to the root namespace of your project.


using System;

namespace YourNameSpaceHere
{
  
  internal partial class Resources
  {
    internal static short GetStringId(string name)
    {
      switch(name)
      {
        case "Default" : return (short)Resources.StringResources.Default;        
        case "Index" : return (short)Resources.StringResources.Index;        
        case "String1" : return (short)Resources.StringResources.String1;        
      }
      return -1;
    }

    internal static string GetString(string name)
    {
      int id = GetStringId(name);
      if (id == -1) throw new ArgumentException("name");
      return Resources.GetString((Resources.StringResources)id);
    }
  } 
}

Then you can use the code as follows


Resources.GetString("Index");

Here is the template (It is really very simplistic, there is no error checking etc.)


<#@ template language="C#" hostspecific="true" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="System.IO" #>
<#@ assembly name="System.Windows.Forms" #>
<#@ assembly name="EnvDTE" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Resources" #>
<#@ import namespace="System.Collections" #>
<#@ import namespace="EnvDTE" #>
<#
  var serviceProvider = Host as IServiceProvider;
  var dte = serviceProvider.GetService(typeof(DTE)) as DTE;  
  
  ProjectItem projectItem = dte.Solution.FindProjectItem(Host.TemplateFile);
  
  var projectRoot = Path.GetDirectoryName(Host.TemplateFile);
  var resxFile = Path.Combine(projectRoot, "Resources.resx");
  var rootNamespace = projectItem.ContainingProject.Properties.Item("RootNamespace").Value.ToString();

  ResXResourceReader rdr = new ResXResourceReader(resxFile);
  rdr.UseResXDataNodes = true;
 #>
using System;

namespace <#=rootNamespace#>
{  
  internal partial class Resources
  {
    internal static short GetStringId(string name)
    {
      switch(name)
      {
<#
  foreach (DictionaryEntry x in rdr)
  {
    ResXDataNode dnode = x.Value as ResXDataNode;
    string typeName = dnode.GetValueTypeName((System.ComponentModel.Design.ITypeResolutionService)null);
    if (string.IsNullOrEmpty(typeName) || (Type.GetType(typeName) == typeof(string)))
    {
 #>
        case "<#=x.Key#>" : return (short)Resources.StringResources.<#=x.Key#>;        
<#      
    }        
  }
 #>
      }
      return -1;
    }

    internal static string GetString(string name)
    {
      int id = GetStringId(name);
      if (id == -1) throw new ArgumentException("name");
      return Resources.GetString((Resources.StringResources)id);
    }
  } 
}

Wow, that is quite the slick trick to at least mitigate the switch statement a little. I had to tag that for the answer on the post, given the detail, and given that there really is no way to avoid the switch. Thanks all!